diff options
author | data-man <datamanrb@gmail.com> | 2018-02-19 16:57:17 +0300 |
---|---|---|
committer | data-man <datamanrb@gmail.com> | 2018-02-19 16:57:17 +0300 |
commit | b30b9207510a62b09a8e77706219a829a73c1d78 (patch) | |
tree | 31b3e0d94699da551203baf8fd00f870322f81e4 | |
parent | 864467ade3e82c492645c3f3709613336365ba1c (diff) | |
download | Nim-b30b9207510a62b09a8e77706219a829a73c1d78.tar.gz |
Fix date parsing for a bad inputs
-rw-r--r-- | lib/pure/times.nim | 198 | ||||
-rw-r--r-- | tests/stdlib/ttimes.nim | 49 |
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 = |