summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authorMichael Voronin <m.voronin@ngenix.net>2018-05-31 15:50:18 +0300
committerMichael Voronin <m.voronin@ngenix.net>2018-05-31 16:01:58 +0300
commita573577cdc7d334c88088058dc00f0771ce9bb39 (patch)
tree503c4fc3111b48d3b20a5a6282f7f4989e87994a /lib/pure
parent08d1b5892b69548bf9a63353b8128e52e222e53d (diff)
downloadNim-a573577cdc7d334c88088058dc00f0771ce9bb39.tar.gz
[add+change] Added toDurationParts proc to convert Duration to array[FixedTimeUnit, int64] of it's human-readable parts, use it in `$Duration`
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/times.nim75
1 files changed, 53 insertions, 22 deletions
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index a6b6137e6..8eda73cc1 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -183,6 +183,9 @@ type
                     ## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``.
     isDst*: bool    ## Determines whether DST is in effect.
 
+  DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts
+
+
 {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
     TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
 
@@ -451,44 +454,55 @@ const DurationZero* = initDuration() ## \
   ##   doAssert initDuration(seconds = 1) > DurationZero
   ##   doAssert initDuration(seconds = 0) == DurationZero
 
-proc `$`*(dur: Duration): string =
-  ## Human friendly string representation of ``dur``.
+proc toParts*(dur: Duration): DurationParts =
+  ## Converts a duration into an array consisting of fixed time units.
+  ##
+  ## Each value in the array gives information about a specific unit of
+  ## time, for example ``result[Days]`` gives a count of days.
+  ##
+  ## This procedure is useful for converting ``Duration`` values to strings.
   runnableExamples:
-    doAssert $initDuration(seconds = 2) == "2 seconds"
-    doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days"
-    doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds"
-    doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds"
-  var parts = newSeq[string]()
+    var dp = toParts(initDuration(weeks=2, days=1))
+    doAssert dp[Days] == 1
+    doAssert dp[Weeks] == 2
+    dp = toParts(initDuration(days = -1))
+    doAssert dp[Days] == -1
+
   var remS = dur.seconds
   var remNs = dur.nanosecond.int
 
-  # Normally ``nanoseconds`` should always be positive, but
-  # that makes no sense when printing.
-  if remS < 0:
+  # Ensure the same sign for seconds and nanoseconds
+  if remS < 0 and remNs != 0:
     remNs -= convert(Seconds, Nanoseconds, 1)
     remS.inc 1
 
-  const unitStrings: array[FixedTimeUnit, string] = [
-    "nanosecond", "microsecond", "millisecond", "second", "minute", "hour", "day", "week"
-  ]
-
   for unit in countdown(Weeks, Seconds):
     let quantity = convert(Seconds, unit, remS)
     remS = remS mod convert(unit, Seconds, 1)
 
-    if quantity.abs == 1:
-      parts.add $quantity & " " & unitStrings[unit]
-    elif quantity != 0:
-      parts.add $quantity & " " & unitStrings[unit] & "s"
+    result[unit] = quantity
 
   for unit in countdown(Milliseconds, Nanoseconds):
     let quantity = convert(Nanoseconds, unit, remNs)
     remNs = remNs mod convert(unit, Nanoseconds, 1)
 
-    if quantity.abs == 1:
-      parts.add $quantity & " " & unitStrings[unit]
-    elif quantity != 0:
-      parts.add $quantity & " " & unitStrings[unit] & "s"
+    result[unit] = quantity
+
+proc stringifyUnit*(value: int | int64, unit: string): string =
+  ## Stringify time unit with it's name, lowercased
+  runnableExamples:
+    doAssert stringifyUnit(2, "Seconds") == "2 seconds"
+    doAssert stringifyUnit(1, "Years") == "1 year"
+  result = ""
+  result.add($value)
+  result.add(" ")
+  if abs(value) != 1:
+    result.add(unit.toLowerAscii())
+  else:
+    result.add(unit[0..^2].toLowerAscii())
+
+proc humanizeParts(parts: seq[string]): string =
+  ## Make date string parts human-readable
 
   result = ""
   if parts.len == 0:
@@ -502,6 +516,23 @@ proc `$`*(dur: Duration): string =
       result.add part & ", "
     result.add "and " & parts[high(parts)]
 
+proc `$`*(dur: Duration): string =
+  ## Human friendly string representation of ``Duration``.
+  runnableExamples:
+    doAssert $initDuration(seconds = 2) == "2 seconds"
+    doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days"
+    doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds"
+    doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds"
+  var parts = newSeq[string]()
+  var numParts = toParts(dur)
+
+  for unit in countdown(Weeks, Nanoseconds):
+    let quantity = numParts[unit]
+    if quantity != 0.int64:
+      parts.add(stringifyUnit(quantity, $unit))
+  
+  result = humanizeParts(parts)
+
 proc `+`*(a, b: Duration): Duration {.operator.} =
   ## Add two durations together.
   runnableExamples: