chrono_tz/
lib.rs

1//! # Chrono-TZ
2//!
3//! `Chrono-TZ` is a library that provides implementors of the
4//! [`TimeZone`][timezone] trait for [`chrono`][chrono]. The
5//! impls are generated by a build script using the [`IANA database`][iana]
6//! and [`zoneinfo_parse`][zoneinfo_parse].
7//!
8//! [chrono]: https://github.com/lifthrasiir/rust-chrono
9//! [timezone]: https://lifthrasiir.github.io/rust-chrono/chrono/offset/trait.TimeZone.html
10//! [iana]: http://www.iana.org/time-zones
11//! [zoneinfo_parse]: https://github.com/rust-datetime/zoneinfo-parse
12//!
13//! ## Examples
14//!
15//! Create a time in one timezone and convert it to UTC
16//!
17//! ```
18//! # extern crate chrono;
19//! # extern crate chrono_tz;
20//! use chrono::{TimeZone, Utc};
21//! use chrono_tz::US::Pacific;
22//!
23//! # fn main() {
24//! let pacific_time = Pacific.ymd(1990, 5, 6).and_hms(12, 30, 45);
25//! let utc_time = pacific_time.with_timezone(&Utc);
26//! assert_eq!(utc_time, Utc.ymd(1990, 5, 6).and_hms(19, 30, 45));
27//! # }
28//! ```
29//!
30//! Create a naive datetime and convert it to a timezone-aware datetime
31//!
32//! ```
33//! # extern crate chrono;
34//! # extern crate chrono_tz;
35//! use chrono::{TimeZone, NaiveDate};
36//! use chrono_tz::Africa::Johannesburg;
37//!
38//! # fn main() {
39//! let naive_dt = NaiveDate::from_ymd(2038, 1, 19).and_hms(3, 14, 08);
40//! let tz_aware = Johannesburg.from_local_datetime(&naive_dt).unwrap();
41//! assert_eq!(tz_aware.to_string(), "2038-01-19 03:14:08 SAST");
42//! # }
43//! ```
44//!
45//! London and New York change their clocks on different days in March
46//! so only have a 4-hour difference on certain days.
47//!
48//! ```
49//! # extern crate chrono;
50//! # extern crate chrono_tz;
51//! use chrono::TimeZone;
52//! use chrono_tz::Europe::London;
53//! use chrono_tz::America::New_York;
54//!
55//! # fn main() {
56//! let london_time = London.ymd(2016, 3, 18).and_hms(3, 0, 0);
57//! let ny_time = london_time.with_timezone(&New_York);
58//! assert_eq!(ny_time, New_York.ymd(2016, 3, 17).and_hms(23, 0, 0));
59//! # }
60//! ```
61//!
62//! Adding 24 hours across a daylight savings change causes a change
63//! in local time
64//!
65//! ```
66//! # extern crate chrono;
67//! # extern crate chrono_tz;
68//! use chrono::{TimeZone, Duration};
69//! use chrono_tz::Europe::London;
70//!
71//! # fn main() {
72//! let dt = London.ymd(2016, 10, 29).and_hms(12, 0, 0);
73//! let later = dt + Duration::hours(24);
74//! assert_eq!(later, London.ymd(2016, 10, 30).and_hms(11, 0, 0));
75//! # }
76//! ```
77//!
78//! And of course you can always convert a local time to a unix timestamp
79//!
80//! ```
81//! # extern crate chrono;
82//! # extern crate chrono_tz;
83//! use chrono::TimeZone;
84//! use chrono_tz::Asia::Kolkata;
85//!
86//! # fn main() {
87//! let dt = Kolkata.ymd(2000, 1, 1).and_hms(0, 0, 0);
88//! let timestamp = dt.timestamp();
89//! assert_eq!(timestamp, 946665000);
90//! # }
91//! ```
92//!
93//! Pretty-printing a string will use the correct abbreviation for the timezone
94//!
95//! ```
96//! # extern crate chrono;
97//! # extern crate chrono_tz;
98//! use chrono::TimeZone;
99//! use chrono_tz::Europe::London;
100//!
101//! # fn main() {
102//! let dt = London.ymd(2016, 5, 10).and_hms(12, 0, 0);
103//! assert_eq!(dt.to_string(), "2016-05-10 12:00:00 BST");
104//! assert_eq!(dt.to_rfc3339(), "2016-05-10T12:00:00+01:00");
105//! # }
106//! ```
107//!
108//! You can convert a timezone string to a timezone using the FromStr trait
109//!
110//! ```
111//! # extern crate chrono;
112//! # extern crate chrono_tz;
113//! use chrono::TimeZone;
114//! use chrono_tz::Tz;
115//! use chrono_tz::UTC;
116//!
117//! # fn main() {
118//! let tz: Tz = "Antarctica/South_Pole".parse().unwrap();
119//! let dt = tz.ymd(2016, 10, 22).and_hms(12, 0, 0);
120//! let utc = dt.with_timezone(&UTC);
121//! assert_eq!(utc.to_string(), "2016-10-21 23:00:00 UTC");
122//! # }
123//! ```
124//!
125//! If you need to iterate over all variants you can use the TZ_VARIANTS array
126//! ```
127//! use chrono_tz::{TZ_VARIANTS, Tz};
128//! assert!(TZ_VARIANTS.iter().any(|v| *v == Tz::UTC));
129//! ```
130
131#![cfg_attr(not(feature = "std"), no_std)]
132
133#[cfg(feature = "std")]
134extern crate std as core;
135
136extern crate chrono;
137extern crate phf;
138#[cfg(feature = "case-insensitive")]
139extern crate uncased;
140
141#[cfg(feature = "serde")]
142mod serde;
143
144mod binary_search;
145mod directory;
146mod timezone_impl;
147mod timezones;
148
149pub use directory::*;
150pub use timezone_impl::{OffsetComponents, OffsetName};
151pub use timezones::ParseError;
152pub use timezones::Tz;
153pub use timezones::TZ_VARIANTS;
154
155#[cfg(test)]
156mod tests {
157    use super::America::Danmarkshavn;
158    use super::Asia::Dhaka;
159    use super::Australia::Adelaide;
160    use super::Europe::Berlin;
161    use super::Europe::London;
162    use super::Europe::Moscow;
163    use super::Europe::Vilnius;
164    use super::Europe::Warsaw;
165    use super::Pacific::Apia;
166    use super::Pacific::Noumea;
167    use super::Pacific::Tahiti;
168    use super::Tz;
169    use super::US::Eastern;
170    use super::UTC;
171    use chrono::{Duration, TimeZone};
172
173    #[test]
174    fn london_to_berlin() {
175        let dt = London.ymd(2016, 10, 8).and_hms(17, 0, 0);
176        let converted = dt.with_timezone(&Berlin);
177        let expected = Berlin.ymd(2016, 10, 8).and_hms(18, 0, 0);
178        assert_eq!(converted, expected);
179    }
180
181    #[test]
182    fn us_eastern_dst_commutativity() {
183        let dt = UTC.ymd(2002, 4, 7).and_hms(7, 0, 0);
184        for days in -420..720 {
185            let dt1 = (dt + Duration::days(days)).with_timezone(&Eastern);
186            let dt2 = dt.with_timezone(&Eastern) + Duration::days(days);
187            assert_eq!(dt1, dt2);
188        }
189    }
190
191    #[test]
192    fn test_addition_across_dst_boundary() {
193        use chrono::TimeZone;
194        let two_hours = Duration::hours(2);
195        let edt = Eastern.ymd(2019, 11, 3).and_hms(0, 0, 0);
196        let est = edt + two_hours;
197
198        assert_eq!(edt.to_string(), "2019-11-03 00:00:00 EDT".to_string());
199        assert_eq!(est.to_string(), "2019-11-03 01:00:00 EST".to_string());
200        assert_eq!(est.timestamp(), edt.timestamp() + two_hours.num_seconds());
201    }
202
203    #[test]
204    fn warsaw_tz_name() {
205        let dt = UTC.ymd(1915, 8, 4).and_hms(22, 35, 59);
206        assert_eq!(dt.with_timezone(&Warsaw).format("%Z").to_string(), "WMT");
207        let dt = dt + Duration::seconds(1);
208        assert_eq!(dt.with_timezone(&Warsaw).format("%Z").to_string(), "CET");
209    }
210
211    #[test]
212    fn vilnius_utc_offset() {
213        let dt = UTC.ymd(1916, 12, 31).and_hms(22, 35, 59).with_timezone(&Vilnius);
214        assert_eq!(dt, Vilnius.ymd(1916, 12, 31).and_hms(23, 59, 59));
215        let dt = dt + Duration::seconds(1);
216        assert_eq!(dt, Vilnius.ymd(1917, 1, 1).and_hms(0, 11, 36));
217    }
218
219    #[test]
220    fn victorian_times() {
221        let dt = UTC.ymd(1847, 12, 1).and_hms(0, 1, 14).with_timezone(&London);
222        assert_eq!(dt, London.ymd(1847, 11, 30).and_hms(23, 59, 59));
223        let dt = dt + Duration::seconds(1);
224        assert_eq!(dt, London.ymd(1847, 12, 1).and_hms(0, 1, 15));
225    }
226
227    #[test]
228    fn london_dst() {
229        let dt = London.ymd(2016, 3, 10).and_hms(5, 0, 0);
230        let later = dt + Duration::days(180);
231        let expected = London.ymd(2016, 9, 6).and_hms(6, 0, 0);
232        assert_eq!(later, expected);
233    }
234
235    #[test]
236    fn international_date_line_change() {
237        let dt = UTC.ymd(2011, 12, 30).and_hms(9, 59, 59).with_timezone(&Apia);
238        assert_eq!(dt, Apia.ymd(2011, 12, 29).and_hms(23, 59, 59));
239        let dt = dt + Duration::seconds(1);
240        assert_eq!(dt, Apia.ymd(2011, 12, 31).and_hms(0, 0, 0));
241    }
242
243    #[test]
244    fn negative_offset_with_minutes_and_seconds() {
245        let dt = UTC.ymd(1900, 1, 1).and_hms(12, 0, 0).with_timezone(&Danmarkshavn);
246        assert_eq!(dt, Danmarkshavn.ymd(1900, 1, 1).and_hms(10, 45, 20));
247    }
248
249    #[test]
250    fn monotonicity() {
251        let mut dt = Noumea.ymd(1800, 1, 1).and_hms(12, 0, 0);
252        for _ in 0..24 * 356 * 400 {
253            let new = dt + Duration::hours(1);
254            assert!(new > dt);
255            assert!(new.with_timezone(&UTC) > dt.with_timezone(&UTC));
256            dt = new;
257        }
258    }
259
260    fn test_inverse<T: TimeZone>(tz: T, begin: i32, end: i32) {
261        for y in begin..end {
262            for d in 1..366 {
263                for h in 0..24 {
264                    for m in 0..60 {
265                        let dt = UTC.yo(y, d).and_hms(h, m, 0);
266                        let with_tz = dt.with_timezone(&tz);
267                        let utc = with_tz.with_timezone(&UTC);
268                        assert_eq!(dt, utc);
269                    }
270                }
271            }
272        }
273    }
274
275    #[test]
276    fn inverse_london() {
277        test_inverse(London, 1989, 1994);
278    }
279
280    #[test]
281    fn inverse_dhaka() {
282        test_inverse(Dhaka, 1995, 2000);
283    }
284
285    #[test]
286    fn inverse_apia() {
287        test_inverse(Apia, 2011, 2012);
288    }
289
290    #[test]
291    fn inverse_tahiti() {
292        test_inverse(Tahiti, 1911, 1914);
293    }
294
295    #[test]
296    fn string_representation() {
297        let dt = UTC.ymd(2000, 9, 1).and_hms(12, 30, 15).with_timezone(&Adelaide);
298        assert_eq!(dt.to_string(), "2000-09-01 22:00:15 ACST");
299        assert_eq!(format!("{:?}", dt), "2000-09-01T22:00:15ACST");
300        assert_eq!(dt.to_rfc3339(), "2000-09-01T22:00:15+09:30");
301        assert_eq!(format!("{}", dt), "2000-09-01 22:00:15 ACST");
302    }
303
304    #[test]
305    fn tahiti() {
306        let dt = UTC.ymd(1912, 10, 1).and_hms(9, 58, 16).with_timezone(&Tahiti);
307        let before = dt - Duration::hours(1);
308        assert_eq!(before, Tahiti.ymd(1912, 9, 30).and_hms(23, 0, 0));
309        let after = dt + Duration::hours(1);
310        assert_eq!(after, Tahiti.ymd(1912, 10, 1).and_hms(0, 58, 16));
311    }
312
313    #[test]
314    #[should_panic]
315    fn nonexistent_time() {
316        let _ = London.ymd(2016, 3, 27).and_hms(1, 30, 0);
317    }
318
319    #[test]
320    #[should_panic]
321    fn nonexistent_time_2() {
322        let _ = London.ymd(2016, 3, 27).and_hms(1, 0, 0);
323    }
324
325    #[test]
326    fn time_exists() {
327        let _ = London.ymd(2016, 3, 27).and_hms(2, 0, 0);
328    }
329
330    #[test]
331    #[should_panic]
332    fn ambiguous_time() {
333        let _ = London.ymd(2016, 10, 30).and_hms(1, 0, 0);
334    }
335
336    #[test]
337    #[should_panic]
338    fn ambiguous_time_2() {
339        let _ = London.ymd(2016, 10, 30).and_hms(1, 30, 0);
340    }
341
342    #[test]
343    #[should_panic]
344    fn ambiguous_time_3() {
345        let _ = Moscow.ymd(2014, 10, 26).and_hms(1, 30, 0);
346    }
347
348    #[test]
349    #[should_panic]
350    fn ambiguous_time_4() {
351        let _ = Moscow.ymd(2014, 10, 26).and_hms(1, 0, 0);
352    }
353
354    #[test]
355    fn unambiguous_time() {
356        let _ = London.ymd(2016, 10, 30).and_hms(2, 0, 0);
357    }
358
359    #[test]
360    fn unambiguous_time_2() {
361        let _ = Moscow.ymd(2014, 10, 26).and_hms(2, 0, 0);
362    }
363
364    #[test]
365    fn test_get_name() {
366        assert_eq!(London.name(), "Europe/London");
367        assert_eq!(Tz::Africa__Abidjan.name(), "Africa/Abidjan");
368        assert_eq!(Tz::UTC.name(), "UTC");
369        assert_eq!(Tz::Zulu.name(), "Zulu");
370    }
371
372    #[test]
373    fn test_display() {
374        assert_eq!(format!("{}", London), "Europe/London");
375        assert_eq!(format!("{}", Tz::Africa__Abidjan), "Africa/Abidjan");
376        assert_eq!(format!("{}", Tz::UTC), "UTC");
377        assert_eq!(format!("{}", Tz::Zulu), "Zulu");
378    }
379
380    #[test]
381    fn test_impl_hash() {
382        #[allow(dead_code)]
383        #[derive(Hash)]
384        struct Foo(Tz);
385    }
386}