github.com/wI2L/jettison@v0.7.5-0.20230106001914-c70014c6417a/time.go (about) 1 package jettison 2 3 import "time" 4 5 const epoch = 62135683200 // 1970-01-01T00:00:00 6 7 // DurationFmt represents the format used 8 // to encode a time.Duration value. 9 type DurationFmt int 10 11 // DurationFmt constants. 12 const ( 13 DurationString DurationFmt = iota 14 DurationMinutes 15 DurationSeconds 16 DurationMilliseconds 17 DurationMicroseconds 18 DurationNanoseconds // default 19 ) 20 21 // String implements the fmt.Stringer 22 // interface for DurationFmt. 23 func (f DurationFmt) String() string { 24 if !f.valid() { 25 return "unknown" 26 } 27 return durationFmtStr[f] 28 } 29 30 func (f DurationFmt) valid() bool { 31 return f >= DurationString && f <= DurationNanoseconds 32 } 33 34 var ( 35 zeroDuration = []byte("0s") 36 durationFmtStr = []string{"str", "min", "s", "ms", "μs", "nanosecond"} 37 dayOffset = [13]uint16{0, 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275} 38 ) 39 40 // appendDuration appends the textual representation 41 // of d to the tail of dst and returns the extended buffer. 42 // Adapted from https://golang.org/src/time/time.go. 43 func appendDuration(dst []byte, d time.Duration) []byte { 44 var buf [32]byte 45 46 l := len(buf) 47 u := uint64(d) 48 n := d < 0 49 if n { 50 u = -u 51 } 52 if u < uint64(time.Second) { 53 // Special case: if duration is smaller than 54 // a second, use smaller units, like 1.2ms 55 var prec int 56 l-- 57 buf[l] = 's' 58 l-- 59 switch { 60 case u == 0: 61 return append(dst, zeroDuration...) 62 case u < uint64(time.Microsecond): 63 prec = 0 64 buf[l] = 'n' 65 case u < uint64(time.Millisecond): 66 prec = 3 67 // U+00B5 'µ' micro sign is 0xC2 0xB5. 68 // Need room for two bytes. 69 l-- 70 copy(buf[l:], "µ") 71 default: // Format as milliseconds. 72 prec = 6 73 buf[l] = 'm' 74 } 75 l, u = fmtFrac(buf[:l], u, prec) 76 l = fmtInt(buf[:l], u) 77 } else { 78 l-- 79 buf[l] = 's' 80 81 l, u = fmtFrac(buf[:l], u, 9) 82 83 // Format as seconds. 84 l = fmtInt(buf[:l], u%60) 85 u /= 60 86 87 // Format as minutes. 88 if u > 0 { 89 l-- 90 buf[l] = 'm' 91 l = fmtInt(buf[:l], u%60) 92 u /= 60 93 94 // Format as hours. Stop there, because 95 // days can be different lengths. 96 if u > 0 { 97 l-- 98 buf[l] = 'h' 99 l = fmtInt(buf[:l], u) 100 } 101 } 102 } 103 if n { 104 l-- 105 buf[l] = '-' 106 } 107 return append(dst, buf[l:]...) 108 } 109 110 // fmtInt formats v into the tail of buf. 111 // It returns the index where the output begins. 112 // Taken from https://golang.org/src/time/time.go. 113 func fmtInt(buf []byte, v uint64) int { 114 w := len(buf) 115 if v == 0 { 116 w-- 117 buf[w] = '0' 118 } else { 119 for v > 0 { 120 w-- 121 buf[w] = byte(v%10) + '0' 122 v /= 10 123 } 124 } 125 return w 126 } 127 128 // fmtFrac formats the fraction of v/10**prec (e.g., ".12345") 129 // into the tail of buf, omitting trailing zeros. It omits the 130 // decimal point too when the fraction is 0. It returns the 131 // index where the output bytes begin and the value v/10**prec. 132 // Taken from https://golang.org/src/time/time.go. 133 func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) { 134 // Omit trailing zeros up to and including decimal point. 135 w := len(buf) 136 print := false 137 for i := 0; i < prec; i++ { 138 digit := v % 10 139 print = print || digit != 0 140 if print { 141 w-- 142 buf[w] = byte(digit) + '0' 143 } 144 v /= 10 145 } 146 if print { 147 w-- 148 buf[w] = '.' 149 } 150 return w, v 151 } 152 153 func rdnToYmd(rdn uint32) (uint16, uint16, uint16) { 154 // Rata Die algorithm by Peter Baum. 155 var ( 156 Z = rdn + 306 157 H = 100*Z - 25 158 A = H / 3652425 159 B = A - (A >> 2) 160 y = (100*B + H) / 36525 161 d = B + Z - (1461 * y >> 2) 162 m = (535*d + 48950) >> 14 163 ) 164 if m > 12 { 165 y++ 166 m -= 12 167 } 168 return uint16(y), uint16(m), uint16(d) - dayOffset[m] 169 } 170 171 // appendRFC3339Time appends the RFC3339 textual representation 172 // of t to the tail of dst and returns the extended buffer. 173 // Adapted from https://github.com/chansen/c-timestamp. 174 func appendRFC3339Time(t time.Time, dst []byte, nano bool) []byte { 175 var buf [37]byte 176 177 // Base layout chars with opening quote. 178 buf[0], buf[5], buf[8], buf[11], buf[14], buf[17] = '"', '-', '-', 'T', ':', ':' 179 180 // Year. 181 _, offset := t.Zone() 182 sec := t.Unix() + int64(offset) + epoch 183 y, m, d := rdnToYmd(uint32(sec / 86400)) 184 for i := 4; i >= 1; i-- { 185 buf[i] = byte(y%10) + '0' 186 y /= 10 187 } 188 buf[7], m = byte(m%10)+'0', m/10 // month 189 buf[6] = byte(m%10) + '0' 190 191 buf[10], d = byte(d%10)+'0', d/10 // day 192 buf[9] = byte(d%10) + '0' 193 194 // Hours/minutes/seconds. 195 s := sec % 86400 196 buf[19], s = byte(s%10)+'0', s/10 197 buf[18], s = byte(s%06)+'0', s/6 198 buf[16], s = byte(s%10)+'0', s/10 199 buf[15], s = byte(s%06)+'0', s/6 200 buf[13], s = byte(s%10)+'0', s/10 201 buf[12], _ = byte(s%10)+'0', 0 202 203 n := 20 204 205 // Fractional second precision. 206 nsec := t.Nanosecond() 207 if nano && nsec != 0 { 208 buf[n] = '.' 209 u := nsec 210 for i := 9; i >= 1; i-- { 211 buf[n+i] = byte(u%10) + '0' 212 u /= 10 213 } 214 // Remove trailing zeros. 215 var rpad int 216 for i := 9; i >= 1; i-- { 217 if buf[n+i] == '0' { 218 rpad++ 219 } else { 220 break 221 } 222 } 223 n += 10 - rpad 224 } 225 // Zone. 226 if offset == 0 { 227 buf[n] = 'Z' 228 n++ 229 } else { 230 var z int 231 zone := offset / 60 // convert to minutes 232 if zone < 0 { 233 buf[n] = '-' 234 z = -zone 235 } else { 236 buf[n] = '+' 237 z = zone 238 } 239 buf[n+3] = ':' 240 buf[n+5], z = byte(z%10)+'0', z/10 241 buf[n+4], z = byte(z%06)+'0', z/6 242 buf[n+2], z = byte(z%10)+'0', z/10 243 buf[n+1], _ = byte(z%10)+'0', 0 244 n += 6 245 } 246 // Finally, add the closing quote. 247 // It's position depends on the presence 248 // of the fractional seconds and/or the 249 // timezone offset. 250 buf[n] = '"' 251 252 return append(dst, buf[:n+1]...) 253 }