Mercurial > gemma
comparison pkg/soap/xsdDateTime.go @ 5607:f2b51ac3d5cf erdms2
Modified SOAP client to fit new needs.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 31 Oct 2022 17:02:55 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
5606:5049f7ad9faa | 5607:f2b51ac3d5cf |
---|---|
1 // This Source Code Form is subject to the terms of the Mozilla Public | |
2 // License, v. 2.0. If a copy of the MPL was not distributed with this | |
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
4 | |
5 package soap | |
6 | |
7 import ( | |
8 "encoding/xml" | |
9 "strings" | |
10 "time" | |
11 ) | |
12 | |
13 const ( | |
14 dateLayout = "2006-01-02Z07:00" | |
15 timeLayout = "15:04:05.999999999Z07:00" | |
16 ) | |
17 | |
18 // | |
19 // DateTime struct | |
20 // | |
21 | |
22 // XSDDateTime is a type for representing xsd:datetime in Golang | |
23 type XSDDateTime struct { | |
24 innerTime time.Time | |
25 hasTz bool | |
26 } | |
27 | |
28 // StripTz removes TZ information from the datetime | |
29 func (xdt *XSDDateTime) StripTz() { | |
30 xdt.hasTz = false | |
31 } | |
32 | |
33 // ToGoTime converts the time to time.Time by checking if a TZ is specified. | |
34 // If there is a TZ, that TZ is used, otherwise local TZ is used | |
35 func (xdt *XSDDateTime) ToGoTime() time.Time { | |
36 if xdt.hasTz { | |
37 return xdt.innerTime | |
38 } | |
39 return time.Date(xdt.innerTime.Year(), xdt.innerTime.Month(), xdt.innerTime.Day(), | |
40 xdt.innerTime.Hour(), xdt.innerTime.Minute(), xdt.innerTime.Second(), | |
41 xdt.innerTime.Nanosecond(), time.Local) | |
42 } | |
43 | |
44 // MarshalXML implements xml.MarshalerAttr on XSDDateTime | |
45 func (xdt XSDDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error { | |
46 xdtString := xdt.string() | |
47 if xdtString != "" { | |
48 return e.EncodeElement(xdtString, start) | |
49 } | |
50 return nil | |
51 } | |
52 | |
53 // MarshalXMLAttr implements xml.MarshalerAttr on XSDDateTime | |
54 func (xdt XSDDateTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { | |
55 xdtString := xdt.string() | |
56 attr := xml.Attr{} | |
57 if xdtString != "" { | |
58 attr.Name = name | |
59 attr.Value = xdtString | |
60 } | |
61 return attr, nil | |
62 } | |
63 | |
64 // returns string representation and skips "zero" time values. It also checks if nanoseconds and TZ exist. | |
65 func (xdt XSDDateTime) string() string { | |
66 if !xdt.innerTime.IsZero() { | |
67 dateTimeLayout := time.RFC3339Nano | |
68 if xdt.innerTime.Nanosecond() == 0 { | |
69 dateTimeLayout = time.RFC3339 | |
70 } | |
71 dtString := xdt.innerTime.Format(dateTimeLayout) | |
72 if !xdt.hasTz { | |
73 // split off time portion | |
74 dateAndTime := strings.SplitN(dtString, "T", 2) | |
75 toks := strings.SplitN(dateAndTime[1], "Z", 2) | |
76 toks = strings.SplitN(toks[0], "+", 2) | |
77 toks = strings.SplitN(toks[0], "-", 2) | |
78 dtString = dateAndTime[0] + "T" + toks[0] | |
79 } | |
80 return dtString | |
81 } | |
82 return "" | |
83 } | |
84 | |
85 // UnmarshalXML implements xml.Unmarshaler on XSDDateTime to use time.RFC3339Nano | |
86 func (xdt *XSDDateTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { | |
87 var content string | |
88 err := d.DecodeElement(&content, &start) | |
89 if err != nil { | |
90 return err | |
91 } | |
92 xdt.innerTime, xdt.hasTz, err = fromString(content, time.RFC3339Nano) | |
93 return err | |
94 } | |
95 | |
96 // UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDDateTime to use time.RFC3339Nano | |
97 func (xdt *XSDDateTime) UnmarshalXMLAttr(attr xml.Attr) error { | |
98 var err error | |
99 xdt.innerTime, xdt.hasTz, err = fromString(attr.Value, time.RFC3339Nano) | |
100 return err | |
101 } | |
102 | |
103 func fromString(content string, format string) (time.Time, bool, error) { | |
104 var t time.Time | |
105 if content == "" { | |
106 return t, true, nil | |
107 } | |
108 hasTz := false | |
109 if strings.Contains(content, "T") { // check if we have a time portion | |
110 // split into date and time portion | |
111 dateAndTime := strings.SplitN(content, "T", 2) | |
112 if len(dateAndTime) > 1 { | |
113 if strings.Contains(dateAndTime[1], "Z") || | |
114 strings.Contains(dateAndTime[1], "+") || | |
115 strings.Contains(dateAndTime[1], "-") { | |
116 hasTz = true | |
117 } | |
118 } | |
119 if !hasTz { | |
120 content += "Z" | |
121 } | |
122 if content == "0001-01-01T00:00:00Z" { | |
123 return t, true, nil | |
124 } | |
125 } else { | |
126 // we don't see to have a time portion, check timezone | |
127 if strings.Contains(content, "Z") || | |
128 strings.Contains(content, ":") { | |
129 hasTz = true | |
130 } | |
131 if !hasTz { | |
132 content += "Z" | |
133 } | |
134 } | |
135 t, err := time.Parse(format, content) | |
136 return t, hasTz, err | |
137 } | |
138 | |
139 // CreateXsdDateTime creates an object represent xsd:datetime object in Golang | |
140 func CreateXsdDateTime(dt time.Time, hasTz bool) XSDDateTime { | |
141 return XSDDateTime{ | |
142 innerTime: dt, | |
143 hasTz: hasTz, | |
144 } | |
145 } | |
146 | |
147 // XSDDate is a type for representing xsd:date in Golang | |
148 type XSDDate struct { | |
149 innerDate time.Time | |
150 hasTz bool | |
151 } | |
152 | |
153 // StripTz removes the TZ information from the date | |
154 func (xd *XSDDate) StripTz() { | |
155 xd.hasTz = false | |
156 } | |
157 | |
158 // ToGoTime converts the date to Golang time.Time by checking if a TZ is specified. | |
159 // If there is a TZ, that TZ is used, otherwise local TZ is used | |
160 func (xd *XSDDate) ToGoTime() time.Time { | |
161 if xd.hasTz { | |
162 return xd.innerDate | |
163 } | |
164 return time.Date(xd.innerDate.Year(), xd.innerDate.Month(), xd.innerDate.Day(), | |
165 0, 0, 0, 0, time.Local) | |
166 } | |
167 | |
168 // MarshalXML implementation on XSDDate | |
169 func (xd XSDDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error { | |
170 xdtString := xd.string() | |
171 if xdtString != "" { | |
172 return e.EncodeElement(xdtString, start) | |
173 } | |
174 return nil | |
175 } | |
176 | |
177 // MarshalXMLAttr implementation on XSDDate | |
178 func (xd XSDDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { | |
179 xdString := xd.string() | |
180 attr := xml.Attr{} | |
181 if xdString != "" { | |
182 attr.Name = name | |
183 attr.Value = xdString | |
184 } | |
185 return attr, nil | |
186 } | |
187 | |
188 // returns string representation and skips "zero" time values | |
189 func (xd XSDDate) string() string { | |
190 if !xd.innerDate.IsZero() { | |
191 dateString := xd.innerDate.Format(dateLayout) // serialize with TZ | |
192 if !xd.hasTz { | |
193 if strings.Contains(dateString, "Z") { | |
194 // UTC Tz | |
195 toks := strings.SplitN(dateString, "Z", 2) | |
196 dateString = toks[0] | |
197 } else { | |
198 // [+-]00:00 Tz, remove last 6 chars | |
199 if len(dateString) > 5 { // this should always be true | |
200 start := len(dateString) - 6 // locate at "-" | |
201 dateString = dateString[0:start] | |
202 } | |
203 } | |
204 } | |
205 return dateString | |
206 } | |
207 return "" | |
208 } | |
209 | |
210 // UnmarshalXML implements xml.Unmarshaler on XSDDate to use dateLayout | |
211 func (xd *XSDDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { | |
212 var content string | |
213 err := d.DecodeElement(&content, &start) | |
214 if err != nil { | |
215 return err | |
216 } | |
217 xd.innerDate, xd.hasTz, err = fromString(content, dateLayout) | |
218 return err | |
219 } | |
220 | |
221 // UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDDate to use dateLayout | |
222 func (xd *XSDDate) UnmarshalXMLAttr(attr xml.Attr) error { | |
223 var err error | |
224 xd.innerDate, xd.hasTz, err = fromString(attr.Value, dateLayout) | |
225 return err | |
226 } | |
227 | |
228 // CreateXsdDate creates an object represent xsd:datetime object in Golang | |
229 func CreateXsdDate(date time.Time, hasTz bool) XSDDate { | |
230 return XSDDate{ | |
231 innerDate: date, | |
232 hasTz: hasTz, | |
233 } | |
234 } | |
235 | |
236 // XSDTime is a type for representing xsd:time | |
237 type XSDTime struct { | |
238 innerTime time.Time | |
239 hasTz bool | |
240 } | |
241 | |
242 // MarshalXML implements xml.Marshaler on XSDTime | |
243 func (xt XSDTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error { | |
244 xdtString := xt.string() | |
245 if xdtString != "" { | |
246 return e.EncodeElement(xdtString, start) | |
247 } | |
248 return nil | |
249 } | |
250 | |
251 // MarshalXMLAttr implements xml.MarshalerAttr on XSDTime | |
252 func (xt XSDTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { | |
253 xdString := xt.string() | |
254 attr := xml.Attr{} | |
255 if xdString != "" { | |
256 attr.Name = name | |
257 attr.Value = xdString | |
258 } | |
259 return attr, nil | |
260 } | |
261 | |
262 // returns string representation and skips "zero" time values | |
263 func (xt XSDTime) string() string { | |
264 if !xt.innerTime.IsZero() { | |
265 dateTimeLayout := time.RFC3339Nano | |
266 if xt.innerTime.Nanosecond() == 0 { | |
267 dateTimeLayout = time.RFC3339 | |
268 } | |
269 // split off date portion | |
270 dateAndTime := strings.SplitN(xt.innerTime.Format(dateTimeLayout), "T", 2) | |
271 timeString := dateAndTime[1] | |
272 if !xt.hasTz { | |
273 toks := strings.SplitN(timeString, "Z", 2) | |
274 toks = strings.SplitN(toks[0], "+", 2) | |
275 toks = strings.SplitN(toks[0], "-", 2) | |
276 timeString = toks[0] | |
277 } | |
278 return timeString | |
279 } | |
280 return "" | |
281 } | |
282 | |
283 // UnmarshalXML implements xml.Unmarshaler on XSDTime to use dateTimeLayout | |
284 func (xt *XSDTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { | |
285 var err error | |
286 var content string | |
287 err = d.DecodeElement(&content, &start) | |
288 if err != nil { | |
289 return err | |
290 } | |
291 return xt.fromString(content) | |
292 } | |
293 | |
294 // UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDTime to use dateTimeLayout | |
295 func (xt *XSDTime) UnmarshalXMLAttr(attr xml.Attr) error { | |
296 return xt.fromString(attr.Value) | |
297 } | |
298 | |
299 func (xt *XSDTime) fromString(content string) error { | |
300 var t time.Time | |
301 var err error | |
302 if content == "" { | |
303 xt.innerTime = t | |
304 return nil | |
305 } | |
306 xt.hasTz = false | |
307 if strings.Contains(content, "Z") || | |
308 strings.Contains(content, "+") || | |
309 strings.Contains(content, "-") { | |
310 xt.hasTz = true | |
311 } | |
312 if !xt.hasTz { | |
313 content += "Z" | |
314 } | |
315 xt.innerTime, err = time.Parse(timeLayout, content) | |
316 return err | |
317 } | |
318 | |
319 // Hour returns hour of the xsd:time | |
320 func (xt XSDTime) Hour() int { | |
321 return xt.innerTime.Hour() | |
322 } | |
323 | |
324 // Minute returns minutes of the xsd:time | |
325 func (xt XSDTime) Minute() int { | |
326 return xt.innerTime.Minute() | |
327 } | |
328 | |
329 // Second returns seconds of the xsd:time | |
330 func (xt XSDTime) Second() int { | |
331 return xt.innerTime.Second() | |
332 } | |
333 | |
334 // Nanosecond returns nanosecond of the xsd:time | |
335 func (xt XSDTime) Nanosecond() int { | |
336 return xt.innerTime.Nanosecond() | |
337 } | |
338 | |
339 // Location returns the TZ information of the xsd:time | |
340 func (xt XSDTime) Location() *time.Location { | |
341 if xt.hasTz { | |
342 return xt.innerTime.Location() | |
343 } | |
344 return nil | |
345 } | |
346 | |
347 // CreateXsdTime creates an object representing xsd:time in Golang | |
348 func CreateXsdTime(hour int, min int, sec int, nsec int, loc *time.Location) XSDTime { | |
349 realLoc := loc | |
350 if loc == nil { | |
351 realLoc = time.Local | |
352 } | |
353 return XSDTime{ | |
354 innerTime: time.Date(1951, 10, 22, hour, min, sec, nsec, realLoc), | |
355 hasTz: loc != nil, | |
356 } | |
357 } |