tz/
lib.rs

1#![deny(missing_docs)]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5//! This crate provides the [`TimeZone`] and [`DateTime`] types, which can be used to determine local time on a given time zone.
6//!
7//! This allows to convert between an [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time) and a calendar time exprimed in the [proleptic gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar) with a provided time zone.
8//!
9//! Time zones are provided to the library with a [POSIX `TZ` string](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html) which can be read from the environment.
10//!
11//! Two formats are currently accepted for the `TZ` string:
12//! * `std offset[dst[offset][,start[/time],end[/time]]]` providing a time zone description,
13//! * `file` or `:file` providing the path to a [TZif file](https://datatracker.ietf.org/doc/html/rfc8536), which is absolute or relative to the system timezone directory.
14//!
15//! See also the [Linux manual page of tzset(3)](https://man7.org/linux/man-pages/man3/tzset.3.html) and the [glibc documentation of the `TZ` environment variable](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html).
16//!
17//! # Usage
18//!
19//! ## Time zone
20//!
21//! ```rust
22//! # fn main() -> Result<(), tz::TzError> {
23//! # #[cfg(feature = "std")] {
24//! use tz::TimeZone;
25//!
26//! // 2000-01-01T00:00:00Z
27//! let unix_time = 946684800;
28//!
29//! // Get UTC time zone
30//! let time_zone_utc = TimeZone::utc();
31//! assert_eq!(time_zone_utc.find_local_time_type(unix_time)?.ut_offset(), 0);
32//!
33//! // Get fixed time zone at GMT-1
34//! let time_zone_fixed = TimeZone::fixed(-3600)?;
35//! assert_eq!(time_zone_fixed.find_local_time_type(unix_time)?.ut_offset(), -3600);
36//!
37//! // Get local time zone (UNIX only)
38//! let time_zone_local = TimeZone::local()?;
39//! // Get the current local time type
40//! let _current_local_time_type = time_zone_local.find_current_local_time_type()?;
41//!
42//! // Get time zone from a TZ string:
43//! // From an absolute file
44//! let _ = TimeZone::from_posix_tz("/usr/share/zoneinfo/Pacific/Auckland");
45//! // From a file relative to the system timezone directory
46//! let _ = TimeZone::from_posix_tz("Pacific/Auckland");
47//! // From a time zone description
48//! TimeZone::from_posix_tz("HST10")?;
49//! TimeZone::from_posix_tz("<-03>3")?;
50//! TimeZone::from_posix_tz("NZST-12:00:00NZDT-13:00:00,M10.1.0,M3.3.0")?;
51//! // Use a leading colon to force searching for a corresponding file
52//! let _ = TimeZone::from_posix_tz(":UTC");
53//! # }
54//! # Ok(())
55//! # }
56//! ```
57//!
58//! ## Date time
59//!
60//! ```rust
61//! # fn main() -> Result<(), tz::TzError> {
62//! # #[cfg(feature = "std")] {
63//! use tz::{DateTime, LocalTimeType, TimeZone, UtcDateTime};
64//!
65//! // Get the current UTC date time
66//! let _current_utc_date_time = UtcDateTime::now()?;
67//!
68//! // Create a new UTC date time (2000-01-01T00:00:00.123456789Z)
69//! let utc_date_time = UtcDateTime::new(2000, 1, 1, 0, 0, 0, 123_456_789)?;
70//! assert_eq!(utc_date_time.year(), 2000);
71//! assert_eq!(utc_date_time.month(), 1);
72//! assert_eq!(utc_date_time.month_day(), 1);
73//! assert_eq!(utc_date_time.hour(), 0);
74//! assert_eq!(utc_date_time.minute(), 0);
75//! assert_eq!(utc_date_time.second(), 0);
76//! assert_eq!(utc_date_time.week_day(), 6);
77//! assert_eq!(utc_date_time.year_day(), 0);
78//! assert_eq!(utc_date_time.unix_time(), 946684800);
79//! assert_eq!(utc_date_time.nanoseconds(), 123_456_789);
80//! assert_eq!(utc_date_time.to_string(), "2000-01-01T00:00:00.123456789Z");
81//!
82//! // Create a new UTC date time from a Unix time with nanoseconds (2000-01-01T00:00:00.123456789Z)
83//! let other_utc_date_time = UtcDateTime::from_timespec(946684800, 123_456_789)?;
84//! assert_eq!(other_utc_date_time, utc_date_time);
85//!
86//! // Project the UTC date time to a time zone
87//! let date_time = utc_date_time.project(TimeZone::fixed(-3600)?.as_ref())?;
88//! assert_eq!(date_time.year(), 1999);
89//! assert_eq!(date_time.month(), 12);
90//! assert_eq!(date_time.month_day(), 31);
91//! assert_eq!(date_time.hour(), 23);
92//! assert_eq!(date_time.minute(), 0);
93//! assert_eq!(date_time.second(), 0);
94//! assert_eq!(date_time.week_day(), 5);
95//! assert_eq!(date_time.year_day(), 364);
96//! assert_eq!(date_time.local_time_type().ut_offset(), -3600);
97//! assert_eq!(date_time.unix_time(), 946684800);
98//! assert_eq!(date_time.nanoseconds(), 123_456_789);
99//! assert_eq!(date_time.to_string(), "1999-12-31T23:00:00.123456789-01:00");
100//!
101//! // Project the date time to another time zone
102//! let other_date_time = date_time.project(TimeZone::fixed(3600)?.as_ref())?;
103//! assert_eq!(other_date_time.year(), 2000);
104//! assert_eq!(other_date_time.month(), 1);
105//! assert_eq!(other_date_time.month_day(), 1);
106//! assert_eq!(other_date_time.hour(), 1);
107//! assert_eq!(other_date_time.minute(), 0);
108//! assert_eq!(other_date_time.second(), 0);
109//! assert_eq!(other_date_time.week_day(), 6);
110//! assert_eq!(other_date_time.year_day(), 0);
111//! assert_eq!(other_date_time.local_time_type().ut_offset(), 3600);
112//! assert_eq!(other_date_time.unix_time(), 946684800);
113//! assert_eq!(other_date_time.nanoseconds(), 123_456_789);
114//! assert_eq!(other_date_time.to_string(), "2000-01-01T01:00:00.123456789+01:00");
115//!
116//! // Create a new date time from a Unix time with nanoseconds and a time zone (2000-01-01T00:00:00.123456789Z)
117//! let another_date_time = DateTime::from_timespec(946684800, 123_456_789, TimeZone::fixed(86400)?.as_ref())?;
118//!
119//! // DateTime objects are compared by their Unix time and nanoseconds
120//! assert_eq!(another_date_time, other_date_time);
121//!
122//! // Get the current date time at the local time zone (UNIX only)
123//! let time_zone_local = TimeZone::local()?;
124//! let _date_time = DateTime::now(time_zone_local.as_ref())?;
125//!
126//! // Create a new date time with an UTC offset (2000-01-01T01:00:00.123456789+01:00)
127//! let date_time = DateTime::new(2000, 1, 1, 1, 0, 0, 123_456_789, LocalTimeType::with_ut_offset(3600)?)?;
128//! assert_eq!(date_time.year(), 2000);
129//! assert_eq!(date_time.month(), 1);
130//! assert_eq!(date_time.month_day(), 1);
131//! assert_eq!(date_time.hour(), 1);
132//! assert_eq!(date_time.minute(), 0);
133//! assert_eq!(date_time.second(), 0);
134//! assert_eq!(date_time.week_day(), 6);
135//! assert_eq!(date_time.year_day(), 0);
136//! assert_eq!(date_time.unix_time(), 946684800);
137//! assert_eq!(date_time.nanoseconds(), 123_456_789);
138//! assert_eq!(date_time.to_string(), "2000-01-01T01:00:00.123456789+01:00");
139//!
140//! //
141//! // Find the possible date times corresponding to a date, a time and a time zone
142//! //
143//! let time_zone = TimeZone::from_posix_tz("CET-1CEST,M3.5.0,M10.5.0/3")?;
144//!
145//! // Found date time is unique
146//! let found_date_times = DateTime::find(2000, 1, 1, 0, 0, 0, 0, time_zone.as_ref())?;
147//! let unique = found_date_times.unique().unwrap();
148//! assert_eq!(unique, found_date_times.earliest().unwrap());
149//! assert_eq!(unique, found_date_times.latest().unwrap());
150//! assert_eq!(unique.local_time_type().ut_offset(), 3600);
151//! assert_eq!(unique.local_time_type().time_zone_designation(), "CET");
152//!
153//! // Found date time was skipped by a forward transition
154//! let found_date_times = DateTime::find(2000, 3, 26, 2, 30, 0, 0, time_zone.as_ref())?;
155//!
156//! assert_eq!(found_date_times.unique(), None);
157//!
158//! let earliest = found_date_times.earliest().unwrap();
159//! assert_eq!(earliest.hour(), 2);
160//! assert_eq!(earliest.minute(), 0);
161//! assert_eq!(earliest.local_time_type().ut_offset(), 3600);
162//! assert_eq!(earliest.local_time_type().time_zone_designation(), "CET");
163//!
164//! let latest = found_date_times.latest().unwrap();
165//! assert_eq!(latest.hour(), 3);
166//! assert_eq!(latest.minute(), 0);
167//! assert_eq!(latest.local_time_type().ut_offset(), 7200);
168//! assert_eq!(latest.local_time_type().time_zone_designation(), "CEST");
169//!
170//! // Found date time is ambiguous because of a backward transition
171//! let found_date_times = DateTime::find(2000, 10, 29, 2, 30, 0, 0, time_zone.as_ref())?;
172//!
173//! assert_eq!(found_date_times.unique(), None);
174//!
175//! let earliest = found_date_times.earliest().unwrap();
176//! assert_eq!(earliest.hour(), 2);
177//! assert_eq!(earliest.minute(), 30);
178//! assert_eq!(earliest.local_time_type().ut_offset(), 7200);
179//! assert_eq!(earliest.local_time_type().time_zone_designation(), "CEST");
180//!
181//! let latest = found_date_times.latest().unwrap();
182//! assert_eq!(latest.hour(), 2);
183//! assert_eq!(latest.minute(), 30);
184//! assert_eq!(latest.local_time_type().ut_offset(), 3600);
185//! assert_eq!(latest.local_time_type().time_zone_designation(), "CET");
186//! # }
187//! # Ok(())
188//! # }
189//! ```
190
191#[cfg(feature = "alloc")]
192extern crate alloc;
193
194mod constants;
195mod utils;
196
197#[cfg(feature = "std")]
198mod parse;
199
200pub mod datetime;
201pub mod error;
202pub mod timezone;
203
204#[doc(inline)]
205pub use datetime::{DateTime, UtcDateTime};
206
207#[doc(inline)]
208pub use error::TzError;
209
210#[doc(inline)]
211pub use timezone::{LocalTimeType, TimeZoneRef};
212
213#[doc(inline)]
214#[cfg(feature = "alloc")]
215pub use timezone::TimeZone;
216
217/// Alias for [`std::result::Result`] with the crate unified error
218pub type Result<T> = core::result::Result<T, TzError>;