duration.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package humanize
  2. import (
  3. "bytes"
  4. "git.nspix.com/golang/kos/util/bs"
  5. "time"
  6. )
  7. const (
  8. Nanosecond Duration = 1
  9. Microsecond = 1000 * Nanosecond
  10. Millisecond = 1000 * Microsecond
  11. Second = 1000 * Millisecond
  12. Minute = 60 * Second
  13. Hour = 60 * Minute
  14. )
  15. type Duration int64
  16. // fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the
  17. // tail of buf, omitting trailing zeros. It omits the decimal
  18. // point too when the fraction is 0. It returns the index where the
  19. // output bytes begin and the value v/10**prec.
  20. func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) {
  21. // Omit trailing zeros up to and including decimal point.
  22. w := len(buf)
  23. print := false
  24. for i := 0; i < prec; i++ {
  25. digit := v % 10
  26. print = print || digit != 0
  27. if print {
  28. w--
  29. buf[w] = byte(digit) + '0'
  30. }
  31. v /= 10
  32. }
  33. if print {
  34. w--
  35. buf[w] = '.'
  36. }
  37. return w, v
  38. }
  39. // fmtInt formats v into the tail of buf.
  40. // It returns the index where the output begins.
  41. func fmtInt(buf []byte, v uint64) int {
  42. w := len(buf)
  43. if v == 0 {
  44. w--
  45. buf[w] = '0'
  46. } else {
  47. for v > 0 {
  48. w--
  49. buf[w] = byte(v%10) + '0'
  50. v /= 10
  51. }
  52. }
  53. return w
  54. }
  55. func (d Duration) String() string {
  56. // Largest time is 2540400h10m10.000000000s
  57. var buf [32]byte
  58. w := len(buf)
  59. u := uint64(d)
  60. neg := d < 0
  61. if neg {
  62. u = -u
  63. }
  64. if u < uint64(time.Second) {
  65. // Special case: if duration is smaller than a second,
  66. // use smaller units, like 1.2ms
  67. var prec int
  68. w--
  69. buf[w] = 's'
  70. w--
  71. switch {
  72. case u == 0:
  73. return "0s"
  74. case u < uint64(time.Microsecond):
  75. // print nanoseconds
  76. prec = 0
  77. buf[w] = 'n'
  78. case u < uint64(time.Millisecond):
  79. // print microseconds
  80. prec = 3
  81. // U+00B5 'µ' micro sign == 0xC2 0xB5
  82. w-- // Need room for two bytes.
  83. copy(buf[w:], "µ")
  84. default:
  85. // print milliseconds
  86. prec = 6
  87. buf[w] = 'm'
  88. }
  89. w, u = fmtFrac(buf[:w], u, prec)
  90. w = fmtInt(buf[:w], u)
  91. } else {
  92. w--
  93. buf[w] = 's'
  94. w, u = fmtFrac(buf[:w], u, 9)
  95. // u is now integer seconds
  96. w = fmtInt(buf[:w], u%60)
  97. u /= 60
  98. // u is now integer minutes
  99. if u > 0 {
  100. w--
  101. buf[w] = 'm'
  102. w = fmtInt(buf[:w], u%60)
  103. u /= 60
  104. // u is now integer hours
  105. // Stop at hours because days can be different lengths.
  106. if u > 0 {
  107. w--
  108. buf[w] = 'h'
  109. w = fmtInt(buf[:w], u)
  110. }
  111. }
  112. }
  113. if neg {
  114. w--
  115. buf[w] = '-'
  116. }
  117. return string(buf[w:])
  118. }
  119. func (d *Duration) UnmarshalJSON(b []byte) (err error) {
  120. var n time.Duration
  121. b = bytes.TrimFunc(b, func(r rune) bool {
  122. if r == '"' {
  123. return true
  124. }
  125. return false
  126. })
  127. if n, err = time.ParseDuration(bs.BytesToString(b)); err == nil {
  128. *d = Duration(n)
  129. }
  130. return err
  131. }
  132. func (d Duration) MarshalJSON() ([]byte, error) {
  133. return bs.StringToBytes(`"` + d.String() + `"`), nil
  134. }
  135. func (d Duration) Duration() time.Duration {
  136. return time.Duration(d)
  137. }