[add+change] Added toDurationParts proc to convert Duration to array[FixedTimeUnit, int64] of it's human-readable parts, use it in $Duration

This commit is contained in:
Michael Voronin 2018-05-31 15:50:18 +03:00
commit a573577cdc

View file

@ -183,6 +183,9 @@ type
## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``. ## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``.
isDst*: bool ## Determines whether DST is in effect. isDst*: bool ## Determines whether DST is in effect.
DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].} TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
@ -451,44 +454,55 @@ const DurationZero* = initDuration() ## \
## doAssert initDuration(seconds = 1) > DurationZero ## doAssert initDuration(seconds = 1) > DurationZero
## doAssert initDuration(seconds = 0) == DurationZero ## doAssert initDuration(seconds = 0) == DurationZero
proc `$`*(dur: Duration): string = proc toParts*(dur: Duration): DurationParts =
## Human friendly string representation of ``dur``. ## 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: runnableExamples:
doAssert $initDuration(seconds = 2) == "2 seconds" var dp = toParts(initDuration(weeks=2, days=1))
doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days" doAssert dp[Days] == 1
doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds" doAssert dp[Weeks] == 2
doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds" dp = toParts(initDuration(days = -1))
var parts = newSeq[string]() doAssert dp[Days] == -1
var remS = dur.seconds var remS = dur.seconds
var remNs = dur.nanosecond.int var remNs = dur.nanosecond.int
# Normally ``nanoseconds`` should always be positive, but # Ensure the same sign for seconds and nanoseconds
# that makes no sense when printing. if remS < 0 and remNs != 0:
if remS < 0:
remNs -= convert(Seconds, Nanoseconds, 1) remNs -= convert(Seconds, Nanoseconds, 1)
remS.inc 1 remS.inc 1
const unitStrings: array[FixedTimeUnit, string] = [
"nanosecond", "microsecond", "millisecond", "second", "minute", "hour", "day", "week"
]
for unit in countdown(Weeks, Seconds): for unit in countdown(Weeks, Seconds):
let quantity = convert(Seconds, unit, remS) let quantity = convert(Seconds, unit, remS)
remS = remS mod convert(unit, Seconds, 1) remS = remS mod convert(unit, Seconds, 1)
if quantity.abs == 1: result[unit] = quantity
parts.add $quantity & " " & unitStrings[unit]
elif quantity != 0:
parts.add $quantity & " " & unitStrings[unit] & "s"
for unit in countdown(Milliseconds, Nanoseconds): for unit in countdown(Milliseconds, Nanoseconds):
let quantity = convert(Nanoseconds, unit, remNs) let quantity = convert(Nanoseconds, unit, remNs)
remNs = remNs mod convert(unit, Nanoseconds, 1) remNs = remNs mod convert(unit, Nanoseconds, 1)
if quantity.abs == 1: result[unit] = quantity
parts.add $quantity & " " & unitStrings[unit]
elif quantity != 0: proc stringifyUnit*(value: int | int64, unit: string): string =
parts.add $quantity & " " & unitStrings[unit] & "s" ## 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 = "" result = ""
if parts.len == 0: if parts.len == 0:
@ -502,6 +516,23 @@ proc `$`*(dur: Duration): string =
result.add part & ", " result.add part & ", "
result.add "and " & parts[high(parts)] 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.} = proc `+`*(a, b: Duration): Duration {.operator.} =
## Add two durations together. ## Add two durations together.
runnableExamples: runnableExamples: