Sub second time resolution (#6978)
* Add deprecation warnings to recently deprecated procs * Fix bad usage of the times module * Introduce sub second resolution * Fix usage of C's time() * Switch to nanosecond resolution * Make Time & Duration opaque again and fix some errors * Change back to TimeInterval for shorthands * Fix JS test * Fix build error for windows * Undeprecate epochTime * Documentation and minor changes * Lots of bugfixes and doc comments * Attempt to make travis & appveyor green * Fix edge cases for dealing with the local timezone * Workaround JS backend overflow/underflow bug * Use better workaround for not knowing the size of time_t * Use all available timezones for tests * Fix indentation * Add procs for accessing the fractional part of a duration * Order time units from smallest to largest since it makes more sense * Include months and years in `TimeUnit` * Review fix
This commit is contained in:
parent
19a1cc914f
commit
f6df2d9956
7 changed files with 699 additions and 296 deletions
|
|
@ -77,8 +77,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||||
cbos copyDir:
|
cbos copyDir:
|
||||||
os.copyDir(getString(a, 0), getString(a, 1))
|
os.copyDir(getString(a, 0), getString(a, 1))
|
||||||
cbos getLastModificationTime:
|
cbos getLastModificationTime:
|
||||||
# depends on Time's implementation!
|
setResult(a, getLastModificationTime(getString(a, 0)).toUnix)
|
||||||
setResult(a, int64(getLastModificationTime(getString(a, 0))))
|
|
||||||
cbos findExe:
|
cbos findExe:
|
||||||
setResult(a, os.findExe(getString(a, 0)))
|
setResult(a, os.findExe(getString(a, 0)))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ proc genOid*(): Oid =
|
||||||
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
||||||
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
|
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
|
||||||
|
|
||||||
var t = getTime().int32
|
var t = getTime().toUnix.int32
|
||||||
|
|
||||||
var i = int32(atomicInc(incr))
|
var i = int32(atomicInc(incr))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -225,10 +225,10 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
|
||||||
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
|
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
|
||||||
## modification time is later than `b`'s.
|
## modification time is later than `b`'s.
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
result = getLastModificationTime(a) - getLastModificationTime(b) >= 0
|
result = getLastModificationTime(a) - getLastModificationTime(b) >= DurationZero
|
||||||
# Posix's resolution sucks so, we use '>=' for posix.
|
# Posix's resolution sucks so, we use '>=' for posix.
|
||||||
else:
|
else:
|
||||||
result = getLastModificationTime(a) - getLastModificationTime(b) > 0
|
result = getLastModificationTime(a) - getLastModificationTime(b) > DurationZero
|
||||||
|
|
||||||
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
|
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
|
||||||
## Returns the `current working directory`:idx:.
|
## Returns the `current working directory`:idx:.
|
||||||
|
|
|
||||||
|
|
@ -191,8 +191,8 @@ when not defined(nimscript):
|
||||||
proc randomize*() {.benign.} =
|
proc randomize*() {.benign.} =
|
||||||
## Initializes the random number generator with a "random"
|
## Initializes the random number generator with a "random"
|
||||||
## number, i.e. a tickcount. Note: Does not work for NimScript.
|
## number, i.e. a tickcount. Note: Does not work for NimScript.
|
||||||
let time = int64(times.epochTime() * 1_000_000_000)
|
let now = times.getTime()
|
||||||
randomize(time)
|
randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanoseconds)
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -17,14 +17,14 @@ block localTime:
|
||||||
|
|
||||||
let a = fromUnix(1_000_000_000)
|
let a = fromUnix(1_000_000_000)
|
||||||
let b = fromUnix(1_500_000_000)
|
let b = fromUnix(1_500_000_000)
|
||||||
doAssert b - a == 500_000_000
|
doAssert b - a == initDuration(seconds = 500_000_000)
|
||||||
|
|
||||||
# Because we can't change the timezone JS uses, we define a simple static timezone for testing.
|
# Because we can't change the timezone JS uses, we define a simple static timezone for testing.
|
||||||
|
|
||||||
proc staticZoneInfoFromUtc(time: Time): ZonedTime =
|
proc staticZoneInfoFromUtc(time: Time): ZonedTime =
|
||||||
result.utcOffset = -7200
|
result.utcOffset = -7200
|
||||||
result.isDst = false
|
result.isDst = false
|
||||||
result.adjTime = (time.toUnix + 7200).Time
|
result.adjTime = time + 7200.seconds
|
||||||
|
|
||||||
proc staticZoneInfoFromTz(adjTime: Time): ZonedTIme =
|
proc staticZoneInfoFromTz(adjTime: Time): ZonedTIme =
|
||||||
result.utcOffset = -7200
|
result.utcOffset = -7200
|
||||||
|
|
@ -40,5 +40,4 @@ block timezoneTests:
|
||||||
doAssert $dt.utc.inZone(utcPlus2) == $dt
|
doAssert $dt.utc.inZone(utcPlus2) == $dt
|
||||||
|
|
||||||
doAssert $initDateTime(01, mJan, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00+00:00"
|
doAssert $initDateTime(01, mJan, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00+00:00"
|
||||||
# See #6752
|
doAssert $initDateTime(01, mJan, 0023, 12, 00, 00, utc()) == "0023-01-01T12:00:00+00:00"
|
||||||
# doAssert $initDateTime(01, mJan, 1900, 12, 00, 00, utc()) == "0023-01-01T12:00:00+00:00"
|
|
||||||
|
|
@ -37,8 +37,8 @@ var t4 = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997
|
||||||
t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
|
t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
|
||||||
|
|
||||||
# Interval tests
|
# Interval tests
|
||||||
(t4 - initInterval(years = 2)).checkFormat("yyyy", "1995")
|
(t4 - initTimeInterval(years = 2)).checkFormat("yyyy", "1995")
|
||||||
(t4 - initInterval(years = 7, minutes = 34, seconds = 24)).checkFormat("yyyy mm ss", "1990 24 10")
|
(t4 - initTimeInterval(years = 7, minutes = 34, seconds = 24)).checkFormat("yyyy mm ss", "1990 24 10")
|
||||||
|
|
||||||
# checking dayOfWeek matches known days
|
# checking dayOfWeek matches known days
|
||||||
doAssert getDayOfWeek(01, mJan, 0000) == dSat
|
doAssert getDayOfWeek(01, mJan, 0000) == dSat
|
||||||
|
|
@ -56,32 +56,34 @@ doAssert toUnix(toTime(t4L)) + t4L.utcOffset == toUnix(toTime(t4))
|
||||||
|
|
||||||
# adding intervals
|
# adding intervals
|
||||||
var
|
var
|
||||||
a1L = toUnix(toTime(t4L + initInterval(hours = 1))) + t4L.utcOffset
|
a1L = toUnix(toTime(t4L + initTimeInterval(hours = 1))) + t4L.utcOffset
|
||||||
a1G = toUnix(toTime(t4)) + 60 * 60
|
a1G = toUnix(toTime(t4)) + 60 * 60
|
||||||
doAssert a1L == a1G
|
doAssert a1L == a1G
|
||||||
|
|
||||||
# subtracting intervals
|
# subtracting intervals
|
||||||
a1L = toUnix(toTime(t4L - initInterval(hours = 1))) + t4L.utcOffset
|
a1L = toUnix(toTime(t4L - initTimeInterval(hours = 1))) + t4L.utcOffset
|
||||||
a1G = toUnix(toTime(t4)) - (60 * 60)
|
a1G = toUnix(toTime(t4)) - (60 * 60)
|
||||||
doAssert a1L == a1G
|
doAssert a1L == a1G
|
||||||
|
|
||||||
# add/subtract TimeIntervals and Time/TimeInfo
|
# add/subtract TimeIntervals and Time/TimeInfo
|
||||||
doAssert getTime() - 1.seconds == getTime() - 3.seconds + 2.seconds
|
let now = getTime().utc
|
||||||
doAssert getTime() + 65.seconds == getTime() + 1.minutes + 5.seconds
|
doAssert now + convert(Seconds, Nanoseconds, 1).nanoseconds == now + 1.seconds
|
||||||
doAssert getTime() + 60.minutes == getTime() + 1.hours
|
doAssert now + 1.weeks == now + 7.days
|
||||||
doAssert getTime() + 24.hours == getTime() + 1.days
|
doAssert now - 1.seconds == now - 3.seconds + 2.seconds
|
||||||
doAssert getTime() + 13.months == getTime() + 1.years + 1.months
|
doAssert now + 65.seconds == now + 1.minutes + 5.seconds
|
||||||
var
|
doAssert now + 60.minutes == now + 1.hours
|
||||||
ti1 = getTime() + 1.years
|
doAssert now + 24.hours == now + 1.days
|
||||||
ti1 -= 1.years
|
doAssert now + 13.months == now + 1.years + 1.months
|
||||||
doAssert ti1 == getTime()
|
var ti1 = now + 1.years
|
||||||
ti1 += 1.days
|
ti1 = ti1 - 1.years
|
||||||
doAssert ti1 == getTime() + 1.days
|
doAssert ti1 == now
|
||||||
|
ti1 = ti1 + 1.days
|
||||||
|
doAssert ti1 == now + 1.days
|
||||||
|
|
||||||
# Bug with adding a day to a Time
|
# Bug with adding a day to a Time
|
||||||
let day = 24.hours
|
let day = 24.hours
|
||||||
let tomorrow = getTime() + day
|
let tomorrow = now + day
|
||||||
doAssert tomorrow - getTime() == 60*60*24
|
doAssert tomorrow - now == initDuration(days = 1)
|
||||||
|
|
||||||
# Comparison between Time objects should be detected by compiler
|
# Comparison between Time objects should be detected by compiler
|
||||||
# as 'noSideEffect'.
|
# as 'noSideEffect'.
|
||||||
|
|
@ -207,8 +209,8 @@ template runTimezoneTests() =
|
||||||
let
|
let
|
||||||
parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
|
parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
|
||||||
parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
|
parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
|
||||||
doAssert toTime(parsedJan) == fromUnix(1451962800)
|
doAssert toTime(parsedJan).toUnix == 1451962800
|
||||||
doAssert toTime(parsedJul) == fromUnix(1467342000)
|
doAssert toTime(parsedJul).toUnix == 1467342000
|
||||||
|
|
||||||
suite "ttimes":
|
suite "ttimes":
|
||||||
|
|
||||||
|
|
@ -220,7 +222,7 @@ suite "ttimes":
|
||||||
|
|
||||||
let orig_tz = getEnv("TZ")
|
let orig_tz = getEnv("TZ")
|
||||||
var tz_cnt = 0
|
var tz_cnt = 0
|
||||||
for tz_fn in walkFiles(tz_dir & "/*"):
|
for tz_fn in walkFiles(tz_dir & "/**/*"):
|
||||||
if symlinkExists(tz_fn) or tz_fn.endsWith(".tab") or
|
if symlinkExists(tz_fn) or tz_fn.endsWith(".tab") or
|
||||||
tz_fn.endsWith(".list"):
|
tz_fn.endsWith(".list"):
|
||||||
continue
|
continue
|
||||||
|
|
@ -252,19 +254,20 @@ suite "ttimes":
|
||||||
var local = fromUnix(1469275200).local
|
var local = fromUnix(1469275200).local
|
||||||
var utc = fromUnix(1469275200).utc
|
var utc = fromUnix(1469275200).utc
|
||||||
|
|
||||||
let claimedOffset = local.utcOffset
|
let claimedOffset = initDuration(seconds = local.utcOffset)
|
||||||
local.utcOffset = 0
|
local.utcOffset = 0
|
||||||
check claimedOffset == utc.toTime - local.toTime
|
check claimedOffset == utc.toTime - local.toTime
|
||||||
|
|
||||||
test "issue #5704":
|
test "issue #5704":
|
||||||
putEnv("TZ", "Asia/Seoul")
|
putEnv("TZ", "Asia/Seoul")
|
||||||
let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime - parse("19000101-000000", "yyyyMMdd-hhmmss").toTime
|
let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime - parse("19000101-000000", "yyyyMMdd-hhmmss").toTime
|
||||||
check diff == 2208986872
|
check diff == initDuration(seconds = 2208986872)
|
||||||
|
|
||||||
test "issue #6465":
|
test "issue #6465":
|
||||||
putEnv("TZ", "Europe/Stockholm")
|
putEnv("TZ", "Europe/Stockholm")
|
||||||
let dt = parse("2017-03-25 12:00", "yyyy-MM-dd hh:mm")
|
let dt = parse("2017-03-25 12:00", "yyyy-MM-dd hh:mm")
|
||||||
check $(dt + 1.days) == "2017-03-26T12:00:00+02:00"
|
check $(dt + initTimeInterval(days = 1)) == "2017-03-26T12:00:00+02:00"
|
||||||
|
check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"
|
||||||
|
|
||||||
test "datetime before epoch":
|
test "datetime before epoch":
|
||||||
check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52+00:00"
|
check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52+00:00"
|
||||||
|
|
@ -362,12 +365,40 @@ suite "ttimes":
|
||||||
|
|
||||||
test "subtract months":
|
test "subtract months":
|
||||||
var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc())
|
var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc())
|
||||||
check $(dt - 1.months) == "2017-01-01T00:00:00+00:00"
|
check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00+00:00"
|
||||||
dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc())
|
dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc())
|
||||||
check $(dt - 1.months) == "2017-02-15T00:00:00+00:00"
|
check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00+00:00"
|
||||||
dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc())
|
dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc())
|
||||||
# This happens due to monthday overflow. It's consistent with Phobos.
|
# This happens due to monthday overflow. It's consistent with Phobos.
|
||||||
check $(dt - 1.months) == "2017-03-03T00:00:00+00:00"
|
check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00+00:00"
|
||||||
|
|
||||||
|
test "duration":
|
||||||
|
let d = initDuration
|
||||||
|
check d(hours = 48) + d(days = 5) == d(weeks = 1)
|
||||||
|
let dt = initDateTime(01, mFeb, 2000, 00, 00, 00, 0, utc()) + d(milliseconds = 1)
|
||||||
|
check dt.nanosecond == convert(Milliseconds, Nanoseconds, 1)
|
||||||
|
check d(seconds = 1, milliseconds = 500) * 2 == d(seconds = 3)
|
||||||
|
check d(seconds = 3) div 2 == d(seconds = 1, milliseconds = 500)
|
||||||
|
check d(milliseconds = 1001).seconds == 1
|
||||||
|
check d(seconds = 1, milliseconds = 500) - d(milliseconds = 1250) ==
|
||||||
|
d(milliseconds = 250)
|
||||||
|
check d(seconds = 1, milliseconds = 1) < d(seconds = 1, milliseconds = 2)
|
||||||
|
check d(seconds = 1) <= d(seconds = 1)
|
||||||
|
check d(seconds = 0) - d(milliseconds = 1500) == d(milliseconds = -1500)
|
||||||
|
check d(milliseconds = -1500) == d(seconds = -1, milliseconds = -500)
|
||||||
|
check d(seconds = -1, milliseconds = 500) == d(milliseconds = -500)
|
||||||
|
|
||||||
|
test "large/small dates":
|
||||||
|
discard initDateTime(1, mJan, -35_000, 12, 00, 00, utc())
|
||||||
|
# with local tz
|
||||||
|
discard initDateTime(1, mJan, -35_000, 12, 00, 00)
|
||||||
|
discard initDateTime(1, mJan, 35_000, 12, 00, 00)
|
||||||
|
# with duration/timeinterval
|
||||||
|
let dt = initDateTime(1, mJan, 35_000, 12, 00, 00, utc()) +
|
||||||
|
initDuration(seconds = 1)
|
||||||
|
check dt.second == 1
|
||||||
|
let dt2 = dt + 35_001.years
|
||||||
|
check $dt2 == "0001-01-01T12:00:01+00:00"
|
||||||
|
|
||||||
test "compare datetimes":
|
test "compare datetimes":
|
||||||
var dt1 = now()
|
var dt1 = now()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue