summary refs log tree commit diff stats
path: root/tests/stdlib
diff options
context:
space:
mode:
authorOscar NihlgÄrd <oscarnihlgard@gmail.com>2018-07-09 20:04:25 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-07-09 20:04:25 +0200
commit3b310e91cd592b76cd34678ae394c1af2c3808f3 (patch)
tree1ff39481058d79f1e2aa2dbc4ef4fd4fcb95dbbc /tests/stdlib
parentc6671776a16127be30a627d1672fee9897a2320f (diff)
downloadNim-3b310e91cd592b76cd34678ae394c1af2c3808f3.tar.gz
New implementations of times.parse & times.format (#8094)
Diffstat (limited to 'tests/stdlib')
-rw-r--r--tests/stdlib/ttimes.nim210
1 files changed, 149 insertions, 61 deletions
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index 4ab3ba581..e3f61ff77 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -8,6 +8,24 @@ discard """
 import
   times, os, strutils, unittest
 
+proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} =
+  let offset = hours * 3600 + minutes * 60 + seconds
+
+  proc zoneInfoFromTz(adjTime: Time): ZonedTime {.locks: 0.} =
+    result.isDst = false
+    result.utcOffset = offset
+    result.adjTime = adjTime
+
+  proc zoneInfoFromUtc(time: Time): ZonedTime {.locks: 0.}=
+    result.isDst = false
+    result.utcOffset = offset
+    result.adjTime = fromUnix(time.toUnix - offset)
+
+  result.name = ""
+  result.zoneInfoFromTz = zoneInfoFromTz
+  result.zoneInfoFromUtc = zoneInfoFromUtc
+
+
 # $ date --date='@2147483647'
 # Tue 19 Jan 03:14:07 GMT 2038
 
@@ -19,25 +37,10 @@ proc checkFormat(t: DateTime, format, expected: string) =
     echo "actual  : ", actual
     doAssert false
 
-let t = fromUnix(2147483647).utc
-t.checkFormat("ddd dd MMM hh:mm:ss yyyy", "Tue 19 Jan 03:14:07 2038")
-t.checkFormat("ddd ddMMMhh:mm:ssyyyy", "Tue 19Jan03:14:072038")
-t.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
-  " ss t tt y yy yyy yyyy yyyyy z zz zzz",
-  "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 +0 +00 +00:00")
-
-t.checkFormat("yyyyMMddhhmmss", "20380119031407")
-
-# issue 7620
-let t7620_am = parse("4/15/2017 12:01:02 AM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
-t7620_am.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 AM +0")
-let t7620_pm = parse("4/15/2017 12:01:02 PM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
-t7620_pm.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 PM +0")
-
-let t2 = fromUnix(160070789).utc # Mon 27 Jan 16:06:29 GMT 1975
+let t2 = fromUnix(160070789).utc() # Mon 27 Jan 16:06:29 GMT 1975
 t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
   " ss t tt y yy yyy yyyy yyyyy z zz zzz",
-  "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 +0 +00 +00:00")
+  "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 Z Z Z")
 
 var t4 = fromUnix(876124714).utc # Mon  6 Oct 08:58:34 BST 1997
 t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
@@ -83,16 +86,16 @@ let seqB: seq[Time] = @[]
 doAssert seqA == seqB
 
 for tz in [
-    (0, "+0", "+00", "+00:00"), # UTC
-    (-3600, "+1", "+01", "+01:00"), # CET
-    (-39600, "+11", "+11", "+11:00"), # two digits
-    (-1800, "+0", "+00", "+00:30"), # half an hour
-    (7200, "-2", "-02", "-02:00"), # positive
-    (38700, "-10", "-10", "-10:45")]: # positive with three quaters hour
-  let ti = DateTime(month: mJan, monthday: 1, utcOffset: tz[0])
-  doAssert ti.format("z") == tz[1]
-  doAssert ti.format("zz") == tz[2]
-  doAssert ti.format("zzz") == tz[3]
+    (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]
 
 block countLeapYears:
   # 1920, 2004 and 2020 are leap years, and should be counted starting at the following year
@@ -112,11 +115,9 @@ template parseTest(s, f, sExpected: string, ydExpected: int) =
   let
     parsed = s.parse(f, utc())
     parsedStr = $parsed
+  if parsedStr != sExpected:
+    echo "GOT ", parsedStr, " EXPECTED ", sExpected, " FORMAT ", f
   check parsedStr == sExpected
-  if parsed.yearday != ydExpected:
-    echo s
-    echo parsed.repr
-    echo parsed.yearday, " exp: ", ydExpected
   check(parsed.yearday == ydExpected)
 
 template parseTestExcp(s, f: string) =
@@ -130,51 +131,43 @@ template parseTestTimeOnly(s, f, sExpected: string) =
 # 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:00+00:00", 348)
+      "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:05+00:00", 11)
+      "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:05+00:00", 11)
+      "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:05+00:00", 59) # leap day
+      "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:00+00:00", 11)
+      "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:00+00:00", 60) # day after february in leap year
+      "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:05+00:00", 11)
+      "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:05+00:00", 59) # day after february in non-leap year
+      "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:05+00:00", 11)
+      "2006-01-12T22:04:05Z", 11)
   # RFC3339     = "2006-01-02T15:04:05Z07:00"
-  parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-ddTHH:mm:ssZzzz",
-      "2006-01-12T22:04:05+00:00", 11)
   parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-dd'T'HH:mm:ss'Z'zzz",
-      "2006-01-12T22:04:05+00:00", 11)
+      "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-ddTHH:mm:ss.999999999Zzzz", "2006-01-12T22:04:05+00:00", 11)
+      "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:05+00:00", 11)
+        "2001-01-12T22:04:05Z", 11)
   # Kitchen     = "3:04PM"
   parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00")
-  #when not defined(testing):
-  #  echo "Kitchen: " & $s.parse(f)
-  #  var ti = timeToTimeInfo(getTime())
-  #  echo "Todays date after decoding: ", ti
-  #  var tint = timeToTimeInterval(getTime())
-  #  echo "Todays date after decoding to interval: ", tint
 
   # 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
@@ -195,8 +188,8 @@ template runTimezoneTests() =
     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")
-    doAssert toTime(parsedJan).toUnix == 1451962800
-    doAssert toTime(parsedJul).toUnix == 1467342000
+    check toTime(parsedJan).toUnix == 1451962800
+    check toTime(parsedJul).toUnix == 1467342000
 
 suite "ttimes":
 
@@ -256,7 +249,7 @@ suite "ttimes":
       check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"      
 
     test "datetime before epoch":
-      check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52+00:00"
+      check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z"
 
     test "adding/subtracting time across dst":
       putenv("TZ", "Europe/Stockholm")
@@ -319,6 +312,15 @@ suite "ttimes":
   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 "dynamic timezone":
     proc staticOffset(offset: int): Timezone =
       proc zoneInfoFromTz(adjTime: Time): ZonedTime =
@@ -340,7 +342,7 @@ suite "ttimes":
     check dt.utcOffset == -9000
     check dt.isDst == false
     check $dt == "2000-01-01T12:00:00+02:30"
-    check $dt.utc == "2000-01-01T09:30:00+00:00"
+    check $dt.utc == "2000-01-01T09:30:00Z"
     check $dt.utc.inZone(tz) == $dt
 
   test "isLeapYear":
@@ -351,12 +353,12 @@ suite "ttimes":
 
   test "subtract months":
     var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc())
-    check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00+00:00"
+    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:00+00:00"
+    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:00+00:00"
+    check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00Z"
 
   test "duration":
     let d = initDuration
@@ -384,11 +386,11 @@ suite "ttimes":
     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()) +
+    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"
+    check $dt2 == "0001-01-01T12:00:01Z"
 
   test "compare datetimes":
     var dt1 = now()
@@ -426,4 +428,90 @@ suite "ttimes":
     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 
\ No newline at end of file
+    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'")
+
+  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")