fmt.go 8.5 KB

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