Compare commits

...

9 Commits
0.2.0 ... main

Author SHA1 Message Date
a9308cbaf3 Add ISO8601 date format (yyyy-MM-dd) to parseIso8601 utility function. 2023-02-28 23:25:44 -06:00
f4e392f910 Update for Nim 1.3.1
Change `startOfDay` and `trimNanoSec` to use `initDateTime` instead of
mutating the new copy via the deprecated property accessors.

Updated the test suite to reflect functions that moved from this library
to the standard library. We're still testing the expected functionality
to make sure that the contract is maintained for users of this library.
2021-07-17 09:45:39 -05:00
600747e1ac Update for Nim 1.0.0 - Added format for Durations. Remove deprecated functions. 2020-02-14 12:22:05 -06:00
08c0962b40 Add addtional valid ISO8601 formats. 2019-12-27 10:07:04 -06:00
a537b980e6 Expand allowed ISO8601 parsing formats. 2019-04-27 23:15:14 -05:00
Jonathan Bernard
dfee8aae33 Update for Nim 0.19.0, add trimNanoSec 2018-12-09 06:03:53 -06:00
Jonathan Bernard
f831fd6238 Add , , and . 2018-05-12 13:11:02 -05:00
Jonathan Bernard
8987a4e268 Changes to support Nim 0.18 (with new times implementation). 2018-04-02 14:24:40 -05:00
Jonathan Bernard
b4804cdb52 WIP: Waiting for fix to be merged in Nim core so I can deprecate fixedParse. 2016-10-21 17:35:16 -05:00
5 changed files with 170 additions and 168 deletions

View File

