Skip to content

infinityabundance/localtime-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

localtime-rs

A Rust port of upstream tzcode localtime.c runtime semantics, provided as the localtime library crate. Given an admitted TZif file (or a POSIX TZ string) and a POSIX timestamp, it produces the local broken-down time — gmtoff, isdst, the zone abbreviation, and the calendar fields — reproducing what tzcode's localtime_rz observes.

let bytes = std::fs::read("/usr/share/zoneinfo/America/New_York")?;
let tz = localtime::TimeZone::from_tzif_bytes(&bytes)?;
let lt = tz.local_time(1620000000)?;
assert_eq!((lt.year, lt.month, lt.abbreviation.as_str()), (2021, 5, "EDT"));

This starts the runtime-interpretation half of the Rust tzdb toolchain (TZif file → runtime interpretation → local civil time), beside zic-rs (the compiler) and the producer/QA crates.

Claim boundary

localtime-rs does not replace libc, does not define civil-time truth, and does not claim global timezone correctness. It ports the observable runtime semantics of upstream tzcode localtime.c into Rust for admitted TZif inputs and verifies timestamp-to-local-time behaviour against the upstream C oracle under pinned TZ/TZDIR environments. It is not an ergonomic date/time framework, a chrono/jiff competitor, a full libc layer, or a policy-correct civil-time engine.

Scope

TZif v1/v2/v3/v4 loading · UTC timestamp → local broken-down time · gmtoff · isdst · abbreviation · transition search · POSIX footer projection · pre-1970 · post-last-transition · corrupt-TZif refusal (typed errors), plus the four sealed deepening phases below (POSIX-TZ · RIGHT-LEAP · HOSTILE · MKTIME). Deferred: strftime/asctime/ctime (separate crates), difftime (difftime-rs), locale behaviour, the thread-local/global libc API shape, environment-mutation semantics, and mktime on bare POSIX-string / right/ leap zones.

Public API

let tz = TimeZone::from_tzif_bytes(bytes)?;   // or TimeZone::from_posix("EST5EDT,M3.2.0,M11.1.0")?
let lt = tz.local_time(unix_timestamp)?;      // LocalTime { year, month, day, hour,
                                              //   minute, second, weekday, yearday,
                                              //   utc_offset, isdst, abbreviation }
// The inverse: broken-down local civil time → time_t (gaps/folds/tm_isdst/normalization).
let t = tz.mktime(&Tm { tm_year: 2021 - 1900, tm_mon: 6, tm_mday: 1,
                        tm_hour: 8, tm_min: 0, tm_sec: 0, tm_isdst: -1 })?;  // 1625140800
tz.version();        // 1/2/3/4, or 0 for a POSIX zone
tz.is_leap_aware();  // a right/ build

Oracle & evidence

The oracle is a C harness linking upstream localtime.c (tzalloc + localtime_rz), emitting structured rows ts|year|mon|mday|hour|min|sec|isdst| gmtoff|abbr. localtime-rs is compared field-for-field.

  • Sweep: 36 / 0. 19 fixture zones + 6 POSIX strings + 4 right/ zones, each over 34 timestamps (epoch, ±boundaries, the 2³¹ boundary, deep i64, DST edges, pre-1970 LMT, footer-projected 2200, leap-second instants), plus 7 hostile TZif files (all refused). Receipt: reports/localtime-oracle/.
    • Posix zones are verified against the standalone localtime.c harness; right/ (leap) zones against glibc's vendored tzcode localtime — the standalone build ships with runtime leap application off (TZ_RUNTIME_LEAPS), a classified build quirk; both harnesses are byte-identical on non-leap zones.
  • Kani: the transition-search index (lo - 1) is proven in bounds for any transition count and t.
  • Fuzz: cargo +nightly fuzz run tzif_runtime over arbitrary TZif bytes — found and fixed 3 panics (a leap-correction subtraction overflow, a POSIX footer offset-multiply overflow, and an out-of-range footer-rule index), then 21.4M executions clean. Receipt: reports/fuzz/.

Deepening phases (v0.2.0)

  • POSIX-TZ (reports/posix-tz/) — from_posix parsing + footer projection vs the standalone oracle: 40 strings/zones × 25,980 timestamps = 1,039,200 field-rows bit-identical (northern/southern hemisphere, M/J/n rules, angle-bracket numeric names, half-hour offsets, far-future 2200+).
  • RIGHT-LEAP (reports/right-leap/) — every one of the 27 leap instants (135 :60 rows), the 1→27s correction ladder, pre-first-leap, and future no-new-leap, across 5 right/ zones, 5/0 vs the leap-applying glibc oracle. Root-causes the standalone build's leap-off quirk (a +3-byte v1→v2 over-skip).
  • HOSTILE (reports/hostile/) — malformed TZif/TZ fail closed with typed errors, never a panic: ~16,500 bit-flips + 2,069 truncations + crafted cases, and fuzz 10.6M (TZif) + 35.8M (POSIX) runs, 0 new crashes.
  • MKTIME (reports/mktime/) — mktime/mktime_z over 14 zones × 38,921 rows = 544,894 comparisons bit-identical (gaps, folds, tm_isdst hints, out-of-range normalization, overflow); a footer-type-ordering bug found + fixed; mktime fuzz 33,887 runs clean.

Build & test

cargo test
cargo clippy --all-targets -- -D warnings
cargo kani --harness transition_search_index_in_bounds
python3 lab/oracle/sweep.py        # the oracle sweep (needs the compiled harness)

License

BSD-3-Clause, retaining the upstream Regents of the University of California / tz contributors copyright (localtime.c is BSD-licensed). An independent reimplementation.

About

Rust port of upstream tzcode localtime.c runtime semantics — TZif → local civil time, verified field-for-field against a compiled localtime.c oracle. Not a libc replacement.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors