summary refs log tree commit diff stats
diff options
context:
space:
mode:
authordata-man <datamanrb@gmail.com>2018-02-19 16:57:17 +0300
committerdata-man <datamanrb@gmail.com>2018-02-19 16:57:17 +0300
commitb30b9207510a62b09a8e77706219a829a73c1d78 (patch)
tree31b3e0d94699da551203baf8fd00f870322f81e4
parent864467ade3e82c492645c3f3709613336365ba1c (diff)
downloadNim-b30b9207510a62b09a8e77706219a829a73c1d78.tar.gz
Fix date parsing for a bad inputs
-rw-r--r--lib/pure/times.nim198
-rw-r--r--tests/stdlib/ttimes.nim49
2 files changed, 188 insertions, 59 deletions
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 50bf8f7f2..b571cd51c 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -936,24 +936,36 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
   var sv: int
   case token
   of "d":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.monthday = sv
-    j += pd
+    if value.len >= j+1:
+      var pd = parseInt(value[j..j+1], sv)
+      dt.monthday = sv
+      j += pd
+    else:
+      raise newException(ValueError,
+        "Couldn't parse day of month (d), got: " & value)
   of "dd":
-    dt.monthday = value[j..j+1].parseInt()
-    j += 2
-  of "ddd":
-    case value[j..j+2].toLowerAscii()
-    of "sun": dt.weekday = dSun
-    of "mon": dt.weekday = dMon
-    of "tue": dt.weekday = dTue
-    of "wed": dt.weekday = dWed
-    of "thu": dt.weekday = dThu
-    of "fri": dt.weekday = dFri
-    of "sat": dt.weekday = dSat
+    if value.len >= j+1:
+      dt.monthday = value[j..j+1].parseInt()
     else:
       raise newException(ValueError,
+        "Couldn't parse day of month (dd), got: " & value)
+    j += 2
+  of "ddd":
+    if value.len >= j+2:
+      case value[j..j+2].toLowerAscii()
+      of "sun": dt.weekday = dSun
+      of "mon": dt.weekday = dMon
+      of "tue": dt.weekday = dTue
+      of "wed": dt.weekday = dWed
+      of "thu": dt.weekday = dThu
+      of "fri": dt.weekday = dFri
+      of "sat": dt.weekday = dSat
+      else:
+        raise newException(ValueError,
         "Couldn't parse day of week (ddd), got: " & value[j..j+2])
+    else:
+        raise newException(ValueError,
+        "Couldn't parse day of week (ddd), got: " & value)
     j += 3
   of "dddd":
     if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0:
@@ -981,41 +993,69 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
       raise newException(ValueError,
         "Couldn't parse day of week (dddd), got: " & value)
   of "h", "H":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.hour = sv
-    j += pd
+    if value.len >= j+1:
+      var pd = parseInt(value[j..j+1], sv)
+      dt.hour = sv
+      j += pd
+    else:
+      raise newException(ValueError,
+        "Couldn't parse hour (h), got: " & value)
   of "hh", "HH":
-    dt.hour = value[j..j+1].parseInt()
+    if value.len >= j+1:
+      dt.hour = value[j..j+1].parseInt()
+    else:
+      raise newException(ValueError,
+        "Couldn't parse hour (hh), got: " & value)
     j += 2
   of "m":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.minute = sv
-    j += pd
+    if value.len >= j+1:
+      var pd = parseInt(value[j..j+1], sv)
+      dt.minute = sv
+      j += pd
+    else:
+      raise newException(ValueError,
+        "Couldn't parse minute (m), got: " & value)
   of "mm":
-    dt.minute = value[j..j+1].parseInt()
+    if value.len >= j+1:
+      dt.minute = value[j..j+1].parseInt()
+    else:
+      raise newException(ValueError,
+        "Couldn't parse minute (mm), got: " & value)
     j += 2
   of "M":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.month = sv.Month
-    j += pd
+    if value.len >= j+1:
+      var pd = parseInt(value[j..j+1], sv)
+      dt.month = sv.Month
+      j += pd
+    else:
+      raise newException(ValueError,
+        "Couldn't parse month (M), got: " & value)
   of "MM":
-    var month = value[j..j+1].parseInt()
-    j += 2
-    dt.month = month.Month
+    if value.len >= j+1:
+      var month = value[j..j+1].parseInt()
+      j += 2
+      dt.month = month.Month
+    else:
+      raise newException(ValueError,
+        "Couldn't parse month (MM), got: " & value)
   of "MMM":