@ -1,126 +0,0 @@
import times, timeutils, unittest
let dtFormat = "yyyy-MM-dd HH:mm:ss"
suite "timeutils":
test "format TimeInterval":
let interval = seconds(70)
check:
interval.format("ss's'") == "10s"
interval.format("mm'm' ss's'") == "01m 10s"
interval.format("mm:ss") == "01:10"
test "TimeInfo difference":
var t1 = getLocalTime(getTime())
var t2 = t1 + 30.seconds
check t2 - t1 == 30.seconds
t1 = parse("2016-10-10 09:45:00", "yyyy-MM-dd HH:mm:ss")
t2 = parse("2016-10-11 09:45:00", "yyyy-MM-dd HH:mm:ss")
check t2 - t1 == 1.days
t2 = parse("2016-10-11 10:00:00", "yyyy-MM-dd HH:mm:ss")
check t2 - t1 == (1.days + 15.minutes)
test "TimeInfo comparisons":
let t1 = getLocalTime(getTime())
check:
t1 < t1 + 10.seconds
not (t1 + 10.seconds < t1)
not (t1 > t1)
t1 + 10.seconds > t1
not (t1 > t1 + 10.seconds)
not (t1 < t1)
t1 + 10.seconds >= t1
t1 >= t1
not (t1 >= t1 + 10.seconds)
t1 <= t1 + 10.seconds
t1 <= t1
not (t1 + 10.seconds <= t1)
test "TimeInterval comparisons":
check:
30.seconds > 10.seconds
not (10.seconds > 30.seconds)
not (10.seconds > 10.seconds)
10.minutes < 1.hours
not (1.hours < 10.minutes)
not (1.hours < 1.hours)
60.seconds >= 1.minutes
60.seconds >= 1.minutes
not (60.seconds >= 2.minutes)
60.seconds <= 1.minutes
60.seconds <= 2.minutes
not (2.minutes <= 60.seconds)
test "TimeInfo cmp":
let t1 = getLocalTime(getTime())
check cmp(t1, t1) == 0
check cmp(t1, t1 + 10.seconds) == -1
check cmp(t1 + 10.seconds, t1) == 1
test "startOfDay":
let t1 = fixedParse("13:42:19", "HH:mm:ss")
let t2 = fixedParse("2015-12-31 23:59:59", dtFormat)
check:
fixedParse("00:00:00", "HH:mm:ss") == startOfDay(t1)
#check fixedParse("2015-12-31 00:00:00", dtFormat) == startOfDay(t2)
startOfDay(startOfDay(t1)) == startOfDay(t1)
test "startOfWeek":
let t1 = fixedParse("2015-12-31 23:59:59", dtFormat)
let t2 = fixedParse("2015-12-26 23:59:59", dtFormat)
let t3 = fixedParse("2016-01-01 23:59:59", dtFormat)
# Not parsing the start of the day in order to work around the bug
# mentioned above.
check:
# Start of week = Monday
startOfWeek(t1) == startOfDay(getLocalTime(toTime(fixedParse("2015-12-28 12:01:00", dtFormat))))
startOfWeek(t1).weekday == dMon
startOfWeek(startOfWeek(t1)) == startOfWeek(t1)
startOfWeek(t2) == startOfDay(fixedParse("2015-12-21 01:00:00", dtFormat))
startOfWeek(t3) == startOfDay(fixedParse("2015-12-28 01:00:00", dtFormat))
# Start of week = Sunday
startOfWeek(t1, dSun) == startOfDay(fixedParse("2015-12-27 01:00:00", dtFormat))
startOfWeek(t1, dSun).weekday == dSun
startOfWeek(startOfWeek(t1, dSun), dSun) == startOfWeek(t1, dSun)
startOfWeek(t2, dSun) == startOfDay(fixedParse("2015-12-20 01:00:00", dtFormat))
startOfWeek(t3, dSun) == startOfDay(fixedParse("2015-12-27 01:00:00", dtFormat))
test "times.parse is still broken":
let t1 = parse("2015-12-01 12:00:00", dtFormat)
let t2 = parse("2015-06-01 12:00:00", dtFormat)
# parse is broken in that is uses the DST setting of the current time when
# parsing dates when it should figure out the DST time for that date. So
# depending on if you are currently in DST or not, one of the above dates
# will not parse correctly. We want to check that one of those fails to
# parse correctly. When they both parse correctly, the times.parse bug has
# been fixed and fixedParse is no longer necessary.
# This test works because passing the time through getLocalTime(toTime())
# correctly the DST setting for the time.
check t1 != getLocalTime(toTime(t1)) or t2 != getLocalTime(toTime(t2))
test "fixedParse":
let t1 = fixedParse("2015-12-01 12:00:00", dtFormat)
let t2 = fixedParse("2015-06-01 12:00:00", dtFormat)
check: # test both in DST and out of DST
t1 == getLocalTime(toTime(t1))
t2 == getLocalTime(toTime(t2))

115
tests/ttimeutils.nim Normal file
View File

