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  }