/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jiff-0.2.35/src/tz/mod.rs
Line | Count | Source |
1 | | /*! |
2 | | Routines for interacting with time zones and the zoneinfo database. |
3 | | |
4 | | The main type in this module is [`TimeZone`]. For most use cases, you may not |
5 | | even need to interact with this type at all. For example, this code snippet |
6 | | converts a civil datetime to a zone aware datetime: |
7 | | |
8 | | ``` |
9 | | use jiff::civil::date; |
10 | | |
11 | | let zdt = date(2024, 7, 10).at(20, 48, 0, 0).in_tz("America/New_York")?; |
12 | | assert_eq!(zdt.to_string(), "2024-07-10T20:48:00-04:00[America/New_York]"); |
13 | | |
14 | | # Ok::<(), Box<dyn std::error::Error>>(()) |
15 | | ``` |
16 | | |
17 | | And this example parses a zone aware datetime from a string: |
18 | | |
19 | | ``` |
20 | | use jiff::Zoned; |
21 | | |
22 | | let zdt: Zoned = "2024-07-10 20:48[america/new_york]".parse()?; |
23 | | assert_eq!(zdt.year(), 2024); |
24 | | assert_eq!(zdt.month(), 7); |
25 | | assert_eq!(zdt.day(), 10); |
26 | | assert_eq!(zdt.hour(), 20); |
27 | | assert_eq!(zdt.minute(), 48); |
28 | | assert_eq!(zdt.offset().seconds(), -4 * 60 * 60); |
29 | | assert_eq!(zdt.time_zone().iana_name(), Some("America/New_York")); |
30 | | |
31 | | # Ok::<(), Box<dyn std::error::Error>>(()) |
32 | | ``` |
33 | | |
34 | | Yet, neither of the above examples require uttering [`TimeZone`]. This is |
35 | | because the datetime types in this crate provide higher level abstractions for |
36 | | working with time zone identifiers. Nevertheless, sometimes it is useful to |
37 | | work with a `TimeZone` directly. For example, if one has a `TimeZone`, then |
38 | | conversion from a [`Timestamp`](crate::Timestamp) to a [`Zoned`](crate::Zoned) |
39 | | is infallible: |
40 | | |
41 | | ``` |
42 | | use jiff::{tz::TimeZone, Timestamp}; |
43 | | |
44 | | let tz = TimeZone::get("America/New_York")?; |
45 | | let ts = Timestamp::UNIX_EPOCH; |
46 | | let zdt = ts.to_zoned(tz); |
47 | | assert_eq!(zdt.to_string(), "1969-12-31T19:00:00-05:00[America/New_York]"); |
48 | | |
49 | | # Ok::<(), Box<dyn std::error::Error>>(()) |
50 | | ``` |
51 | | |
52 | | # The [IANA Time Zone Database] |
53 | | |
54 | | Since a time zone is a set of rules for determining the civil time, via an |
55 | | offset from UTC, in a particular geographic region, a database is required to |
56 | | represent the full complexity of these rules in practice. The standard database |
57 | | is widespread use is the [IANA Time Zone Database]. On Unix systems, this is |
58 | | typically found at `/usr/share/zoneinfo`, and Jiff will read it automatically. |
59 | | On Windows systems, there is no canonical Time Zone Database installation, and |
60 | | so Jiff embeds it into the compiled artifact. (This does not happen on Unix |
61 | | by default.) |
62 | | |
63 | | See the [`TimeZoneDatabase`] for more information. |
64 | | |
65 | | # The system or "local" time zone |
66 | | |
67 | | In many cases, the operating system manages a "default" time zone. It might, |
68 | | for example, be how the `date` program converts a Unix timestamp to a time that |
69 | | is "local" to you. |
70 | | |
71 | | Unfortunately, there is no universal approach to discovering a system's default |
72 | | time zone. Instead, Jiff uses heuristics like reading `/etc/localtime` on Unix, |
73 | | and calling [`GetDynamicTimeZoneInformation`] on Windows. But in all cases, |
74 | | Jiff will always use the IANA Time Zone Database for implementing time zone |
75 | | transition rules. (For example, Windows specific APIs for time zone transitions |
76 | | are not supported by Jiff.) |
77 | | |
78 | | Moreover, Jiff supports reading the `TZ` environment variable, as specified |
79 | | by POSIX, on all systems. |
80 | | |
81 | | To get the system's default time zone, use [`TimeZone::system`]. |
82 | | |
83 | | # Core-only environments |
84 | | |
85 | | By default, Jiff attempts to read time zone rules from `/usr/share/zoneinfo` |
86 | | on Unix and a bundled database on other platforms (like on Windows). This happens |
87 | | at runtime, and aside from requiring APIs to interact with the file system |
88 | | on Unix, it also requires dynamic memory allocation. |
89 | | |
90 | | For core-only environments that don't have file system APIs or dynamic |
91 | | memory allocation, Jiff provides a way to construct `TimeZone` values at |
92 | | compile time by compiling time zone rules into your binary. This does mean |
93 | | that your program will need to be re-compiled if the time zone rules change |
94 | | (in contrast to Jiff's default behavior of reading `/usr/share/zoneinfo` at |
95 | | runtime on Unix), but sometimes there isn't a practical alternative. |
96 | | |
97 | | With the `static` crate feature enabled, the [`jiff::tz::get`](crate::tz::get) |
98 | | macro becomes available in this module. This example shows how use it to build |
99 | | a `TimeZone` at compile time. Here, we find the next DST transition from a |
100 | | particular timestamp in `Europe/Zurich`, and then print that in local time for |
101 | | Zurich: |
102 | | |
103 | | ``` |
104 | | use jiff::{tz::{self, TimeZone}, Timestamp}; |
105 | | |
106 | | static TZ: TimeZone = tz::get!("Europe/Zurich"); |
107 | | |
108 | | let ts: Timestamp = "2025-02-25T00:00Z".parse()?; |
109 | | let Some(next_transition) = TZ.following(ts).next() else { |
110 | | return Err("no time zone transitions".into()); |
111 | | }; |
112 | | let zdt = next_transition.timestamp().to_zoned(TZ.clone()); |
113 | | assert_eq!(zdt.to_string(), "2025-03-30T03:00:00+02:00[Europe/Zurich]"); |
114 | | |
115 | | # Ok::<(), Box<dyn std::error::Error>>(()) |
116 | | ``` |
117 | | |
118 | | The above example does not require dynamic memory allocation or access to file |
119 | | system APIs. It also _only_ embeds the `Europe/Zurich` time zone into your |
120 | | compiled binary. |
121 | | |
122 | | [IANA Time Zone Database]: https://en.wikipedia.org/wiki/Tz_database |
123 | | [`GetDynamicTimeZoneInformation`]: https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-getdynamictimezoneinformation |
124 | | */ |
125 | | |
126 | | pub use self::{ |
127 | | ambiguous::{ |
128 | | AmbiguousOffset, AmbiguousTimestamp, AmbiguousZoned, Disambiguation, |
129 | | }, |
130 | | db::{db, TimeZoneDatabase, TimeZoneName, TimeZoneNameIter}, |
131 | | offset::{Dst, Offset, OffsetArithmetic, OffsetConflict, OffsetRound}, |
132 | | timezone::{ |
133 | | TimeZone, TimeZoneFollowingTransitions, TimeZoneOffsetInfo, |
134 | | TimeZonePrecedingTransitions, TimeZoneTransition, |
135 | | }, |
136 | | }; |
137 | | |
138 | | mod ambiguous; |
139 | | #[cfg(feature = "tzdb-concatenated")] |
140 | | mod concatenated; |
141 | | mod db; |
142 | | mod offset; |
143 | | pub(crate) mod posix; |
144 | | #[cfg(feature = "tz-system")] |
145 | | mod system; |
146 | | #[cfg(all(test, feature = "alloc"))] |
147 | | pub(crate) mod testdata; |
148 | | mod timezone; |
149 | | mod tzif; |
150 | | // See module comment for WIP status. :-( |
151 | | #[cfg(all(test, feature = "alloc"))] |
152 | | mod zic; |
153 | | |
154 | | /// Create a `TimeZone` value from TZif data in [`jiff-tzdb`] at compile time. |
155 | | /// |
156 | | /// This reads the data for the time zone with the IANA identifier given from |
157 | | /// [`jiff-tzdb`], parses it as TZif specified by [RFC 9636], and constructs a |
158 | | /// `TimeZone` value for use in a `const` context. This enables using IANA time |
159 | | /// zones with Jiff in core-only environments. No dynamic memory allocation is |
160 | | /// used. |
161 | | /// |
162 | | /// # Input |
163 | | /// |
164 | | /// This macro takes one positional parameter that must be a literal string. |
165 | | /// The string should be an IANA time zone identifier, e.g., |
166 | | /// `America/New_York`. |
167 | | /// |
168 | | /// # Return type |
169 | | /// |
170 | | /// This macro returns a value with type `TimeZone`. To get a `&'static |
171 | | /// TimeZone`, simply use `&get!("...")`. |
172 | | /// |
173 | | /// # Usage |
174 | | /// |
175 | | /// Callers should only call this macro once for each unique IANA time zone |
176 | | /// identifier you need. Otherwise, multiple copies of the same embedded |
177 | | /// time zone data could appear in your binary. There are no correctness |
178 | | /// issues with this, but it could make your binary bigger than it needs to be. |
179 | | /// |
180 | | /// # When should I use this? |
181 | | /// |
182 | | /// Users should only use this macro if they have a _specific need_ for it |
183 | | /// (like using a time zone on an embedded device). In particular, this will |
184 | | /// embed the time zone transition rules into your binary. If the time zone |
185 | | /// rules change, your program will need to be re-compiled. |
186 | | /// |
187 | | /// In contrast, Jiff's default configuration on Unix is to read from |
188 | | /// `/usr/share/zoneinfo` at runtime. This means your application will |
189 | | /// automatically use time zone updates and doesn't need to be re-compiled. |
190 | | /// |
191 | | /// Using a static `TimeZone` may also be faster in some cases. In particular, |
192 | | /// a `TimeZone` created at runtime from a `/usr/share/zoneinfo` uses |
193 | | /// automatic reference counting internally. In contrast, a `TimeZone` created |
194 | | /// with this macro does not. |
195 | | /// |
196 | | /// # Example |
197 | | /// |
198 | | /// This example shows how to find the next DST transition from a particular |
199 | | /// timestamp in `Europe/Zurich`, and then print that in local time for Zurich: |
200 | | /// |
201 | | /// ``` |
202 | | /// use jiff::{tz::{self, TimeZone}, Timestamp}; |
203 | | /// |
204 | | /// static TZ: TimeZone = tz::get!("Europe/Zurich"); |
205 | | /// |
206 | | /// let ts: Timestamp = "2025-02-25T00:00Z".parse()?; |
207 | | /// let Some(next_transition) = TZ.following(ts).next() else { |
208 | | /// return Err("no time zone transitions".into()); |
209 | | /// }; |
210 | | /// let zdt = next_transition.timestamp().to_zoned(TZ.clone()); |
211 | | /// assert_eq!(zdt.to_string(), "2025-03-30T03:00:00+02:00[Europe/Zurich]"); |
212 | | /// |
213 | | /// # Ok::<(), Box<dyn std::error::Error>>(()) |
214 | | /// ``` |
215 | | /// |
216 | | /// [RFC 9636]: https://datatracker.ietf.org/doc/rfc9636/ |
217 | | /// [`jiff-tzdb`]: https://docs.rs/jiff-tzdb |
218 | | #[cfg(feature = "static")] |
219 | | pub use jiff_static::get; |
220 | | |
221 | | /// Create a `TimeZone` value from TZif data in a file at compile time. |
222 | | /// |
223 | | /// This reads the data in the file path given, parses it as TZif specified by |
224 | | /// [RFC 9636], and constructs a `TimeZone` value for use in a `const` context. |
225 | | /// This enables using IANA time zones with Jiff in core-only environments. No |
226 | | /// dynamic memory allocation is used. |
227 | | /// |
228 | | /// Unlike [`jiff::tz::get`](get), this reads TZif data from a file. |
229 | | /// `jiff::tz::get`, in contrast, reads TZif data from the [`jiff-tzdb`] crate. |
230 | | /// `jiff::tz::get` is more convenient and doesn't require managing your own |
231 | | /// TZif files, but it comes at the cost of a compile-time dependency on |
232 | | /// `jiff-tzdb` and being forced to use whatever data is in `jiff-tzdb`. |
233 | | /// |
234 | | /// # Input |
235 | | /// |
236 | | /// This macro takes two positional parameters that must be literal strings. |
237 | | /// |
238 | | /// The first is required and is a path to a file containing TZif data. For |
239 | | /// example, `/usr/share/zoneinfo/America/New_York`. |
240 | | /// |
241 | | /// The second parameter is an IANA time zone identifier, e.g., |
242 | | /// `America/New_York`, and is required only when an IANA time zone identifier |
243 | | /// could not be determined from the file path. The macro will automatically |
244 | | /// infer an IANA time zone identifier as anything after the last occurrence |
245 | | /// of the literal `zoneinfo/` in the file path. |
246 | | /// |
247 | | /// # Return type |
248 | | /// |
249 | | /// This macro returns a value with type `TimeZone`. To get a `&'static |
250 | | /// TimeZone`, simply use `&include!("...")`. |
251 | | /// |
252 | | /// # Usage |
253 | | /// |
254 | | /// Callers should only call this macro once for each unique IANA time zone |
255 | | /// identifier you need. Otherwise, multiple copies of the same embedded |
256 | | /// time zone data could appear in your binary. There are no correctness |
257 | | /// issues with this, but it could make your binary bigger than it needs to be. |
258 | | /// |
259 | | /// # When should I use this? |
260 | | /// |
261 | | /// Users should only use this macro if they have a _specific need_ for it |
262 | | /// (like using a time zone on an embedded device). In particular, this will |
263 | | /// embed the time zone transition rules into your binary. If the time zone |
264 | | /// rules change, your program will need to be re-compiled. |
265 | | /// |
266 | | /// In contrast, Jiff's default configuration on Unix is to read from |
267 | | /// `/usr/share/zoneinfo` at runtime. This means your application will |
268 | | /// automatically use time zone updates and doesn't need to be re-compiled. |
269 | | /// |
270 | | /// Using a static `TimeZone` may also be faster in some cases. In particular, |
271 | | /// a `TimeZone` created at runtime from a `/usr/share/zoneinfo` uses |
272 | | /// automatic reference counting internally. In contrast, a `TimeZone` created |
273 | | /// with this macro does not. |
274 | | /// |
275 | | /// # Example |
276 | | /// |
277 | | /// This example shows how to find the next DST transition from a particular |
278 | | /// timestamp in `Europe/Zurich`, and then print that in local time for Zurich: |
279 | | /// |
280 | | /// ```ignore |
281 | | /// use jiff::{tz::{self, TimeZone}, Timestamp}; |
282 | | /// |
283 | | /// static TZ: TimeZone = tz::include!("/usr/share/zoneinfo/Europe/Zurich"); |
284 | | /// |
285 | | /// let ts: Timestamp = "2025-02-25T00:00Z".parse()?; |
286 | | /// let Some(next_transition) = TZ.following(ts).next() else { |
287 | | /// return Err("no time zone transitions".into()); |
288 | | /// }; |
289 | | /// let zdt = next_transition.timestamp().to_zoned(TZ.clone()); |
290 | | /// assert_eq!(zdt.to_string(), "2025-03-30T03:00:00+02:00[Europe/Zurich]"); |
291 | | /// |
292 | | /// # Ok::<(), Box<dyn std::error::Error>>(()) |
293 | | /// ``` |
294 | | /// |
295 | | /// # Example: using `/etc/localtime` |
296 | | /// |
297 | | /// On most Unix systems, `/etc/localtime` is a symbolic link to a file in |
298 | | /// your `/usr/share/zoneinfo` directory. This means it is a valid input to |
299 | | /// this macro. However, Jiff currently does not detect the IANA time zone |
300 | | /// identifier, so you'll need to provide it yourself: |
301 | | /// |
302 | | /// ```ignore |
303 | | /// use jiff::{tz::{self, TimeZone}, Timestamp}; |
304 | | /// |
305 | | /// static TZ: TimeZone = tz::include!("/etc/localtime", "America/New_York"); |
306 | | /// |
307 | | /// let ts: Timestamp = "2025-02-25T00:00Z".parse()?; |
308 | | /// let zdt = ts.to_zoned(TZ.clone()); |
309 | | /// assert_eq!(zdt.to_string(), "2025-02-24T19:00:00-05:00[America/New_York]"); |
310 | | /// |
311 | | /// # Ok::<(), Box<dyn std::error::Error>>(()) |
312 | | /// ``` |
313 | | /// |
314 | | /// Note that this is reading `/etc/localtime` _at compile time_, which means |
315 | | /// that the program will only use the time zone on the system in which it |
316 | | /// was compiled. It will _not_ use the time zone of the system running it. |
317 | | /// |
318 | | /// [RFC 9636]: https://datatracker.ietf.org/doc/rfc9636/ |
319 | | /// [`jiff-tzdb`]: https://docs.rs/jiff-tzdb |
320 | | #[cfg(feature = "static-tz")] |
321 | | pub use jiff_static::include; |
322 | | |
323 | | /// Creates a new time zone offset in a `const` context from a given number |
324 | | /// of hours. |
325 | | /// |
326 | | /// Negative offsets correspond to time zones west of the prime meridian, |
327 | | /// while positive offsets correspond to time zones east of the prime |
328 | | /// meridian. Equivalently, in all cases, `civil-time - offset = UTC`. |
329 | | /// |
330 | | /// The fallible non-const version of this constructor is |
331 | | /// [`Offset::from_hours`]. |
332 | | /// |
333 | | /// This is a convenience free function for [`Offset::constant`]. It is |
334 | | /// intended to provide a terse syntax for constructing `Offset` values from |
335 | | /// a value that is known to be valid. |
336 | | /// |
337 | | /// # Panics |
338 | | /// |
339 | | /// This routine panics when the given number of hours is out of range. |
340 | | /// Namely, `hours` must be in the range `-25..=25`. |
341 | | /// |
342 | | /// Similarly, when used in a const context, an out of bounds hour will prevent |
343 | | /// your Rust program from compiling. |
344 | | /// |
345 | | /// # Example |
346 | | /// |
347 | | /// ``` |
348 | | /// use jiff::tz::offset; |
349 | | /// |
350 | | /// let o = offset(-5); |
351 | | /// assert_eq!(o.seconds(), -18_000); |
352 | | /// let o = offset(5); |
353 | | /// assert_eq!(o.seconds(), 18_000); |
354 | | /// ``` |
355 | | #[inline] |
356 | 0 | pub const fn offset(hours: i8) -> Offset { |
357 | 0 | Offset::constant(hours) |
358 | 0 | } |