time/time.rs
1//! Date and time handling with an embedded IANA time zone database.
2//!
3//! `time` provides a small, correct, `no_std`-friendly date and time API built
4//! around a single value type, [`DateTime`], plus [`TimeZone`] and [`Error`].
5//! Durations are the standard [`core::time::Duration`]; there is no dedicated
6//! duration type.
7//!
8//! # Design
9//!
10//! * A [`DateTime`] always represents a precise instant (seconds and
11//! nanoseconds since the Unix epoch) together with a [`TimeZone`]. There is
12//! no separate "naive" or "floating" datetime.
13//! * Equality, ordering, hashing, subtraction and all instant arithmetic are
14//! defined on the instant only, never on the time zone. This avoids the
15//! classic footgun where `==` depends on the zone.
16//! * The `offset` reported by a value is always the offset in effect at that
17//! instant, even for civil times that fall in a DST gap.
18//! * Zone-less input is parsed as UTC. Use
19//! [`DateTime::parse_with_timezone`] to interpret zone-less input in a
20//! specific zone.
21//! * Well-known representations are selected with the [`Format`] enum via
22//! [`DateTime::format`]: RFC 3339, RFC 9557, RFC 2822, HTTP dates, and more.
23//! Strict, allocation-free output is available through
24//! [`DateTime::try_format`] and [`DateTime::format_to`].
25//! * The embedded IANA database ([`TimeZone::named`]) is available by default,
26//! is pre-parsed at build time, and works without `alloc`. Future
27//! daylight-saving transitions are materialized up to a rolling horizon (the
28//! IANA release year plus 50) so near-future lookups stay a plain search,
29//! with the POSIX rule governing beyond it. The advertised release is
30//! [`TZDB_VERSION`].
31//!
32//! # Measuring elapsed time
33//!
34//! Values from [`DateTime::now`] and [`DateTime::now_in`] also carry a
35//! monotonic clock reading. When both values being compared have one,
36//! [`DateTime::duration_since`], [`DateTime::duration_until`],
37//! [`DateTime::signed_duration_since`] and [`DateTime::elapsed`] measure with
38//! the monotonic clock and are robust against wall-clock adjustments (for
39//! example NTP steps or manual clock changes). Values that were parsed or
40//! built from civil fields use the wall clock, so measuring a duration across
41//! a serialization round trip is not monotonic. Use
42//! [`DateTime::has_monotonic`] to check.
43//!
44//! # Leap seconds
45//!
46//! The supported calendar has no leap seconds. A `:60` second is accepted on
47//! input (both in parsing and in [`DateTime::from_parts`]) and is represented
48//! as `:59` of the same minute; the library never produces `:60` on output.
49//!
50//! # Ambiguous and skipped civil times
51//!
52//! A civil time can be skipped (a DST gap) or repeated (a DST fold).
53//! [`DateTime::from_parts`] applies the "compatible" strategy: gaps are
54//! shifted forward and folds select the earlier instant. Use
55//! [`DateTime::from_parts_with`] with a [`Disambiguation`] to choose another
56//! strategy (including rejecting such times with
57//! [`ErrorKind::Ambiguous`]) and [`TimeZone::ambiguity`] to inspect the
58//! mapping.
59//!
60//! # Limitations
61//!
62//! * The supported UTC calendar range is `-9999-01-01` through `9999-12-31`.
63//! A value near the boundary can display a year one outside this range when
64//! a large UTC offset is applied.
65//! * There is no leap-second support (see above) and no local-time database
66//! for the hours, minutes and seconds of a zone beyond the abbreviation and
67//! the offset.
68//! * RFC 9557 support is partial: a single, bare time zone name annotation is
69//! understood on input; other annotation forms are rejected.
70//! * Parsing accepts up to nanosecond precision and truncates further
71//! fractional digits; output uses 3, 6 or 9 fractional digits, which keeps
72//! formatted strings sortable.
73//!
74//! # Feature flags
75//!
76//! * `std` (default): enables reading the system clock and the monotonic
77//! clock, and the system time zone. It uses `alloc` internally but exposes no
78//! allocation-requiring API.
79//! * `timezone-db` (default): embeds a copy of the IANA time zone database.
80//! * `timezone-system`: reads time zone rules from the system database
81//! (implies `std`), detecting and caching the system zone once per process.
82//! * `serde`: RFC 3339 / RFC 9557 serialization.
83//!
84//! # Examples
85//!
86//! ```
87//! use time::{DateTime, TimeZone};
88//!
89//! let dt: DateTime = "2024-07-11T01:14:00Z".parse().unwrap();
90//! assert_eq!(dt.unix(), 1_720_660_440);
91//!
92//! // Requires the default `timezone-db` feature.
93//! if let Ok(ny) = TimeZone::named("America/New_York") {
94//! let dt = dt.in_timezone(ny);
95//! assert_eq!(dt.hour(), 21);
96//! assert_eq!(dt.offset(), -4 * 3600);
97//! }
98//! ```
99
100#![no_std]
101#![forbid(unsafe_code)]
102#![warn(missing_docs)]
103
104#[cfg(feature = "std")]
105extern crate alloc;
106
107#[cfg(feature = "std")]
108extern crate std;
109
110mod civil;
111mod datetime;
112mod error;
113mod format;
114mod parse;
115mod posix;
116#[cfg(feature = "serde")]
117mod serde;
118mod timezone;
119mod tzdb;
120mod tzif;
121
122#[cfg(test)]
123mod tests;
124
125pub use core::time::Duration;
126
127#[cfg(feature = "timezone-db")]
128pub use crate::tzdb::all_timezones;
129pub use crate::{
130 datetime::{DateTime, Weekday},
131 error::{Error, ErrorKind},
132 format::{Format, Formatted},
133 timezone::{Ambiguity, Disambiguation, TimeZone},
134 tzdb::TZDB_VERSION,
135};