@ -0,0 +1,115 @@
import times, unittest
import "../timeutils"
let dtFormat = "yyyy-MM-dd HH:mm:ss"
suite "timeutils":
test "format TimeInterval":
let interval = seconds(70)
check:
interval.format("ss's'") == "10s"
interval.format("mm'm' ss's'") == "01m 10s"
interval.format("mm:ss") == "01:10"
test "DateTime difference":
var t1 = getTime().local
var t2 = t1 + 30.seconds
check t2 - t1 == initDuration(seconds = 30)
t1 = parse("2016-10-10 09:45:00", "yyyy-MM-dd HH:mm:ss")
t2 = parse("2016-10-11 09:45:00", "yyyy-MM-dd HH:mm:ss")
check t2 - t1 == initDuration(seconds = 24 * 60 * 60)
t2 = parse("2016-10-11 10:00:00", "yyyy-MM-dd HH:mm:ss")
check t2 - t1 == initDuration(seconds = (24 * 60 + 15) * 60)
test "DateTime comparisons":
let t1 = getTime().local
check:
not (t1 < t1)
t1 < t1 + 10.seconds
not (t1 + 10.seconds < t1)
t1 <= t1
t1 <= t1 + 10.seconds
not (t1 + 10.seconds <= t1)
t1.between(t1 - 10.seconds, t1 + 10.seconds)
t1.between(t1, t1 + 10.seconds) # start is inclusive
not t1.between(t1 - 10.seconds, t1) # end is exclusive
test "TimeInterval comparisons":
check:
timeutils.`<`(10.minutes, 1.hours)
not timeutils.`<`(1.hours, 10.minutes)
not timeutils.`<`(1.hours, 1.hours)
timeutils.`<=`(60.seconds, 1.minutes)
timeutils.`<=`(60.seconds, 2.minutes)
not timeutils.`<=`(2.minutes, 60.seconds)
test "DateTime cmp":
let t1 = getTime().local
check cmp(t1, t1) == 0
check cmp(t1, t1 + 10.seconds) == -1
check cmp(t1 + 10.seconds, t1) == 1
test "startOfDay":
let t1 = parse("13:42:19", "HH:mm:ss")
let t2 = parse("2015-12-31 23:59:59", dtFormat)
check:
parse("00:00:00", "HH:mm:ss") == startOfDay(t1)
#check parse("2015-12-31 00:00:00", dtFormat) == startOfDay(t2)
startOfDay(startOfDay(t1)) == startOfDay(t1)
test "startOfWeek":
let t1 = parse("2015-12-31 23:59:59", dtFormat)
let t2 = parse("2015-12-26 23:59:59", dtFormat)
let t3 = parse("2016-01-01 23:59:59", dtFormat)
# Not parsing the start of the day in order to work around the bug
# mentioned above.
check:
# Start of week = Monday
startOfWeek(t1) == startOfDay(toTime(parse("2015-12-28 12:01:00", dtFormat)).local)
startOfWeek(t1).weekday == dMon
startOfWeek(startOfWeek(t1)) == startOfWeek(t1)
startOfWeek(t2) == startOfDay(parse("2015-12-21 01:00:00", dtFormat))
startOfWeek(t3) == startOfDay(parse("2015-12-28 01:00:00", dtFormat))
# Start of week = Sunday
startOfWeek(t1, dSun) == startOfDay(parse("2015-12-27 01:00:00", dtFormat))
startOfWeek(t1, dSun).weekday == dSun
startOfWeek(startOfWeek(t1, dSun), dSun) == startOfWeek(t1, dSun)
startOfWeek(t2, dSun) == startOfDay(parse("2015-12-20 01:00:00", dtFormat))
startOfWeek(t3, dSun) == startOfDay(parse("2015-12-27 01:00:00", dtFormat))
test "fixedParse":
let t1 = fixedParse("2015-12-01 12:00:00", dtFormat)
let t2 = fixedParse("2015-06-01 12:00:00", dtFormat)
check: # test both in DST and out of DST
t1 == toTime(t1).local
t2 == toTime(t2).local
test "parseIso8601":
let t1 = parseIso8601("2018-01-01T12:00:00-05:00")
let t2 = parseIso8601("2018-01-01T17:00:00Z")
check:
t1 == t2
test "formatIso8601":
let t1 = parseIso8601("2018-01-01T12:00:00-05:00")
let t2 = parseIso8601("2018-01-01T17:00:00Z")
check:
t1 == parseIso8601(formatIso8601(t1))
t2 == parseIso8601(formatIso8601(t2))

View File

