Skip to content
Draft
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
10 changes: 5 additions & 5 deletions test/time.carp
Original file line number Diff line number Diff line change
Expand Up @@ -1156,16 +1156,16 @@
"strftime copies non-ASCII literals verbatim")
(assert-equal test
&@"Größe: 2024"
&(Datetime.format "Größe: %Y" &(Datetime.date 2024 3 15))
"format slices non-ASCII literals by byte")
&(Datetime.unsafe-format "Größe: %Y" &(Datetime.date 2024 3 15))
"unsafe-format slices non-ASCII literals by byte")
(assert-equal test
&@"Größe: 2024"
&(fmt "Größe: %Y" &(Datetime.date 2024 3 15))
"fmt reaches format with a non-ASCII literal")
"fmt reaches unsafe-format with a non-ASCII literal")
(assert-equal test
&@"hello"
&(Datetime.format "hello" &(Datetime.date 2024 3 15))
"format passes through a slice with no directive")
&(Datetime.unsafe-format "hello" &(Datetime.date 2024 3 15))
"unsafe-format passes through a slice with no directive")

(assert-equal test
&(Result.Success (Datetime.date 2024 3 15))
Expand Down
54 changes: 32 additions & 22 deletions time.carp
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ All of the time information is obtained from the operating system directly.")
@(TM.tm_gmtoff ct)
(/= 0 @(TM.tm_isdst ct)))))))

(register format (Fn [(Ref String) (Ref Datetime)] String))
(implements format Datetime.format)
(register unsafe-format (Fn [(Ref String) (Ref Datetime)] String))
(implements unsafe-format Datetime.unsafe-format)

(register format-for (Fn [Char (Ref Datetime)] String))

Expand All @@ -501,9 +501,9 @@ Text outside a directive is copied verbatim. `%%` renders a single `%`, as does
a trailing `%` with nothing after it; an unrecognized specifier renders as the
character that follows the `%`.

You can alternatively use the `fmt` macro or `format` function, but you’ll only
be able to use one of the formatting properties there. Thus this function is
provided as a convenience for fine-grained string tweaking.
You can alternatively use the `fmt` macro or `unsafe-format` function, but
you’ll only be able to use one of the formatting properties there. Thus this
function is provided as a convenience for fine-grained string tweaking.

Example:

Expand Down Expand Up @@ -546,30 +546,30 @@ Example:
\w
(str (weekday dt))
\d
(format "%02d" @(day dt))
(unsafe-format "%02d" @(day dt))
\b
(month-short-string dt)
\B
(month-string dt)
\m
(format "%02d" @(month dt))
(unsafe-format "%02d" @(month dt))
\y
(format "%02d" (mod @(year dt) 100))
(unsafe-format "%02d" (mod @(year dt) 100))
\Y
(format "%04d" @(year dt))
(unsafe-format "%04d" @(year dt))
\H
(format "%02d" (Maybe.from @(hours dt) 0))
(unsafe-format "%02d" (Maybe.from @(hours dt) 0))
\I
(let [h (mod (Maybe.from @(hours dt) 0) 12)]
(format "%02d" (if (= h 0) 12 h)))
(unsafe-format "%02d" (if (= h 0) 12 h)))
\p
(if (> (Maybe.from @(hours dt) 0) 11) @"PM" @"AM")
\M
(format "%02d" (Maybe.from @(minutes dt) 0))
(unsafe-format "%02d" (Maybe.from @(minutes dt) 0))
\S
(format "%02d" (Maybe.from @(seconds dt) 0))
(unsafe-format "%02d" (Maybe.from @(seconds dt) 0))
\n
(format "%09d" (Maybe.from @(nanoseconds dt) 0))
(unsafe-format "%09d" (Maybe.from @(nanoseconds dt) 0))
\z
(match @(tz dt)
(Maybe.Just t)
Expand All @@ -583,15 +583,16 @@ Example:
\Z
@(Timezone.name &(Maybe.from @(tz dt) (Timezone.zero)))
\j
(format "%03d" (yearday dt))
(unsafe-format "%03d" (yearday dt))
\U
; week of the year, Sunday as the first day; days before the first
; Sunday are week 0. weekday is Monday-based, so shift to Sunday-based.
(format "%02d" (/ (+ (- (yearday dt) (mod (+ (weekday dt) 1) 7)) 6) 7))
(unsafe-format "%02d"
(/ (+ (- (yearday dt) (mod (+ (weekday dt) 1) 7)) 6) 7))
\W
; week of the year, Monday as the first day; days before the first
; Monday are week 0. weekday is already Monday-based.
(format "%02d" (/ (+ (- (yearday dt) (weekday dt)) 6) 7))
(unsafe-format "%02d" (/ (+ (- (yearday dt) (weekday dt)) 6) 7))
\c
(strftime dt "%a %b %d %H:%M:%S %Y")
\x
Expand All @@ -601,9 +602,11 @@ Example:
\u
(str (isoweekday dt))
\V
(let [ic (isocalendar dt)] (format "%02d" @(Array.unsafe-nth &ic 1)))
(let [ic (isocalendar dt)]
(unsafe-format "%02d" @(Array.unsafe-nth &ic 1)))
\G
(let [ic (isocalendar dt)] (format "%04d" @(Array.unsafe-nth &ic 0)))
(let [ic (isocalendar dt)]
(unsafe-format "%04d" @(Array.unsafe-nth &ic 0)))
\F
(strftime dt "%Y-%m-%d")
\T
Expand All @@ -618,10 +621,17 @@ Example:
(month-short-string dt)
(String.from-bytes &[(Byte.from-int (Char.to-int c))])))

(doc format "The interface implementation of `format` for `Datetime` values.
(doc unsafe-format "The interface implementation of `unsafe-format` for
`Datetime` values.

It mimics [the C interface](http://www.cplusplus.com/reference/ctime/strftime/).")
(defn format [s dt]
It mimics [the C interface](http://www.cplusplus.com/reference/ctime/strftime/).

Unlike the `unsafe-format` implementations that wrap C’s `printf`, this one is
directive-safe: it expands the first directive it finds and copies the rest of
`s` verbatim, so a string carrying more directives than arguments cannot read
past the end of the argument list. It carries the interface’s name, not its
hazard.")
(defn unsafe-format [s dt]
(let [idx (String.index-of s \%)
len (String.length s)]
(if (or (< idx 0) (= idx (Int.dec len)))
Expand Down
Loading