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 }