A Rust port of upstream tzcode strftime.c formatting semantics, provided as
the strftime library crate. It formats an explicit broken-down time (Tm) by a
strftime format string, reproducing what tzcode's strftime writes under the
C / C.UTF-8 locale. It is the formatting layer over
localtime-rs in the Rust
tzdb runtime toolchain.
let tm = strftime::Tm { tm_year: 101, tm_mon: 8, tm_mday: 9, tm_hour: 1,
tm_min: 46, tm_sec: 40, tm_wday: 0, tm_yday: 251, tm_isdst: 0,
tm_gmtoff: Some(0), tm_zone: Some("UTC".into()) };
assert_eq!(strftime::format_tm("%Y-%m-%d %H:%M:%S %Z", &tm).unwrap(),
"2001-09-09 01:46:40 UTC");strftime-rs does not replace libc strftime, does not implement
locale-complete date formatting, and does not define civil-time truth. It ports
the observable formatting semantics of upstream tzcode strftime.c under pinned
C / C.UTF-8 locale settings and verifies output against the compiled upstream C
oracle for admitted tm rows and format strings. It is not a general
date-formatting framework, a chrono/jiff competitor, a locale/ICU layer, or a
human-date library.
pub struct Tm { tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year,
tm_wday, tm_yday, tm_isdst: i32,
tm_gmtoff: Option<i32>, tm_zone: Option<String> }
// everyday: no length limit; returns the formatted string.
pub fn format_tm(format: &str, tm: &Tm) -> Result<String, Error>;
// C-like strftime(s, maxsize, …): maxsize includes the NUL; Err(Range) on overflow.
pub fn strftime_limited(format: &str, tm: &Tm, maxsize: usize) -> (Result<usize, Error>, String);tm_gmtoff / tm_zone are explicit because %z / %Z need them. Fields are
used verbatim (no normalization), exactly as strftime.c reads them.
Every directive strftime.c handles: %A %a %B %b %h %C %c %D %d %e %F %H %I %j %k %l %M %m %n %p %R %r %S %s %T %t %U %u %V %G %g %v %W %w %X %x %y %Y %Z %z %+ %%, the E/O modifiers (ignored under C), and the exact unknown-conversion /
trailing-% passthrough (%q → %q, %K → %K). First seal: C / C.UTF-8
locale only.
The oracle is a C harness linking upstream strftime.c, fed explicit
struct tm rows (decoupled from runtime conversion), printing retlen|output.
- Sweep: per-pair 1105 / 0 + full-stream byte-identical. 17
tmrows (UTC, pre-1900 / year-1 / year-10000, 2038, leap-secondtm_sec=60, isdst-unknown, ±/non-hourgmtoff, empty/long/--leading zone, week-year boundaries, Feb 29) × 65 formats (every directive + composites +E/O+ unknown + edges). Receipt:reports/strftime-oracle/. - Integration (localtime-rs → strftime-rs): 448 / 0 vs the combined upstream
localtime.c+strftime.coracle (7 zones × 8 timestamps × 8 formats) — the end-to-endtimestamp → local time → formatted stringpath. - Kani: the weekday/month name-table index is proven in bounds.
- Fuzz:
cargo +nightly fuzz run fmtover format strings × arbitrarytmfields × buffer sizes — 0 crashes.
cargo test
cargo clippy --all-targets -- -D warnings
cargo kani --harness name_index_in_bounds
python3 lab/oracle/sweep.py # needs the compiled strftime.c harness
BSD-3-Clause, retaining the upstream Regents / tz contributors copyright
(strftime.c is BSD-licensed). An independent reimplementation.