@@ -2,6 +2,8 @@ package goxpath
22
33import (
44 "fmt"
5+ "strconv"
6+ "strings"
57 "time"
68)
79
@@ -10,6 +12,136 @@ var (
1012 ErrConversion = fmt .Errorf ("conversion failed" )
1113)
1214
15+ // XSDuration represents an xs:duration value with separate date and time components.
16+ type XSDuration struct {
17+ Negative bool
18+ Years int
19+ Months int
20+ Days int
21+ Hours int
22+ Minutes int
23+ Seconds float64
24+ }
25+
26+ func (d XSDuration ) String () string {
27+ var sb strings.Builder
28+ if d .Negative {
29+ sb .WriteByte ('-' )
30+ }
31+ sb .WriteByte ('P' )
32+ if d .Years != 0 {
33+ fmt .Fprintf (& sb , "%dY" , d .Years )
34+ }
35+ if d .Months != 0 {
36+ fmt .Fprintf (& sb , "%dM" , d .Months )
37+ }
38+ if d .Days != 0 {
39+ fmt .Fprintf (& sb , "%dD" , d .Days )
40+ }
41+ if d .Hours != 0 || d .Minutes != 0 || d .Seconds != 0 {
42+ sb .WriteByte ('T' )
43+ if d .Hours != 0 {
44+ fmt .Fprintf (& sb , "%dH" , d .Hours )
45+ }
46+ if d .Minutes != 0 {
47+ fmt .Fprintf (& sb , "%dM" , d .Minutes )
48+ }
49+ if d .Seconds != 0 {
50+ s := strconv .FormatFloat (d .Seconds , 'f' , - 1 , 64 )
51+ sb .WriteString (s )
52+ sb .WriteByte ('S' )
53+ }
54+ }
55+ if sb .Len () == 1 || (d .Negative && sb .Len () == 2 ) {
56+ sb .WriteString ("T0S" )
57+ }
58+ return sb .String ()
59+ }
60+
61+ // ParseXSDuration parses an ISO 8601 duration string (e.g. "P1Y2M3DT4H5M6.5S", "-P1Y").
62+ func ParseXSDuration (s string ) (XSDuration , error ) {
63+ var d XSDuration
64+ orig := s
65+ if len (s ) == 0 {
66+ return d , fmt .Errorf ("FORG0001: empty duration string" )
67+ }
68+ if s [0 ] == '-' {
69+ d .Negative = true
70+ s = s [1 :]
71+ }
72+ if len (s ) == 0 || s [0 ] != 'P' {
73+ return d , fmt .Errorf ("FORG0001: duration must start with 'P': %q" , orig )
74+ }
75+ s = s [1 :]
76+ inTimePart := false
77+ for len (s ) > 0 {
78+ if s [0 ] == 'T' {
79+ inTimePart = true
80+ s = s [1 :]
81+ continue
82+ }
83+ // Read a number (possibly decimal)
84+ numEnd := 0
85+ for numEnd < len (s ) && (s [numEnd ] >= '0' && s [numEnd ] <= '9' || s [numEnd ] == '.' ) {
86+ numEnd ++
87+ }
88+ if numEnd == 0 || numEnd >= len (s ) {
89+ return d , fmt .Errorf ("FORG0001: invalid duration format: %q" , orig )
90+ }
91+ numStr := s [:numEnd ]
92+ designator := s [numEnd ]
93+ s = s [numEnd + 1 :]
94+
95+ if designator == 'S' || strings .Contains (numStr , "." ) {
96+ f , err := strconv .ParseFloat (numStr , 64 )
97+ if err != nil {
98+ return d , fmt .Errorf ("FORG0001: invalid number in duration: %q" , orig )
99+ }
100+ if designator == 'S' {
101+ d .Seconds = f
102+ } else {
103+ return d , fmt .Errorf ("FORG0001: decimal only allowed for seconds: %q" , orig )
104+ }
105+ } else {
106+ n , err := strconv .Atoi (numStr )
107+ if err != nil {
108+ return d , fmt .Errorf ("FORG0001: invalid number in duration: %q" , orig )
109+ }
110+ switch designator {
111+ case 'Y' :
112+ d .Years = n
113+ case 'M' :
114+ if inTimePart {
115+ d .Minutes = n
116+ } else {
117+ d .Months = n
118+ }
119+ case 'D' :
120+ d .Days = n
121+ case 'H' :
122+ d .Hours = n
123+ default :
124+ return d , fmt .Errorf ("FORG0001: unexpected designator '%c' in duration: %q" , designator , orig )
125+ }
126+ }
127+ }
128+ return d , nil
129+ }
130+
131+ // XSQName represents an xs:QName value with namespace URI, prefix, and local name.
132+ type XSQName struct {
133+ Namespace string
134+ Prefix string
135+ Localname string
136+ }
137+
138+ func (q XSQName ) String () string {
139+ if q .Prefix != "" {
140+ return q .Prefix + ":" + q .Localname
141+ }
142+ return q .Localname
143+ }
144+
13145// ToXSInteger converts the item to an xs:integer.
14146func ToXSInteger (itm Item ) (int , error ) {
15147 switch t := itm .(type ) {
@@ -38,6 +170,95 @@ func xsTime(ctx *Context, args []Sequence) (Sequence, error) {
38170 return Sequence {XSTime (t )}, nil
39171}
40172
173+ func xsDouble (ctx * Context , args []Sequence ) (Sequence , error ) {
174+ nv , err := NumberValue (args [0 ])
175+ if err != nil {
176+ return nil , err
177+ }
178+ return Sequence {nv }, nil
179+ }
180+
181+ func xsDate (ctx * Context , args []Sequence ) (Sequence , error ) {
182+ firstarg , err := StringValue (args [0 ])
183+ if err != nil {
184+ return nil , err
185+ }
186+
187+ formats := []string {
188+ "2006-01-02-07:00" ,
189+ "2006-01-02+07:00" ,
190+ "2006-01-02Z" ,
191+ "2006-01-02" ,
192+ }
193+
194+ for _ , format := range formats {
195+ t , err := time .Parse (format , firstarg )
196+ if err == nil {
197+ return Sequence {XSDate (t )}, nil
198+ }
199+ }
200+
201+ return nil , fmt .Errorf ("FORG0001: cannot cast %q to xs:date" , firstarg )
202+ }
203+
204+ func xsInteger (ctx * Context , args []Sequence ) (Sequence , error ) {
205+ nv , err := NumberValue (args [0 ])
206+ if err != nil {
207+ return nil , err
208+ }
209+ return Sequence {int (nv )}, nil
210+ }
211+
212+ func xsString (ctx * Context , args []Sequence ) (Sequence , error ) {
213+ sv , err := StringValue (args [0 ])
214+ if err != nil {
215+ return nil , err
216+ }
217+ return Sequence {sv }, nil
218+ }
219+
220+ func xsDateTime (ctx * Context , args []Sequence ) (Sequence , error ) {
221+ firstarg , err := StringValue (args [0 ])
222+ if err != nil {
223+ return nil , err
224+ }
225+
226+ formats := []string {
227+ "2006-01-02T15:04:05.999999999-07:00" ,
228+ "2006-01-02T15:04:05.999999999Z" ,
229+ "2006-01-02T15:04:05.999999999" ,
230+ "2006-01-02T15:04:05-07:00" ,
231+ "2006-01-02T15:04:05Z" ,
232+ "2006-01-02T15:04:05" ,
233+ }
234+
235+ for _ , format := range formats {
236+ t , err := time .Parse (format , firstarg )
237+ if err == nil {
238+ return Sequence {XSDateTime (t )}, nil
239+ }
240+ }
241+ return nil , fmt .Errorf ("FORG0001: cannot cast %q to xs:dateTime" , firstarg )
242+ }
243+
244+ func xsDuration (ctx * Context , args []Sequence ) (Sequence , error ) {
245+ firstarg , err := StringValue (args [0 ])
246+ if err != nil {
247+ return nil , err
248+ }
249+ d , err := ParseXSDuration (firstarg )
250+ if err != nil {
251+ return nil , err
252+ }
253+ return Sequence {d }, nil
254+ }
255+
41256func init () {
42257 RegisterFunction (& Function {Name : "time" , Namespace : nsXS , F : xsTime , MinArg : 1 , MaxArg : 1 })
258+ RegisterFunction (& Function {Name : "dateTime" , Namespace : nsXS , F : xsDateTime , MinArg : 1 , MaxArg : 1 })
259+ RegisterFunction (& Function {Name : "double" , Namespace : nsXS , F : xsDouble , MinArg : 1 , MaxArg : 1 })
260+ RegisterFunction (& Function {Name : "duration" , Namespace : nsXS , F : xsDuration , MinArg : 1 , MaxArg : 1 })
261+ RegisterFunction (& Function {Name : "date" , Namespace : nsXS , F : xsDate , MinArg : 1 , MaxArg : 1 })
262+ RegisterFunction (& Function {Name : "integer" , Namespace : nsXS , F : xsInteger , MinArg : 1 , MaxArg : 1 })
263+ RegisterFunction (& Function {Name : "string" , Namespace : nsXS , F : xsString , MinArg : 1 , MaxArg : 1 })
43264}
0 commit comments