@ -1,60 +1,74 @@
import times
const zeroTime = fromSeconds(0)
const zeroTime = fromUnix(0)
const ISO_8601_FORMATS = @[
"yyyy-MM-dd",
"yyyy-MM-dd'T'HH:mm:ssz",
"yyyy-MM-dd'T'HH:mm:sszzz",
"yyyy-MM-dd'T'HH:mm:ss'.'fffzzz",
"yyyy-MM-dd HH:mm:ssz",
"yyyy-MM-dd HH:mm:sszzz",
"yyyy-MM-dd HH:mm:ss'.'fffzzz"
]
proc format*(ti: TimeInterval, fmt: string): string =
let info = getGMTime(fromSeconds(0) + ti)
let info = (zeroTime + ti).utc
return info.format(fmt)
proc `-`*(a, b: TimeInfo): TimeInterval =
return seconds(cast[int](a.toTime - b.toTime))
proc `>`*(a, b: TimeInfo): bool =
return a.toTime > b.toTime
proc `<`*(a, b: TimeInfo): bool =
return a.toTime < b.toTime
proc `>=`*(a, b: TimeInfo): bool =
return a.toTime >= b.toTime
proc `<=`*(a, b: TimeInfo): bool =
return a.toTime <= b.toTime
proc `>`*(a, b: TimeInterval): bool =
return (zeroTime + a) > (zeroTime + b)
proc format*(d: Duration, fmt: string): string =
let info = (fromUnix(0) + d).utc
return info.format(fmt)
proc `<`*(a, b: TimeInterval): bool =
return (zeroTime + a) < (zeroTime + b)
proc `>=`*(a, b: TimeInterval): bool =
return (zeroTime + a) >= (zeroTime + b)
proc `<=`*(a, b: TimeInterval): bool =
return (zeroTime + a) <= (zeroTime + b)
proc cmp*(a, b: TimeInfo): int =
proc between*(a, s, e: DateTime): bool =
return times.`<=`(s, a) and times.`<`(a, e)
proc cmp*(a, b: DateTime): int =
if b == a: return 0
elif a > b: return 1
else: return -1
elif times.`<`(a, b): return -1
else: return 1
proc startOfDay*(ti: TimeInfo): TimeInfo =
result = ti
result.hour = 0
result.minute = 0
result.second = 0
proc startOfDay*(dt: DateTime): DateTime =
result = initDateTime(
dt.monthday,
dt.month,
dt.year,
0, 0, 0, 0, # hour, minute, second, nanosecond
dt.timezone)
proc startOfWeek*(ti: TimeInfo, startDay = dMon): TimeInfo =
proc trimNanoSec*(dt: DateTime): DateTime =
result = dt
result = initDateTime(
dt.monthday,
dt.month,
dt.year,
dt.hour,
dt.minute,
dt.second,
0,
dt.timezone)
proc startOfWeek*(ti: DateTime, startDay = dMon): DateTime =
var diff = (ti.weekday.ord - startDay.ord)
if diff < 0: diff += 7
return (ti - diff.days).startOfDay
proc parseIso8601*(val: string): DateTime =
var errString = ""
for df in ISO_8601_FORMATS:
try: return val.parse(df)
except: errString &= "\n" & getCurrentExceptionMsg()
raise newException(Exception, "Could not parse date. Tried:" & errString)
proc formatIso8601*(d: DateTime): string =
return d.format(ISO_8601_FORMATS[2])
# This is a workaround needed due a bug in Nim's times.parse procedure.
# see: https://github.com/nim-lang/Nim/issues/4922
proc fixedParse*(value, format: string): TimeInfo =
let firstParsed = parse(value, format)
let rectified = getLocalTime(toTime(firstParsed))
if firstParsed.isDST and not rectified.isDST: return rectified + 1.hours
elif not firstParsed.isDST and rectified.isDST: return rectified - 1.hours
else: return rectified
template fixedParse*(value, format: string): DateTime {.deprecated.} =
parse(value, format)

View File

@ -1,11 +1,10 @@
# Package
version = "0.2.0"
version = "0.5.4"
author = "Jonathan Bernard"
description = "Utility methods to fill in the lacking time support in Nim\'s stdlib. This is holding me over until I can write a proper time module for the stdlib and submit it."
description = "Utility methods to fill in the lacking time support in Nim\'s stdlib."
license = "BSD3"
# Dependencies
requires "nim >= 0.15.0"
requires "nim >= 1.3.1"