timezone_data/lib.rs
1//! `timezone-data` provides direct, allocation-free access to IANA timezone
2//! data, exposing the transitions, zone types, POSIX TZ rules, leap seconds,
3//! and metadata that most timezone libraries keep private.
4//!
5//! Timezone data is compiled from the official IANA source and embedded in the
6//! crate as pre-parsed static objects — one per zone — so there is no
7//! dependency on the host system's timezone files and nothing is parsed at
8//! runtime. The crate is `#![no_std]` and never allocates: a lookup is a binary
9//! search over `&'static` data.
10//!
11//! # Example
12//!
13//! ```
14//! let z = timezone_data::load("America/New_York").unwrap();
15//!
16//! // Inspect zone types (EST, EDT, ...).
17//! for zt in z.types() {
18//! // zt.abbrev, zt.offset, zt.is_dst
19//! let _ = zt;
20//! }
21//!
22//! // Look up the active zone at a specific Unix timestamp.
23//! let zt = z.lookup(1_700_000_000);
24//! assert_eq!(zt.abbrev, "EST");
25//!
26//! // Compute future transitions from the POSIX TZ rule.
27//! if let Some(rule) = z.extend() {
28//! let (start, end) = rule.transitions_for_year(2025).unwrap();
29//! assert!(start < end);
30//! }
31//! ```
32#![no_std]
33#![forbid(unsafe_code)]
34
35mod db;
36mod error;
37mod meta;
38mod posix;
39mod zone;
40
41pub use error::Error;
42pub use meta::{meta, parse_iso6709, Country, ZoneMeta};
43pub use posix::{parse_posix_tz, PosixTz, RuleKind, TransitionRule};
44pub use zone::{LeapSecond, RangeIter, RangeTransition, Transition, Zone, ZoneType};
45
46/// Loads a [`Zone`] by IANA timezone name from the embedded database.
47///
48/// An empty name or `"UTC"` resolves to the `UTC` zone.
49pub fn load(name: &str) -> Result<Zone, Error> {
50 let query = if name.is_empty() { "UTC" } else { name };
51 db::find(query).ok_or(Error::NotFound)
52}
53
54/// Loads a [`Zone`] by name, falling back to case-insensitive matching.
55pub fn load_insensitive(name: &str) -> Result<Zone, Error> {
56 if let Ok(z) = load(name) {
57 return Ok(z);
58 }
59 for canonical in names() {
60 if canonical.eq_ignore_ascii_case(name) {
61 return load(canonical);
62 }
63 }
64 Err(Error::NotFound)
65}
66
67/// Returns an iterator over every IANA timezone name in the embedded database.
68pub fn names() -> impl Iterator<Item = &'static str> {
69 db::names()
70}
71
72impl Zone {
73 /// Returns metadata (countries, coordinates) for this timezone, or `None`.
74 pub fn meta(&self) -> Option<ZoneMeta<'static>> {
75 meta(self.name())
76 }
77}