github.com/altipla-consulting/ravendb-go-client@v0.1.3/time.go (about) 1 package ravendb 2 3 import ( 4 "encoding/json" 5 "strings" 6 "time" 7 ) 8 9 const ( 10 // time format returned by the server which looks like: 11 // 2018-05-08T05:20:31.5233900Z or 12 // 2018-07-29T23:50:57.9998240 or 13 // 2018-08-16T13:56:59.355664-07:00 14 timeFormat = "2006-01-02T15:04:05.9999999Z" 15 timeFormat2 = "2006-01-02T15:04:05.9999999" 16 timeFormat3 = "2006-01-02T15:04:05.9999999-07:00" 17 ) 18 19 // Time is an alias for time.Time that serializes/deserializes in ways 20 // compatible with Ravendb server 21 type Time time.Time 22 23 // Format formats time in a way that RavenDB server understands. 24 // RavenDB is strict enough that a single format can't 25 // produce valid string values. 26 func (t Time) Format() string { 27 s := time.Time(t).Format(timeFormat) 28 // ravendb server only accepts 7 digits for fraction part but Go's 29 // formatting might remove trailing zeros, producing 6 digits 30 dotIdx := strings.LastIndexByte(s, '.') 31 32 if dotIdx == -1 { 33 s = s[:len(s)-1] // remove 'Z' 34 s = s + ".0000000Z" 35 } else { 36 nToAdd := 9 - (len(s) - dotIdx) // 9: 7 + 1 for 'Z' and 1 for '.' 37 if nToAdd > 0 { 38 s = s[:len(s)-1] // remove 'Z' 39 for ; nToAdd > 0; nToAdd-- { 40 s = s + "0" 41 } 42 s = s + "Z" 43 } 44 } 45 return s 46 } 47 48 // ParseTime parses string time value returned by RavenDB server 49 // The value can't be parsed with a single string format 50 func ParseTime(s string) (time.Time, error) { 51 tt, err := time.Parse(timeFormat, s) 52 if err != nil { 53 tt, err = time.Parse(timeFormat2, s) 54 if err != nil { 55 tt, err = time.Parse(timeFormat3, s) 56 } 57 } 58 return tt, err 59 } 60 61 func (t Time) MarshalJSON() ([]byte, error) { 62 s := t.Format() 63 return []byte(`"` + s + `"`), nil 64 } 65 66 func (t *Time) UnmarshalJSON(d []byte) error { 67 s := string(d) 68 s = strings.TrimLeft(s, `"`) 69 s = strings.TrimRight(s, `"`) 70 71 if s == "null" { 72 return nil 73 } 74 tt, err := ParseTime(s) 75 *t = Time(tt) 76 if err != nil { 77 // TODO: for now make it a fatal error to catch bugs early 78 must(err) 79 return err 80 } 81 return nil 82 } 83 84 func (t *Time) toTime() time.Time { 85 // for convenience make it work on nil pointer 86 if t == nil { 87 return time.Time{} 88 } 89 return time.Time(*t) 90 } 91 92 func (t *Time) toTimePtr() *time.Time { 93 if t == nil { 94 return nil 95 } 96 res := time.Time(*t) 97 return &res 98 } 99 100 // RoundToServerTime rounds t to the same precision as round-tripping 101 // to the server and back. Useful for comparing time.Time values for 102 // equality with values returned by the server 103 func RoundToServerTime(t time.Time) time.Time { 104 st := Time(t) 105 d, err := json.Marshal(st) 106 must(err) 107 var res Time 108 err = json.Unmarshal(d, &res) 109 must(err) 110 return time.Time(res) 111 }