-    case value[j..j+2].toLowerAscii():
-    of "jan": dt.month =  mJan
-    of "feb": dt.month =  mFeb
-    of "mar": dt.month =  mMar
-    of "apr": dt.month =  mApr
-    of "may": dt.month =  mMay
-    of "jun": dt.month =  mJun
-    of "jul": dt.month =  mJul
-    of "aug": dt.month =  mAug
-    of "sep": dt.month =  mSep
-    of "oct": dt.month =  mOct
-    of "nov": dt.month =  mNov
-    of "dec": dt.month =  mDec
+    if value.len >= j+2:
+      case value[j..j+2].toLowerAscii():
+      of "jan": dt.month =  mJan
+      of "feb": dt.month =  mFeb
+      of "mar": dt.month =  mMar
+      of "apr": dt.month =  mApr
+      of "may": dt.month =  mMay
+      of "jun": dt.month =  mJun
+      of "jul": dt.month =  mJul
+      of "aug": dt.month =  mAug
+      of "sep": dt.month =  mSep
+      of "oct": dt.month =  mOct
+      of "nov": dt.month =  mNov
+      of "dec": dt.month =  mDec
+      else:
+        raise newException(ValueError,
+          "Couldn't parse month (MMM), got: " & value)
     else:
       raise newException(ValueError,
         "Couldn't parse month (MMM), got: " & value)
@@ -1061,35 +1101,63 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
       raise newException(ValueError,
         "Couldn't parse month (MMMM), got: " & value)
   of "s":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.second = sv
-    j += pd
+    if value.len >= j+1:
+      var pd = parseInt(value[j..j+1], sv)
+      dt.second = sv
+      j += pd
+    else:
+      raise newException(ValueError,
+        "Couldn't parse second (s), got: " & value)
   of "ss":
-    dt.second = value[j..j+1].parseInt()
+    if value.len >= j+1:
+      dt.second = value[j..j+1].parseInt()
+    else:
+      raise newException(ValueError,
+        "Couldn't parse second (ss), got: " & value)
     j += 2
   of "t":
     if value[j] == 'P' and dt.hour > 0 and dt.hour < 12:
       dt.hour += 12
     j += 1
   of "tt":
-    if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12:
-      dt.hour += 12
+    if value.len >= j+1:
+      if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12:
+        dt.hour += 12
+    else:
+      raise newException(ValueError,
+        "Couldn't parse hour (tt), got: " & value)
     j += 2
   of "yy":
     # Assumes current century
-    var year = value[j..j+1].parseInt()
-    var thisCen = now().year div 100
-    dt.year = thisCen*100 + year
+    if value.len >= j+1:
+      var year = value[j..j+1].parseInt()
+      var thisCen = now().year div 100
+      dt.year = thisCen*100 + year
+    else:
+      raise newException(ValueError,
+        "Couldn't parse year (yy), got: " & value)
     j += 2
   of "yyyy":
-    dt.year = value[j..j+3].parseInt()
+    if value.len >= j+3:
+      dt.year = value[j..j+3].parseInt()
+    else:
+      raise newException(ValueError,
+        "Couldn't parse year (yyyy), got: " & value)
     j += 4
   of "z":
     dt.isDst = false
     if value[j] == '+':
-      dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
+      if value.len >= j+1:
+        dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
+      else:
+        raise newException(ValueError,
+          "Couldn't parse timezone offset (z), got: " & value)
     elif value[j] == '-':
-      dt.utcOffset = parseInt($value[j+1]) * secondsInHour
+      if value.len >= j+1:
+        dt.utcOffset = parseInt($value[j+1]) * secondsInHour
+      else:
+        raise newException(ValueError,
+          "Couldn't parse timezone offset (z), got: " & value)
     elif value[j] == 'Z':
       dt.utcOffset = 0
       j += 1
@@ -1101,9 +1169,17 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
   of "zz":
     dt.isDst = false
     if value[j] == '+':
-      dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
+      if value.len >= j+2:
+        dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
+      else:
+        raise newException(ValueError,
+          "Couldn't parse timezone offset (zz), got: " & value)
     elif value[j] == '-':
-      dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
+      if value.len >= j+2:
+        dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
+      else:
+        raise newException(ValueError,
+          "Couldn't parse timezone offset (zz), got: " & value)
     elif value[j] == 'Z':
       dt.utcOffset = 0
       j += 1
@@ -1124,10 +1200,14 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
     else:
       raise newException(ValueError,
         "Couldn't parse timezone offset (zzz), got: " & value[j])
-    dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
-    j += 4
-    dt.utcOffset += factor * value[j..j+1].parseInt() * 60
-    j += 2
+    if value.len >= j+5:
+      dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
+      j += 4
+      dt.utcOffset += factor * value[j..j+1].parseInt() * 60
+      j += 2
+    else:
+      raise newException(ValueError,
+        "Couldn't parse timezone offset (zzz), got: " & value)
   else:
     # Ignore the token and move forward in the value string by the same length
     j += token.len
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index 1f8ae6a22..ae056a79f 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -131,6 +131,10 @@ template parseTest(s, f, sExpected: string, ydExpected: int) =
     echo parsed.yearday, " exp: ", ydExpected
   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())
 
@@ -281,6 +285,51 @@ suite "ttimes":
     test "parseTest":
       runTimezoneTests()
 
+  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 "dynamic timezone":
     proc staticOffset(offset: int): Timezone =
       proc zoneInfoFromTz(adjTime: Time): ZonedTime =