fmt.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. package duration_fmt
  2. import (
  3. "errors"
  4. "fmt"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "time"
  9. )
  10. var (
  11. //units, _ = DefaultUnitsCoder.Decode("year,week,day,hour,minute,second,millisecond,microsecond")
  12. units, _ = DefaultUnitsCoder.Decode("年,星期,天,小时,分钟,秒,毫秒,微秒")
  13. unitsShort = []string{"y", "w", "d", "h", "m", "s", "ms", "µs"}
  14. )
  15. // Durafmt holds the parsed duration and the original input duration.
  16. type Durafmt struct {
  17. duration time.Duration
  18. input string // Used as reference.
  19. limitN int // Non-zero to limit only first N elements to output.
  20. limitUnit string // Non-empty to limit max unit
  21. }
  22. // LimitToUnit sets the output format, you will not have unit bigger than the UNIT specified. UNIT = "" means no restriction.
  23. func (d *Durafmt) LimitToUnit(unit string) *Durafmt {
  24. d.limitUnit = unit
  25. return d
  26. }
  27. // LimitFirstN sets the output format, outputing only first N elements. n == 0 means no limit.
  28. func (d *Durafmt) LimitFirstN(n int) *Durafmt {
  29. d.limitN = n
  30. return d
  31. }
  32. func (d *Durafmt) Duration() time.Duration {
  33. return d.duration
  34. }
  35. // Truncate sets precision
  36. func (d *Durafmt) Truncate(unit time.Duration) *Durafmt {
  37. d.duration = d.duration.Truncate(unit)
  38. return d
  39. }
  40. // Parse creates a new *Durafmt struct, returns error if input is invalid.
  41. func Parse(dinput time.Duration) *Durafmt {
  42. input := dinput.String()
  43. return &Durafmt{dinput, input, 0, ""}
  44. }
  45. // ParseShort creates a new *Durafmt struct, short form, returns error if input is invalid.
  46. // It's shortcut for `Parse(dur).LimitFirstN(1)`
  47. func ParseShort(dinput time.Duration) *Durafmt {
  48. input := dinput.String()
  49. return &Durafmt{dinput, input, 1, ""}
  50. }
  51. // ParseString creates a new *Durafmt struct from a string.
  52. // returns an error if input is invalid.
  53. func ParseString(input string) (*Durafmt, error) {
  54. if input == "0" || input == "-0" {
  55. return nil, errors.New("durafmt: missing unit in duration " + input)
  56. }
  57. duration, err := time.ParseDuration(input)
  58. if err != nil {
  59. return nil, err
  60. }
  61. return &Durafmt{duration, input, 0, ""}, nil
  62. }
  63. // ParseStringShort creates a new *Durafmt struct from a string, short form
  64. // returns an error if input is invalid.
  65. // It's shortcut for `ParseString(durStr)` and then calling `LimitFirstN(1)`
  66. func ParseStringShort(input string) (*Durafmt, error) {
  67. if input == "0" || input == "-0" {
  68. return nil, errors.New("durafmt: missing unit in duration " + input)
  69. }
  70. duration, err := time.ParseDuration(input)
  71. if err != nil {
  72. return nil, err
  73. }
  74. return &Durafmt{duration, input, 1, ""}, nil
  75. }
  76. // String parses d *Durafmt into a human readable duration with default units.
  77. func (d *Durafmt) String() string {
  78. return d.Format(units)
  79. }
  80. // Format parses d *Durafmt into a human readable duration with units.
  81. func (d *Durafmt) Format(units Units) string {
  82. var duration string
  83. // Check for minus durations.
  84. if string(d.input[0]) == "-" {
  85. duration += "-"
  86. d.duration = -d.duration
  87. }
  88. var microseconds int64
  89. var milliseconds int64
  90. var seconds int64
  91. var minutes int64
  92. var hours int64
  93. var days int64
  94. var weeks int64
  95. var years int64
  96. var shouldConvert = false
  97. remainingSecondsToConvert := int64(d.duration / time.Microsecond)
  98. // Convert duration.
  99. if d.limitUnit == "" {
  100. shouldConvert = true
  101. }
  102. if d.limitUnit == "years" || shouldConvert {
  103. years = remainingSecondsToConvert / (365 * 24 * 3600 * 1000000)
  104. remainingSecondsToConvert -= years * 365 * 24 * 3600 * 1000000
  105. shouldConvert = true
  106. }
  107. if d.limitUnit == "weeks" || shouldConvert {
  108. weeks = remainingSecondsToConvert / (7 * 24 * 3600 * 1000000)
  109. remainingSecondsToConvert -= weeks * 7 * 24 * 3600 * 1000000
  110. shouldConvert = true
  111. }
  112. if d.limitUnit == "days" || shouldConvert {
  113. days = remainingSecondsToConvert / (24 * 3600 * 1000000)
  114. remainingSecondsToConvert -= days * 24 * 3600 * 1000000
  115. shouldConvert = true
  116. }
  117. if d.limitUnit == "hours" || shouldConvert {
  118. hours = remainingSecondsToConvert / (3600 * 1000000)
  119. remainingSecondsToConvert -= hours * 3600 * 1000000
  120. shouldConvert = true
  121. }
  122. if d.limitUnit == "minutes" || shouldConvert {
  123. minutes = remainingSecondsToConvert / (60 * 1000000)
  124. remainingSecondsToConvert -= minutes * 60 * 1000000
  125. shouldConvert = true
  126. }
  127. if d.limitUnit == "seconds" || shouldConvert {
  128. seconds = remainingSecondsToConvert / 1000000
  129. remainingSecondsToConvert -= seconds * 1000000
  130. shouldConvert = true
  131. }
  132. if d.limitUnit == "milliseconds" || shouldConvert {
  133. milliseconds = remainingSecondsToConvert / 1000
  134. remainingSecondsToConvert -= milliseconds * 1000
  135. }
  136. microseconds = remainingSecondsToConvert
  137. // Create a map of the converted duration time.
  138. durationMap := []int64{
  139. microseconds,
  140. milliseconds,
  141. seconds,
  142. minutes,
  143. hours,
  144. days,
  145. weeks,
  146. years,
  147. }
  148. // Construct duration string.
  149. for i, u := range units.Units() {
  150. v := durationMap[7-i]
  151. strval := strconv.FormatInt(v, 10)
  152. switch {
  153. // add to the duration string if v > 1.
  154. case v > 1:
  155. duration += strval + " " + u.Plural + " "
  156. // remove the plural 's', if v is 1.
  157. case v == 1:
  158. duration += strval + " " + u.Singular + " "
  159. // omit any value with 0s or 0.
  160. case d.duration.String() == "0" || d.duration.String() == "0s":
  161. pattern := fmt.Sprintf("^-?0%s$", unitsShort[i])
  162. isMatch, err := regexp.MatchString(pattern, d.input)
  163. if err != nil {
  164. return ""
  165. }
  166. if isMatch {
  167. duration += strval + " " + u.Plural
  168. }
  169. // omit any value with 0.
  170. case v == 0:
  171. continue
  172. }
  173. }
  174. // trim any remaining spaces.
  175. duration = strings.TrimSpace(duration)
  176. // if more than 2 spaces present return the first 2 strings
  177. // if short version is requested
  178. if d.limitN > 0 {
  179. parts := strings.Split(duration, " ")
  180. if len(parts) > d.limitN*2 {
  181. duration = strings.Join(parts[:d.limitN*2], " ")
  182. }
  183. }
  184. return duration
  185. }
  186. func (d *Durafmt) InternationalString() string {
  187. var duration string
  188. // Check for minus durations.
  189. if string(d.input[0]) == "-" {
  190. duration += "-"
  191. d.duration = -d.duration
  192. }
  193. var microseconds int64
  194. var milliseconds int64
  195. var seconds int64
  196. var minutes int64
  197. var hours int64
  198. var days int64
  199. var weeks int64
  200. var years int64
  201. var shouldConvert = false
  202. remainingSecondsToConvert := int64(d.duration / time.Microsecond)
  203. // Convert duration.
  204. if d.limitUnit == "" {
  205. shouldConvert = true
  206. }
  207. if d.limitUnit == "years" || shouldConvert {
  208. years = remainingSecondsToConvert / (365 * 24 * 3600 * 1000000)
  209. remainingSecondsToConvert -= years * 365 * 24 * 3600 * 1000000
  210. shouldConvert = true
  211. }
  212. if d.limitUnit == "weeks" || shouldConvert {
  213. weeks = remainingSecondsToConvert / (7 * 24 * 3600 * 1000000)
  214. remainingSecondsToConvert -= weeks * 7 * 24 * 3600 * 1000000
  215. shouldConvert = true
  216. }
  217. if d.limitUnit == "days" || shouldConvert {
  218. days = remainingSecondsToConvert / (24 * 3600 * 1000000)
  219. remainingSecondsToConvert -= days * 24 * 3600 * 1000000
  220. shouldConvert = true
  221. }
  222. if d.limitUnit == "hours" || shouldConvert {
  223. hours = remainingSecondsToConvert / (3600 * 1000000)
  224. remainingSecondsToConvert -= hours * 3600 * 1000000
  225. shouldConvert = true
  226. }
  227. if d.limitUnit == "minutes" || shouldConvert {
  228. minutes = remainingSecondsToConvert / (60 * 1000000)
  229. remainingSecondsToConvert -= minutes * 60 * 1000000
  230. shouldConvert = true
  231. }
  232. if d.limitUnit == "seconds" || shouldConvert {
  233. seconds = remainingSecondsToConvert / 1000000
  234. remainingSecondsToConvert -= seconds * 1000000
  235. shouldConvert = true
  236. }
  237. if d.limitUnit == "milliseconds" || shouldConvert {
  238. milliseconds = remainingSecondsToConvert / 1000
  239. remainingSecondsToConvert -= milliseconds * 1000
  240. }
  241. microseconds = remainingSecondsToConvert
  242. // Create a map of the converted duration time.
  243. durationMap := map[string]int64{
  244. "µs": microseconds,
  245. "ms": milliseconds,
  246. "s": seconds,
  247. "m": minutes,
  248. "h": hours,
  249. "d": days,
  250. "w": weeks,
  251. "y": years,
  252. }
  253. // Construct duration string.
  254. for i := range units.Units() {
  255. u := unitsShort[i]
  256. v := durationMap[u]
  257. strval := strconv.FormatInt(v, 10)
  258. switch {
  259. // add to the duration string if v > 0.
  260. case v > 0:
  261. duration += strval + " " + u + " "
  262. // omit any value with 0.
  263. case d.duration.String() == "0":
  264. pattern := fmt.Sprintf("^-?0%s$", unitsShort[i])
  265. isMatch, err := regexp.MatchString(pattern, d.input)
  266. if err != nil {
  267. return ""
  268. }
  269. if isMatch {
  270. duration += strval + " " + u
  271. }
  272. // omit any value with 0.
  273. case v == 0:
  274. continue
  275. }
  276. }
  277. // trim any remaining spaces.
  278. duration = strings.TrimSpace(duration)
  279. // if more than 2 spaces present return the first 2 strings
  280. // if short version is requested
  281. if d.limitN > 0 {
  282. parts := strings.Split(duration, " ")
  283. if len(parts) > d.limitN*2 {
  284. duration = strings.Join(parts[:d.limitN*2], " ")
  285. }
  286. }
  287. return duration
  288. }
  289. func (d *Durafmt) TrimSpace() string {
  290. return strings.Replace(d.String(), " ", "", -1)
  291. }