Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions test/time.carp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
; render a Datetime as "YYYY-MM-DD HH:MM:SS" for whole-instant comparisons.
(defn stamp [dt] (fmt "%s %s" &(Datetime.isoformat dt) &(Datetime.isotime dt)))

; n UTF-8 continuation bytes, which decode to no character at all.
(defn conts [n] (String.from-bytes &(Array.replicate n &128b)))

; sweep range: two leap years, two 53-week ISO years (2020 and 2026)
(def SWEEP-FROM (Datetime.to-ordinal &(Datetime.date 2018 1 1)))
(def SWEEP-TO (Datetime.to-ordinal &(Datetime.date 2026 12 31)))
Expand Down Expand Up @@ -1361,6 +1364,69 @@
(Result.error? &(Datetime.strptime "2024-7" "%Y-%w"))
"strptime rejects %w outside 0-6")

(assert-true test
(Result.error? &(Datetime.strptime &(conts 6) "%d %b %Y"))
"strptime rejects continuation bytes for a numeric field")
(assert-true test
(Result.error? &(Datetime.strptime &(conts 20) "%Y-%m-%dT%H:%M:%S%z"))
"strptime rejects continuation bytes across every numeric field")
(assert-true test
(Result.error? &(Datetime.strptime &(conts 4) "%B"))
"strptime rejects continuation bytes for %B")
(assert-true test
(Result.error? &(Datetime.strptime &(conts 3) "%b"))
"strptime rejects continuation bytes for %b")
(assert-true test
(Result.error? &(Datetime.strptime &(conts 2) "%p"))
"strptime rejects continuation bytes for %p")
(assert-true test
(Result.error? &(Datetime.strptime &(conts 3) "%c"))
"strptime rejects continuation bytes for a compound specifier")
(assert-true test
(Result.error? &(Datetime.strptime &(conts 6) "%j"))
"strptime rejects continuation bytes for a ranged field")
(assert-true test
(Result.error? &(Datetime.strptime &(String.from-bytes &[226b 130b]) "%m"))
"strptime rejects input truncated mid-character")
(assert-true test
(Result.error? &(Datetime.strptime "Jané" "%B"))
"strptime rejects a month name running into a multibyte character")

(assert-equal test
&(Result.Success (Datetime.date 2024 3 15))
&(Datetime.strptime "«2024-03-15»" "«%Y-%m-%d»")
"strptime matches multibyte literals in the format")
(assert-equal test
&(Result.Success
(Datetime.init 0
0
0
(Maybe.Nothing)
(Maybe.Nothing)
(Maybe.Nothing)
(Maybe.Nothing)
(Maybe.Just (Timezone.init @"UTC" 0l false))))
&(Datetime.strptime "é UTC" "é %Z")
"strptime reads %Z after a multibyte literal")
(assert-equal test
&(Result.Success
(Datetime.init 0
0
0
(Maybe.Just 14)
(Maybe.Nothing)
(Maybe.Nothing)
(Maybe.Nothing)
(Maybe.Nothing)))
&(Datetime.strptime "é02PM" "é%I%p")
"strptime reads %p after a multibyte literal")
(assert-equal test
&(Result.Error
(String.concat
&[@"unknown format specifier: %" (String.from-bytes &[195b])]))
&(Datetime.strptime "" "%é")
"strptime reports an unknown specifier byte without re-encoding it")

(assert-equal test
&@""
&(roundtrip-sweep SWEEP-FROM SWEEP-TO "%Y-%j")
Expand Down
35 changes: 18 additions & 17 deletions time.carp
Original file line number Diff line number Diff line change
Expand Up @@ -604,9 +604,7 @@ It mimics [the C interface](http://www.cplusplus.com/reference/ctime/strftime/).
(hidden parse-digits)
(defn parse-digits [s pos n]
(if (<= (+ pos n) (String.length s))
(let [sub (String.suffix s pos)
tok (String.prefix &sub n)]
(Int.from-string &tok))
(Int.from-string &(String.byte-slice s pos (+ pos n)))
(Maybe.Nothing)))

(private parse-ranged)
Expand All @@ -622,13 +620,12 @@ It mimics [the C interface](http://www.cplusplus.com/reference/ctime/strftime/).
(defn match-in-array [arr s pos]
(let-do [result -1
i 1
slen (String.length s)
sub (String.suffix s pos)]
slen (String.length s)]
(while (and (= result -1) (< i (Array.length arr)))
(let [candidate (Array.unsafe-nth arr i)
clen (String.length candidate)]
(if (and (<= (+ pos clen) slen)
(= &(String.prefix &sub clen) candidate))
(= &(String.byte-slice s pos (+ pos clen)) candidate))
(set! result i)
(set! i (+ i 1)))))
result))
Expand All @@ -640,12 +637,11 @@ It mimics [the C interface](http://www.cplusplus.com/reference/ctime/strftime/).
-1
(let-do [result -1
i 1
sub (String.suffix s pos)
tok (String.prefix &sub 3)]
tok (String.byte-slice s pos (+ pos 3))]
(while (and (= result -1) (< i (Array.length arr)))
(let [candidate (Array.unsafe-nth arr i)]
(if (and (>= (String.length candidate) 3)
(= &tok &(String.prefix candidate 3)))
(= &tok &(String.byte-slice candidate 0 3)))
(set! result i)
(set! i (+ i 1)))))
result)))
Expand Down Expand Up @@ -699,9 +695,14 @@ It mimics [the C interface](http://www.cplusplus.com/reference/ctime/strftime/).
(set! result (String.concat &[result @"%b"]))
(set! i (+ i 2)))
(do
(set! result (String.concat &[result (str c) (str spec)]))
(set! result
(String.concat
&[result (String.byte-slice fmt i (+ i 2))]))
(set! i (+ i 2)))))
(do (set! result (String.concat &[result (str c)])) (set! i (+ i 1))))))
(do
(set! result
(String.concat &[result (String.byte-slice fmt i (+ i 1))]))
(set! i (+ i 1))))))
result))

(doc strptime "parses a string `input` according to format string `fmt`,
Expand Down Expand Up @@ -851,8 +852,7 @@ Example:
\p
(if (> (+ ipos 2) ilen)
(do (set! err @"failed to parse %p") (set! ok false))
(let [sub (String.suffix input ipos)
tok (String.prefix &sub 2)]
(let [tok (String.byte-slice input ipos (+ ipos 2))]
(cond
(= &tok "AM")
(do
Expand Down Expand Up @@ -1045,9 +1045,9 @@ Example:
(and (>= c \a) (<= c \z)))))
(set! n (+ n 1)))
(if (> n 0)
(let-do [sub (String.suffix input ipos)
nm (String.prefix &sub n)]
(set! tz-nm nm)
(do
(set! tz-nm
(String.byte-slice input ipos (+ ipos n)))
(set! has-tz true)
(set! ipos (+ ipos n))
(set! fpos (+ fpos 2)))
Expand All @@ -1059,7 +1059,8 @@ Example:
(do
(set! err
(String.concat
&[@"unknown format specifier: %" (str spec)]))
&[@"unknown format specifier: %"
(String.byte-slice &efmt (+ fpos 1) (+ fpos 2))]))
(set! ok false))))
(and (< ipos ilen) (= fc (String.char-at input ipos)))
(do (set! ipos (+ ipos 1)) (set! fpos (+ fpos 1)))
Expand Down