github.com/altipla-consulting/ravendb-go-client@v0.1.3/duration.go (about) 1 package ravendb 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 "time" 8 ) 9 10 // Duration is an alias for time.Duration that serializes to JSON 11 // in format compatible with ravendb server 12 type Duration time.Duration 13 14 // MarshalJSON converts to JSON format (as a string) 15 func (d Duration) MarshalJSON() ([]byte, error) { 16 dur := time.Duration(d) 17 nDays := dur / (time.Hour * 24) 18 dur = dur - (time.Hour * 24 * nDays) 19 nHours := dur / time.Hour 20 dur = dur - (time.Hour * nHours) 21 nMins := dur / time.Minute 22 dur = dur - (time.Minute * nMins) 23 nSecs := dur / time.Second 24 dur = dur - (time.Second * nSecs) 25 var s string 26 if nDays > 0 { 27 // "5." 28 s = fmt.Sprintf("%d.", nDays) 29 } 30 // "00:00:00" 31 s += fmt.Sprintf("%02d:%02d:%02d", nHours, nMins, nSecs) 32 33 millis := dur / time.Millisecond 34 if millis > 0 { 35 s += fmt.Sprintf(".%03d0000", millis) 36 } 37 s = `"` + s + `"` 38 return []byte(s), nil 39 } 40 41 // UnmarshalJSON decodes from string 42 func (d *Duration) UnmarshalJSON(data []byte) error { 43 s := string(data) 44 errOut := fmt.Errorf("'%s' is not a valid JSON serializatioon of Duration", s) 45 46 if len(s) < 8+2 || s[0] != '"' || s[len(s)-1] != '"' { 47 // needs at least 00:00:00 + 2 * " 48 return errOut 49 } 50 51 // string string quotes (") from the beginning and end of string 52 s = s[1 : len(s)-1] 53 parts := strings.Split(s, ":") 54 if len(parts) != 3 { 55 return errOut 56 } 57 var nDays int 58 var err error 59 first := parts[0] 60 // this can either be ${hour} or ${day}.${hour} 61 parts2 := strings.Split(first, ".") 62 if len(parts2) > 2 { 63 return errOut 64 } 65 if len(parts2) == 2 { 66 nDays, err = strconv.Atoi(parts2[0]) 67 if err != nil { 68 return errOut 69 } 70 parts[0] = parts2[1] 71 } 72 // TODO: be more strict i.e. only allow 2-digit NN as a valid number 73 nHours, err := strconv.Atoi(parts[0]) 74 if err != nil { 75 return errOut 76 } 77 nMinutes, err := strconv.Atoi(parts[1]) 78 if err != nil { 79 return errOut 80 } 81 parts2 = strings.Split(parts[2], ".") 82 if len(parts2) > 2 { 83 return errOut 84 } 85 secsStr := parts[2] 86 var nMS int 87 if len(parts2) == 2 { 88 secsStr = parts2[0] 89 msStr := parts2[1] 90 if len(msStr) > 3 { 91 msStr = msStr[:3] 92 } 93 // ".1" is really ".100" 94 for len(msStr) < 3 { 95 msStr = msStr + "0" 96 } 97 nMS, err = strconv.Atoi(msStr) 98 if err != nil { 99 return errOut 100 } 101 } 102 nSecs, err := strconv.Atoi(secsStr) 103 if err != nil { 104 return errOut 105 } 106 dur := time.Duration(nDays) * (time.Hour * 24) 107 dur += time.Duration(nHours) * time.Hour 108 dur += time.Duration(nMinutes) * time.Minute 109 dur += time.Duration(nSecs) * time.Second 110 dur += time.Duration(nMS) * time.Millisecond 111 *d = Duration(dur) 112 return nil 113 }