mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 17:34:43 +00:00
* Allow for locale-based parsing/formatting of dates * Updates based on review feedback of PR 11170 DateTimeLocale arrays are now indexed by Month and WeekDay enums. More sane date used for testing. Documentation newline. Case change of DefaultLocale (and make it public) * Add changelog entry for DateTimeLocale addition to times module * Use pattern symbols for DateTimeLocale attribute names
627 lines
24 KiB
Nim
627 lines
24 KiB
Nim
discard """
|
|
target: "c js"
|
|
"""
|
|
|
|
import times, strutils, unittest
|
|
|
|
when not defined(js):
|
|
import os
|
|
|
|
# Normally testament configures unittest with environment variables,
|
|
# but that doesn't work for the JS target. So instead we must set the correct
|
|
# settings here.
|
|
addOutputFormatter(
|
|
newConsoleOutputFormatter(PRINT_FAILURES, colorOutput = false))
|
|
|
|
proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} =
|
|
let offset = hours * 3600 + minutes * 60 + seconds
|
|
|
|
proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime {.locks: 0.} =
|
|
result.isDst = false
|
|
result.utcOffset = offset
|
|
result.time = adjTime + initDuration(seconds = offset)
|
|
|
|
proc zonedTimeFromTime(time: Time): ZonedTime {.locks: 0.}=
|
|
result.isDst = false
|
|
result.utcOffset = offset
|
|
result.time = time
|
|
|
|
newTimezone("", zonedTimeFromTime, zonedTImeFromAdjTime)
|
|
|
|
template parseTest(s, f, sExpected: string, ydExpected: int) =
|
|
let
|
|
parsed = s.parse(f, utc())
|
|
parsedStr = $parsed
|
|
check parsedStr == sExpected
|
|
check parsed.yearday == ydExpected
|
|
|
|
template parseTestExcp(s, f: string) =
|
|
expect ValueError:
|
|
let parsed = s.parse(f)
|
|
|
|
template parseTestTimeOnly(s, f, sExpected: string) =
|
|
check sExpected in $s.parse(f, utc())
|
|
|
|
# because setting a specific timezone for testing is platform-specific, we use
|
|
# explicit timezone offsets in all tests.
|
|
template runTimezoneTests() =
|
|
parseTest("Tuesday at 09:04am on Dec 15, 2015 +0",
|
|
"dddd 'at' hh:mmtt 'on' MMM d, yyyy z", "2015-12-15T09:04:00Z", 348)
|
|
# ANSIC = "Mon Jan _2 15:04:05 2006"
|
|
parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
|
|
"2006-01-12T15:04:05Z", 11)
|
|
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
|
|
parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
|
|
"2006-01-12T15:04:05Z", 11)
|
|
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
|
|
parseTest("Mon Feb 29 15:04:05 -07:00 2016 +0", "ddd MMM dd HH:mm:ss zzz yyyy z",
|
|
"2016-02-29T15:04:05Z", 59) # leap day
|
|
# RFC822 = "02 Jan 06 15:04 MST"
|
|
parseTest("12 Jan 16 15:04 +0", "dd MMM yy HH:mm z",
|
|
"2016-01-12T15:04:00Z", 11)
|
|
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
|
|
parseTest("01 Mar 16 15:04 -07:00", "dd MMM yy HH:mm zzz",
|
|
"2016-03-01T22:04:00Z", 60) # day after february in leap year
|
|
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
|
|
parseTest("Monday, 12-Jan-06 15:04:05 +0", "dddd, dd-MMM-yy HH:mm:ss z",
|
|
"2006-01-12T15:04:05Z", 11)
|
|
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
|
|
parseTest("Sun, 01 Mar 2015 15:04:05 +0", "ddd, dd MMM yyyy HH:mm:ss z",
|
|
"2015-03-01T15:04:05Z", 59) # day after february in non-leap year
|
|
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
|
|
parseTest("Thu, 12 Jan 2006 15:04:05 -07:00", "ddd, dd MMM yyyy HH:mm:ss zzz",
|
|
"2006-01-12T22:04:05Z", 11)
|
|
# RFC3339 = "2006-01-02T15:04:05Z07:00"
|
|
parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-dd'T'HH:mm:ss'Z'zzz",
|
|
"2006-01-12T22:04:05Z", 11)
|
|
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
|
|
parseTest("2006-01-12T15:04:05.999999999Z-07:00",
|
|
"yyyy-MM-dd'T'HH:mm:ss'.999999999Z'zzz", "2006-01-12T22:04:05Z", 11)
|
|
for tzFormat in ["z", "zz", "zzz"]:
|
|
# formatting timezone as 'Z' for UTC
|
|
parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat,
|
|
"2001-01-12T22:04:05Z", 11)
|
|
# timezone offset formats
|
|
parseTest("2001-01-12T15:04:05 +7", "yyyy-MM-dd'T'HH:mm:ss z",
|
|
"2001-01-12T08:04:05Z", 11)
|
|
parseTest("2001-01-12T15:04:05 +07", "yyyy-MM-dd'T'HH:mm:ss zz",
|
|
"2001-01-12T08:04:05Z", 11)
|
|
parseTest("2001-01-12T15:04:05 +07:00", "yyyy-MM-dd'T'HH:mm:ss zzz",
|
|
"2001-01-12T08:04:05Z", 11)
|
|
parseTest("2001-01-12T15:04:05 +07:30:59", "yyyy-MM-dd'T'HH:mm:ss zzzz",
|
|
"2001-01-12T07:33:06Z", 11)
|
|
# Kitchen = "3:04PM"
|
|
parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00")
|
|
|
|
# Bug with parse not setting DST properly if the current local DST differs from
|
|
# the date being parsed. Need to test parse dates both in and out of DST. We
|
|
# are testing that be relying on the fact that tranforming a TimeInfo to a Time
|
|
# and back again will correctly set the DST value. With the incorrect parse
|
|
# behavior this will introduce a one hour offset from the named time and the
|
|
# parsed time if the DST value differs between the current time and the date we
|
|
# are parsing.
|
|
let dstT1 = parse("2016-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss")
|
|
let dstT2 = parse("2016-06-01 00:00:00", "yyyy-MM-dd HH:mm:ss")
|
|
check dstT1 == toTime(dstT1).local
|
|
check dstT2 == toTime(dstT2).local
|
|
|
|
block dstTest:
|
|
# parsing will set isDST in relation to the local time. We take a date in
|
|
# January and one in July to maximize the probability to hit one date with DST
|
|
# and one without on the local machine. However, this is not guaranteed.
|
|
let
|
|
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")
|
|
check toTime(parsedJan).toUnix == 1451962800
|
|
check toTime(parsedJul).toUnix == 1467342000
|
|
|
|
template usingTimezone(tz: string, body: untyped) =
|
|
when defined(linux) or defined(macosx):
|
|
let oldZone = getEnv("TZ")
|
|
putEnv("TZ", tz)
|
|
body
|
|
putEnv("TZ", oldZone)
|
|
|
|
suite "ttimes":
|
|
|
|
# Generate tests for multiple timezone files where available
|
|
# Set the TZ env var for each test
|
|
when defined(linux) or defined(macosx):
|
|
let tz_dir = getEnv("TZDIR", "/usr/share/zoneinfo")
|
|
const f = "yyyy-MM-dd HH:mm zzz"
|
|
|
|
var tz_cnt = 0
|
|
for timezone in walkFiles(tz_dir & "/**/*"):
|
|
if symlinkExists(timezone) or timezone.endsWith(".tab") or
|
|
timezone.endsWith(".list"):
|
|
continue
|
|
|
|
usingTimezone(timezone):
|
|
test "test for " & timezone:
|
|
tz_cnt.inc
|
|
runTimezoneTests()
|
|
|
|
test "enough timezone files tested":
|
|
check tz_cnt > 10
|
|
|
|
else:
|
|
# not on Linux or macosx: run in the local timezone only
|
|
test "parseTest":
|
|
runTimezoneTests()
|
|
|
|
test "dst handling":
|
|
usingTimezone("Europe/Stockholm"):
|
|
# In case of an impossible time, the time is moved to after the
|
|
# impossible time period
|
|
check initDateTime(26, mMar, 2017, 02, 30, 00).format(f) ==
|
|
"2017-03-26 03:30 +02:00"
|
|
# In case of an ambiguous time, the earlier time is choosen
|
|
check initDateTime(29, mOct, 2017, 02, 00, 00).format(f) ==
|
|
"2017-10-29 02:00 +02:00"
|
|
# These are just dates on either side of the dst switch
|
|
check initDateTime(29, mOct, 2017, 01, 00, 00).format(f) ==
|
|
"2017-10-29 01:00 +02:00"
|
|
check initDateTime(29, mOct, 2017, 01, 00, 00).isDst
|
|
check initDateTime(29, mOct, 2017, 03, 01, 00).format(f) ==
|
|
"2017-10-29 03:01 +01:00"
|
|
check (not initDateTime(29, mOct, 2017, 03, 01, 00).isDst)
|
|
|
|
check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) ==
|
|
"2017-10-21 01:00 +02:00"
|
|
|
|
test "issue #6520":
|
|
usingTimezone("Europe/Stockholm"):
|
|
var local = fromUnix(1469275200).local
|
|
var utc = fromUnix(1469275200).utc
|
|
|
|
let claimedOffset = initDuration(seconds = local.utcOffset)
|
|
local.utcOffset = 0
|
|
check claimedOffset == utc.toTime - local.toTime
|
|
|
|
test "issue #5704":
|
|
usingTimezone("Asia/Seoul"):
|
|
let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime -
|
|
parse("19000101-000000", "yyyyMMdd-hhmmss").toTime
|
|
check diff == initDuration(seconds = 2208986872)
|
|
|
|
test "issue #6465":
|
|
usingTimezone("Europe/Stockholm"):
|
|
let dt = parse("2017-03-25 12:00", "yyyy-MM-dd hh:mm")
|
|
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 "adding/subtracting time across dst":
|
|
usingTimezone("Europe/Stockholm"):
|
|
let dt1 = initDateTime(26, mMar, 2017, 03, 00, 00)
|
|
check $(dt1 - 1.seconds) == "2017-03-26T01:59:59+01:00"
|
|
|
|
var dt2 = initDateTime(29, mOct, 2017, 02, 59, 59)
|
|
check $(dt2 + 1.seconds) == "2017-10-29T02:00:00+01:00"
|
|
|
|
test "datetime before epoch":
|
|
check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z"
|
|
|
|
test "incorrect inputs: empty string":
|
|
parseTestExcp("", "yyyy-MM-dd")
|
|
|
|
test "incorrect inputs: year":
|
|
parseTestExcp("20-02-19", "yyyy-MM-dd")
|
|
|
|
test "incorrect inputs: month number":
|
|
parseTestExcp("2018-2-19", "yyyy-MM-dd")
|
|
|
|
test "incorrect inputs: month name":
|
|
parseTestExcp("2018-Fe", "yyyy-MMM-dd")
|
|
|
|
test "incorrect inputs: day":
|
|
parseTestExcp("2018-02-1", "yyyy-MM-dd")
|
|
|
|
test "incorrect inputs: day of week":
|
|
parseTestExcp("2018-Feb-Mo", "yyyy-MMM-ddd")
|
|
|
|
test "incorrect inputs: hour":
|
|
parseTestExcp("2018-02-19 1:30", "yyyy-MM-dd hh:mm")
|
|
|
|
test "incorrect inputs: minute":
|
|
parseTestExcp("2018-02-19 16:3", "yyyy-MM-dd hh:mm")
|
|
|
|
test "incorrect inputs: second":
|
|
parseTestExcp("2018-02-19 16:30:0", "yyyy-MM-dd hh:mm:ss")
|
|
|
|
test "incorrect inputs: timezone (z)":
|
|
parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss z")
|
|
|
|
test "incorrect inputs: timezone (zz) 1":
|
|
parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zz")
|
|
|
|
test "incorrect inputs: timezone (zz) 2":
|
|
parseTestExcp("2018-02-19 16:30:00 +1", "yyyy-MM-dd hh:mm:ss zz")
|
|
|
|
test "incorrect inputs: timezone (zzz) 1":
|
|
parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zzz")
|
|
|
|
test "incorrect inputs: timezone (zzz) 2":
|
|
parseTestExcp("2018-02-19 16:30:00 +01:", "yyyy-MM-dd hh:mm:ss zzz")
|
|
|
|
test "incorrect inputs: timezone (zzz) 3":
|
|
parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz")
|
|
|
|
test "incorrect inputs: year (yyyy/uuuu)":
|
|
parseTestExcp("-0001", "yyyy")
|
|
parseTestExcp("-0001", "YYYY")
|
|
parseTestExcp("1", "yyyy")
|
|
parseTestExcp("12345", "yyyy")
|
|
parseTestExcp("1", "uuuu")
|
|
parseTestExcp("12345", "uuuu")
|
|
parseTestExcp("-1 BC", "UUUU g")
|
|
|
|
test "incorrect inputs: invalid sign":
|
|
parseTestExcp("+1", "YYYY")
|
|
parseTestExcp("+1", "dd")
|
|
parseTestExcp("+1", "MM")
|
|
parseTestExcp("+1", "hh")
|
|
parseTestExcp("+1", "mm")
|
|
parseTestExcp("+1", "ss")
|
|
|
|
test "_ as a separator":
|
|
discard parse("2000_01_01", "YYYY'_'MM'_'dd")
|
|
|
|
test "dynamic timezone":
|
|
let tz = staticTz(seconds = -9000)
|
|
let dt = initDateTime(1, mJan, 2000, 12, 00, 00, tz)
|
|
check dt.utcOffset == -9000
|
|
check dt.isDst == false
|
|
check $dt == "2000-01-01T12:00:00+02:30"
|
|
check $dt.utc == "2000-01-01T09:30:00Z"
|
|
check $dt.utc.inZone(tz) == $dt
|
|
|
|
test "isLeapYear":
|
|
check isLeapYear(2016)
|
|
check (not isLeapYear(2015))
|
|
check isLeapYear(2000)
|
|
check (not isLeapYear(1900))
|
|
|
|
test "TimeInterval":
|
|
let t = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997
|
|
# Interval tests
|
|
let t2 = t - 2.years
|
|
check t2.year == 1995
|
|
let t3 = (t - 7.years - 34.minutes - 24.seconds)
|
|
check t3.year == 1990
|
|
check t3.minute == 24
|
|
check t3.second == 10
|
|
check (t + 1.hours).toTime.toUnix == t.toTime.toUnix + 60 * 60
|
|
check (t - 1.hours).toTime.toUnix == t.toTime.toUnix - 60 * 60
|
|
|
|
test "TimeInterval - months":
|
|
var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc())
|
|
check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00Z"
|
|
dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc())
|
|
check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00Z"
|
|
dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc())
|
|
# This happens due to monthday overflow. It's consistent with Phobos.
|
|
check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00Z"
|
|
|
|
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)
|
|
check initDuration(seconds = 1, nanoseconds = 2) <=
|
|
initDuration(seconds = 1, nanoseconds = 3)
|
|
check (initDuration(seconds = 1, nanoseconds = 3) <=
|
|
initDuration(seconds = 1, nanoseconds = 1)).not
|
|
|
|
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:01Z"
|
|
|
|
test "compare datetimes":
|
|
var dt1 = now()
|
|
var dt2 = dt1
|
|
check dt1 == dt2
|
|
check dt1 <= dt2
|
|
dt2 = dt2 + 1.seconds
|
|
check dt1 < dt2
|
|
|
|
test "adding/subtracting TimeInterval":
|
|
# add/subtract TimeIntervals and Time/TimeInfo
|
|
let now = getTime().utc
|
|
check now + convert(Seconds, Nanoseconds, 1).nanoseconds == now + 1.seconds
|
|
check now + 1.weeks == now + 7.days
|
|
check now - 1.seconds == now - 3.seconds + 2.seconds
|
|
check now + 65.seconds == now + 1.minutes + 5.seconds
|
|
check now + 60.minutes == now + 1.hours
|
|
check now + 24.hours == now + 1.days
|
|
check now + 13.months == now + 1.years + 1.months
|
|
check toUnix(fromUnix(0) + 2.seconds) == 2
|
|
check toUnix(fromUnix(0) - 2.seconds) == -2
|
|
var ti1 = now + 1.years
|
|
ti1 = ti1 - 1.years
|
|
check ti1 == now
|
|
ti1 = ti1 + 1.days
|
|
check ti1 == now + 1.days
|
|
|
|
# Bug with adding a day to a Time
|
|
let day = 24.hours
|
|
let tomorrow = now + day
|
|
check tomorrow - now == initDuration(days = 1)
|
|
|
|
# Disabled for JS because it fails due to precision errors
|
|
# (The JS target uses float64 for int64).
|
|
when not defined(js):
|
|
test "fromWinTime/toWinTime":
|
|
check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0
|
|
check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100
|
|
check (-1).fromWinTime.toWinTime == -1
|
|
# One nanosecond is discarded due to differences in time resolution
|
|
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
|
|
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
|
|
|
|
test "issue 7620":
|
|
let layout = "M/d/yyyy' 'h:mm:ss' 'tt' 'z"
|
|
let t7620_am = parse("4/15/2017 12:01:02 AM +0", layout, utc())
|
|
check t7620_am.format(layout) == "4/15/2017 12:01:02 AM Z"
|
|
let t7620_pm = parse("4/15/2017 12:01:02 PM +0", layout, utc())
|
|
check t7620_pm.format(layout) == "4/15/2017 12:01:02 PM Z"
|
|
|
|
test "format":
|
|
var dt = initDateTime(1, mJan, -0001,
|
|
17, 01, 02, 123_456_789,
|
|
staticTz(hours = 1, minutes = 2, seconds = 3))
|
|
check dt.format("d") == "1"
|
|
check dt.format("dd") == "01"
|
|
check dt.format("ddd") == "Fri"
|
|
check dt.format("dddd") == "Friday"
|
|
check dt.format("h") == "5"
|
|
check dt.format("hh") == "05"
|
|
check dt.format("H") == "17"
|
|
check dt.format("HH") == "17"
|
|
check dt.format("m") == "1"
|
|
check dt.format("mm") == "01"
|
|
check dt.format("M") == "1"
|
|
check dt.format("MM") == "01"
|
|
check dt.format("MMM") == "Jan"
|
|
check dt.format("MMMM") == "January"
|
|
check dt.format("s") == "2"
|
|
check dt.format("ss") == "02"
|
|
check dt.format("t") == "P"
|
|
check dt.format("tt") == "PM"
|
|
check dt.format("yy") == "02"
|
|
check dt.format("yyyy") == "0002"
|
|
check dt.format("YYYY") == "2"
|
|
check dt.format("uuuu") == "-0001"
|
|
check dt.format("UUUU") == "-1"
|
|
check dt.format("z") == "-1"
|
|
check dt.format("zz") == "-01"
|
|
check dt.format("zzz") == "-01:02"
|
|
check dt.format("zzzz") == "-01:02:03"
|
|
check dt.format("g") == "BC"
|
|
|
|
check dt.format("fff") == "123"
|
|
check dt.format("ffffff") == "123456"
|
|
check dt.format("fffffffff") == "123456789"
|
|
dt.nanosecond = 1
|
|
check dt.format("fff") == "000"
|
|
check dt.format("ffffff") == "000000"
|
|
check dt.format("fffffffff") == "000000001"
|
|
|
|
dt.year = 12345
|
|
check dt.format("yyyy") == "+12345"
|
|
check dt.format("uuuu") == "+12345"
|
|
dt.year = -12345
|
|
check dt.format("yyyy") == "+12346"
|
|
check dt.format("uuuu") == "-12345"
|
|
|
|
expect ValueError:
|
|
discard initTimeFormat("'")
|
|
|
|
expect ValueError:
|
|
discard initTimeFormat("'foo")
|
|
|
|
expect ValueError:
|
|
discard initTimeFormat("foo'")
|
|
|
|
for tz in [
|
|
(staticTz(seconds = 0), "+0", "+00", "+00:00"), # UTC
|
|
(staticTz(seconds = -3600), "+1", "+01", "+01:00"), # CET
|
|
(staticTz(seconds = -39600), "+11", "+11", "+11:00"), # two digits
|
|
(staticTz(seconds = -1800), "+0", "+00", "+00:30"), # half an hour
|
|
(staticTz(seconds = 7200), "-2", "-02", "-02:00"), # positive
|
|
(staticTz(seconds = 38700), "-10", "-10", "-10:45")]: # positive with three quaters hour
|
|
let dt = initDateTime(1, mJan, 2000, 00, 00, 00, tz[0])
|
|
doAssert dt.format("z") == tz[1]
|
|
doAssert dt.format("zz") == tz[2]
|
|
doAssert dt.format("zzz") == tz[3]
|
|
|
|
test "format locale":
|
|
let loc = DateTimeLocale(
|
|
MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"],
|
|
MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"],
|
|
ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."],
|
|
dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"],
|
|
)
|
|
var dt = initDateTime(5, mJan, 2010, 17, 01, 02, utc())
|
|
check dt.format("d", loc) == "5"
|
|
check dt.format("dd", loc) == "05"
|
|
check dt.format("ddd", loc) == "Ora."
|
|
check dt.format("dddd", loc) == "Orange"
|
|
check dt.format("M", loc) == "1"
|
|
check dt.format("MM", loc) == "01"
|
|
check dt.format("MMM", loc) == "Fir"
|
|
check dt.format("MMMM", loc) == "Firsty"
|
|
|
|
test "parse":
|
|
check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z"
|
|
parseTestExcp("+120180101", "yyyyMMdd")
|
|
|
|
check parse("1", "YYYY", utc()).year == 1
|
|
check parse("1 BC", "YYYY g", utc()).year == 0
|
|
check parse("0001 BC", "yyyy g", utc()).year == 0
|
|
check parse("+12345 BC", "yyyy g", utc()).year == -12344
|
|
check parse("1 AD", "YYYY g", utc()).year == 1
|
|
check parse("0001 AD", "yyyy g", utc()).year == 1
|
|
check parse("+12345 AD", "yyyy g", utc()).year == 12345
|
|
|
|
check parse("-1", "UUUU", utc()).year == -1
|
|
check parse("-0001", "uuuu", utc()).year == -1
|
|
|
|
discard parse("foobar", "'foobar'")
|
|
discard parse("foo'bar", "'foo''''bar'")
|
|
discard parse("'", "''")
|
|
|
|
parseTestExcp("2000 A", "yyyy g")
|
|
|
|
test "parse locale":
|
|
let loc = DateTimeLocale(
|
|
MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"],
|
|
MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"],
|
|
ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."],
|
|
dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"],
|
|
)
|
|
check $parse("02 Fir 2019", "dd MMM yyyy", utc(), loc) == "2019-01-02T00:00:00Z"
|
|
check $parse("Fourthy 6, 2017", "MMMM d, yyyy", utc(), loc) == "2017-04-06T00:00:00Z"
|
|
|
|
test "countLeapYears":
|
|
# 1920, 2004 and 2020 are leap years, and should be counted starting at the following year
|
|
check countLeapYears(1920) + 1 == countLeapYears(1921)
|
|
check countLeapYears(2004) + 1 == countLeapYears(2005)
|
|
check countLeapYears(2020) + 1 == countLeapYears(2021)
|
|
|
|
test "timezoneConversion":
|
|
var l = now()
|
|
let u = l.utc
|
|
l = u.local
|
|
|
|
check l.timezone == local()
|
|
check u.timezone == utc()
|
|
|
|
test "getDayOfWeek":
|
|
check getDayOfWeek(01, mJan, 0000) == dSat
|
|
check getDayOfWeek(01, mJan, -0023) == dSat
|
|
check getDayOfWeek(21, mSep, 1900) == dFri
|
|
check getDayOfWeek(01, mJan, 1970) == dThu
|
|
check getDayOfWeek(21, mSep, 1970) == dMon
|
|
check getDayOfWeek(01, mJan, 2000) == dSat
|
|
check getDayOfWeek(01, mJan, 2021) == dFri
|
|
|
|
test "between - simple":
|
|
let x = initDateTime(10, mJan, 2018, 13, 00, 00)
|
|
let y = initDateTime(11, mJan, 2018, 12, 00, 00)
|
|
doAssert x + between(x, y) == y
|
|
|
|
test "between - dst start":
|
|
usingTimezone("Europe/Stockholm"):
|
|
let x = initDateTime(25, mMar, 2018, 00, 00, 00)
|
|
let y = initDateTime(25, mMar, 2018, 04, 00, 00)
|
|
doAssert x + between(x, y) == y
|
|
|
|
test "between - empty interval":
|
|
let x = now()
|
|
let y = x
|
|
doAssert x + between(x, y) == y
|
|
|
|
test "between - dst end":
|
|
usingTimezone("Europe/Stockholm"):
|
|
let x = initDateTime(27, mOct, 2018, 02, 00, 00)
|
|
let y = initDateTime(28, mOct, 2018, 01, 00, 00)
|
|
doAssert x + between(x, y) == y
|
|
|
|
test "between - long day":
|
|
usingTimezone("Europe/Stockholm"):
|
|
# This day is 25 hours long in Europe/Stockholm
|
|
let x = initDateTime(28, mOct, 2018, 00, 30, 00)
|
|
let y = initDateTime(29, mOct, 2018, 00, 00, 00)
|
|
doAssert between(x, y) == 24.hours + 30.minutes
|
|
doAssert x + between(x, y) == y
|
|
|
|
test "between - offset change edge case":
|
|
# This test case is important because in this case
|
|
# `x + between(x.utc, y.utc) == y` is not true, which is very rare.
|
|
usingTimezone("America/Belem"):
|
|
let x = initDateTime(24, mOct, 1987, 00, 00, 00)
|
|
let y = initDateTime(26, mOct, 1987, 23, 00, 00)
|
|
doAssert x + between(x, y) == y
|
|
doAssert y + between(y, x) == x
|
|
|
|
test "between - all units":
|
|
let x = initDateTime(1, mJan, 2000, 00, 00, 00, utc())
|
|
let ti = initTimeInterval(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
|
|
let y = x + ti
|
|
doAssert between(x, y) == ti
|
|
doAssert between(y, x) == -ti
|
|
|
|
test "between - monthday overflow":
|
|
let x = initDateTime(31, mJan, 2001, 00, 00, 00, utc())
|
|
let y = initDateTime(1, mMar, 2001, 00, 00, 00, utc())
|
|
doAssert x + between(x, y) == y
|
|
|
|
test "between - misc":
|
|
block:
|
|
let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc())
|
|
let y = initDateTime(01, mJan, 2001, 00, 00, 00, utc())
|
|
doAssert between(x, y) == 12.hours
|
|
|
|
block:
|
|
let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc())
|
|
let y = initDateTime(02, mJan, 2001, 00, 00, 00, utc())
|
|
doAssert between(x, y) == 1.days + 12.hours
|
|
|
|
block:
|
|
let x = initDateTime(31, mDec, 1995, 00, 00, 00, utc())
|
|
let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc())
|
|
doAssert x + between(x, y) == y
|
|
|
|
block:
|
|
let x = initDateTime(01, mDec, 1995, 00, 00, 00, utc())
|
|
let y = initDateTime(31, mJan, 2000, 00, 00, 00, utc())
|
|
doAssert x + between(x, y) == y
|
|
|
|
block:
|
|
let x = initDateTime(31, mJan, 2000, 00, 00, 00, utc())
|
|
let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc())
|
|
doAssert x + between(x, y) == y
|
|
|
|
block:
|
|
let x = initDateTime(01, mJan, 1995, 12, 00, 00, utc())
|
|
let y = initDateTime(01, mFeb, 1995, 00, 00, 00, utc())
|
|
doAssert between(x, y) == 4.weeks + 2.days + 12.hours
|
|
|
|
block:
|
|
let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc())
|
|
let y = initDateTime(10, mFeb, 1995, 00, 00, 00, utc())
|
|
doAssert x + between(x, y) == y
|
|
|
|
block:
|
|
let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc())
|
|
let y = initDateTime(10, mMar, 1995, 00, 00, 00, utc())
|
|
doAssert x + between(x, y) == y
|
|
doAssert between(x, y) == 1.months + 1.weeks
|
|
|
|
test "inX procs":
|
|
doAssert initDuration(seconds = 1).inSeconds == 1
|
|
doAssert initDuration(seconds = -1).inSeconds == -1
|
|
doAssert initDuration(seconds = -1, nanoseconds = 1).inSeconds == 0
|
|
doAssert initDuration(nanoseconds = -1).inSeconds == 0
|
|
doAssert initDuration(milliseconds = 500).inMilliseconds == 500
|
|
doAssert initDuration(milliseconds = -500).inMilliseconds == -500
|
|
doAssert initDuration(nanoseconds = -999999999).inMilliseconds == -999
|