diff options
author | Oscar NihlgÄrd <oscarnihlgard@gmail.com> | 2018-12-22 10:41:54 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-12-22 10:41:54 +0100 |
commit | 253936385143cb9d58f565dee0f1b5768accc81e (patch) | |
tree | 6287bac6a2f8b2cbd97072dbeff038fc8990a574 /lib/pure | |
parent | ca672ec62e8c12bb1ea03da2fa6f0113924d0dbd (diff) | |
download | Nim-253936385143cb9d58f565dee0f1b5768accc81e.tar.gz |
Don't use parseutils.parseInt in the times module (#10028)
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/times.nim | 106 |
1 files changed, 67 insertions, 39 deletions
diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 010551b5a..7afc3dade 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -122,12 +122,8 @@ only for years in the range 1..9999). ]## - -{.push debugger:off.} # the user does not want to trace a part - # of the standard library! - import - strutils, parseutils, algorithm, math, options, strformat + strutils, algorithm, math, options, strformat include "system/inclrtl" @@ -304,7 +300,6 @@ const secondsInMin = 60 secondsInHour = 60*60 secondsInDay = 60*60*24 - minutesInHour = 60 rateDiff = 10000000'i64 # 100 nsecs # The number of hectonanoseconds between 1601/01/01 (windows epoch) # and 1970/01/01 (unix epoch). @@ -1602,6 +1597,12 @@ type ## be encoded as ``@[Lit.byte, 3.byte, 'f'.byte, 'o'.byte, 'o'.byte]``. formatStr: string + TimeParseError* = object of ValueError ## \ + ## Raised when parsing input using a ``TimeFormat`` fails. + + TimeFormatParseError* = object of ValueError ## \ + ## Raised when parsing a ``TimeFormat`` string fails. + const FormatLiterals = { ' ', '-', '/', ':', '(', ')', '[', ']', ',' } proc `$`*(f: TimeFormat): string = @@ -1612,9 +1613,34 @@ proc `$`*(f: TimeFormat): string = f.formatStr proc raiseParseException(f: TimeFormat, input: string, msg: string) = - raise newException(ValueError, + raise newException(TimeParseError, &"Failed to parse '{input}' with format '{f}'. {msg}") +proc parseInt(s: string, b: var int, start = 0, maxLen = int.high, + allowSign = false): int = + var sign = -1 + var i = start + let stop = start + min(s.high - start + 1, maxLen) - 1 + if allowSign and i <= stop: + if s[i] == '+': + inc(i) + elif s[i] == '-': + inc(i) + sign = 1 + if i <= stop and s[i] in {'0'..'9'}: + b = 0 + while i <= stop and s[i] in {'0'..'9'}: + let c = ord(s[i]) - ord('0') + if b >= (low(int) + c) div 10: + b = b * 10 - c + else: + return 0 + inc(i) + if sign == -1 and b == low(int): + return 0 + b = b * sign + result = i - start + iterator tokens(f: string): tuple[kind: FormatTokenKind, token: string] = var i = 0 var currToken = "" @@ -1639,7 +1665,7 @@ iterator tokens(f: string): tuple[kind: FormatTokenKind, token: string] = i.inc if i > f.high: - raise newException(ValueError, + raise newException(TimeFormatParseError, &"Unclosed ' in time format string. " & "For a literal ', use ''.") i.inc @@ -1696,7 +1722,8 @@ proc stringToPattern(str: string): FormatPattern = of "zzz": result = zzz of "zzzz": result = zzzz of "g": result = g - else: raise newException(ValueError, &"'{str}' is not a valid pattern") + else: raise newException(TimeFormatParseError, + &"'{str}' is not a valid pattern") proc initTimeFormat*(format: string): TimeFormat = ## Construct a new time format for parsing & formatting time types. @@ -1715,7 +1742,7 @@ proc initTimeFormat*(format: string): TimeFormat = else: result.patterns.add(FormatPattern.Lit.byte) if token.len > 255: - raise newException(ValueError, + raise newException(TimeFormatParseError, "Format literal is to long:" & token) result.patterns.add(token.len.byte) for c in token: @@ -1833,15 +1860,10 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = proc parsePattern(input: string, pattern: FormatPattern, i: var int, parsed: var ParsedTime): bool = - template takeInt(allowedWidth: Slice[int]): int = + template takeInt(allowedWidth: Slice[int], allowSign = false): int = var sv: int - let max = i + allowedWidth.b - 1 - var pd = - if max > input.high: - parseInt(input, sv, i) - else: - parseInt(input[i..max], sv) - if pd notin allowedWidth: + var pd = parseInt(input, sv, i, allowedWidth.b, allowSign) + if pd < allowedWidth.a: return false i.inc pd sv @@ -1853,11 +1875,13 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, case pattern of d: - parsed.monthday = some(takeInt(1..2)) - result = parsed.monthday.get() in MonthdayRange + let monthday = takeInt(1..2) + parsed.monthday = some(monthday) + result = monthday in MonthdayRange of dd: - parsed.monthday = some(takeInt(2..2)) - result = parsed.monthday.get() in MonthdayRange + let monthday = takeInt(2..2) + parsed.monthday = some(monthday) + result = monthday in MonthdayRange of ddd: result = input.substr(i, i+2).toLowerAscii() in [ "sun", "mon", "tue", "wed", "thu", "fri", "sat"] @@ -1993,7 +2017,7 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, of yyyy: let year = if input[i] in { '+', '-' }: - takeInt(4..high(int)) + takeInt(4..high(int), allowSign = true) else: takeInt(4..4) result = year > 0 @@ -2005,12 +2029,12 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, of uuuu: let year = if input[i] in { '+', '-' }: - takeInt(4..high(int)) + takeInt(4..high(int), allowSign = true) else: takeInt(4..4) parsed.year = some(year) of UUUU: - parsed.year = some(takeInt(1..high(int))) + parsed.year = some(takeInt(1..high(int), allowSign = true)) of z, zz, zzz, zzzz: case input[i] of '+', '-': @@ -2055,9 +2079,8 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, else: result = false of y, yyy, yyyyy: - raise newException(ValueError, - &"The pattern '{pattern}' is only valid for formatting") - of Lit: assert false # Can't happen + raiseAssert "Pattern is invalid for parsing: " & $pattern + of Lit: doAssert false, "Can't happen" proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat, input: string): DateTime = @@ -2147,7 +2170,8 @@ proc format*(dt: DateTime, f: TimeFormat): string {.raises: [].} = formatPattern(dt, f.patterns[idx].FormatPattern, result = result) idx.inc -proc format*(dt: DateTime, f: string): string = +proc format*(dt: DateTime, f: string): string + {.raises: [TimeFormatParseError].} = ## Shorthand for constructing a ``TimeFormat`` and using it to format ``dt``. ## ## See `Parsing and formatting dates`_ for documentation of the @@ -2163,7 +2187,8 @@ proc format*(dt: DateTime, f: static[string]): string {.raises: [].} = const f2 = initTimeFormat(f) result = dt.format(f2) -proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} = +proc format*(time: Time, f: string, zone: Timezone = local()): string + {.raises: [TimeFormatParseError].} = ## Shorthand for constructing a ``TimeFormat`` and using it to format ## ``time``. Will use the timezone specified by ``zone``. ## @@ -2175,13 +2200,14 @@ proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [] doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00" time.inZone(zone).format(f) -proc format*(time: Time, f: static[string], - zone: Timezone = local()): string {.tags: [].} = +proc format*(time: Time, f: static[string], zone: Timezone = local()): string + {.raises: [].} = ## Overload that validates ``f`` at compile time. const f2 = initTimeFormat(f) result = time.inZone(zone).format(f2) -proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime = +proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime + {.raises: [TimeParseError, Defect].} = ## Parses ``input`` as a ``DateTime`` using the format specified by ``f``. ## If no UTC offset was parsed, then ``input`` is assumed to be specified in ## the ``zone`` timezone. If a UTC offset was parsed, the result will be @@ -2221,7 +2247,8 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime = result = toDateTime(parsed, zone, f, input) -proc parse*(input, f: string, tz: Timezone = local()): DateTime = +proc parse*(input, f: string, tz: Timezone = local()): DateTime + {.raises: [TimeParseError, TimeFormatParseError, Defect].} = ## Shorthand for constructing a ``TimeFormat`` and using it to parse ## ``input`` as a ``DateTime``. ## @@ -2233,12 +2260,14 @@ proc parse*(input, f: string, tz: Timezone = local()): DateTime = let dtFormat = initTimeFormat(f) result = input.parse(dtFormat, tz) -proc parse*(input: string, f: static[string], zone: Timezone = local()): DateTime = +proc parse*(input: string, f: static[string], zone: Timezone = local()): DateTime + {.raises: [TimeParseError, Defect].} = ## Overload that validates ``f`` at compile time. const f2 = initTimeFormat(f) result = input.parse(f2, zone) -proc parseTime*(input, f: string, zone: Timezone): Time = +proc parseTime*(input, f: string, zone: Timezone): Time + {.raises: [TimeParseError, TimeFormatParseError, Defect].} = ## Shorthand for constructing a ``TimeFormat`` and using it to parse ## ``input`` as a ``DateTime``, then converting it a ``Time``. ## @@ -2249,7 +2278,8 @@ proc parseTime*(input, f: string, zone: Timezone): Time = doAssert parseTime(tStr, "yyyy-MM-dd'T'HH:mm:sszzz", utc()) == fromUnix(0) parse(input, f, zone).toTime() -proc parseTime*(input: string, f: static[string], zone: Timezone): Time = +proc parseTime*(input: string, f: static[string], zone: Timezone): Time + {.raises: [TimeParseError, Defect].} = ## Overload that validates ``format`` at compile time. const f2 = initTimeFormat(f) result = input.parse(f2, zone).toTime() @@ -2275,8 +2305,6 @@ proc `$`*(time: Time): string {.tags: [], raises: [], benign.} = doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz") $time.local -{.pop.} - proc countLeapYears*(yearSpan: int): int = ## Returns the number of leap years spanned by a given number of years. ## |