1#![cfg_attr(not(any(feature = "std", test)), no_std)]
132#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
133
134#[cfg(feature = "serde")]
135mod serde;
136
137mod binary_search;
138mod directory;
139mod timezone_impl;
140mod timezones;
141
142pub use crate::directory::*;
143pub use crate::timezone_impl::{OffsetComponents, OffsetName};
144pub use crate::timezones::ParseError;
145pub use crate::timezones::Tz;
146pub use crate::timezones::TZ_VARIANTS;
147pub use crate::IANA_TZDB_VERSION;
148
149#[cfg(test)]
150mod tests {
151 use super::America::Danmarkshavn;
152 use super::Asia::Dhaka;
153 use super::Australia::Adelaide;
154 use super::Europe::Berlin;
155 use super::Europe::London;
156 use super::Europe::Moscow;
157 use super::Europe::Vilnius;
158 use super::Europe::Warsaw;
159 use super::Pacific::Apia;
160 use super::Pacific::Noumea;
161 use super::Pacific::Tahiti;
162 use super::Tz;
163 use super::IANA_TZDB_VERSION;
164 use super::US::Eastern;
165 use super::UTC;
166 use chrono::{Duration, NaiveDate, TimeZone};
167
168 #[test]
169 fn london_to_berlin() {
170 let dt = London.with_ymd_and_hms(2016, 10, 8, 17, 0, 0).unwrap();
171 let converted = dt.with_timezone(&Berlin);
172 let expected = Berlin.with_ymd_and_hms(2016, 10, 8, 18, 0, 0).unwrap();
173 assert_eq!(converted, expected);
174 }
175
176 #[test]
177 fn us_eastern_dst_commutativity() {
178 let dt = UTC.with_ymd_and_hms(2002, 4, 7, 7, 0, 0).unwrap();
179 for days in -420..720 {
180 let dt1 = (dt + Duration::days(days)).with_timezone(&Eastern);
181 let dt2 = dt.with_timezone(&Eastern) + Duration::days(days);
182 assert_eq!(dt1, dt2);
183 }
184 }
185
186 #[test]
187 fn test_addition_across_dst_boundary() {
188 use chrono::TimeZone;
189 let two_hours = Duration::hours(2);
190 let edt = Eastern.with_ymd_and_hms(2019, 11, 3, 0, 0, 0).unwrap();
191 let est = edt + two_hours;
192
193 assert_eq!(edt.to_string(), "2019-11-03 00:00:00 EDT".to_string());
194 assert_eq!(est.to_string(), "2019-11-03 01:00:00 EST".to_string());
195 assert_eq!(est.timestamp(), edt.timestamp() + two_hours.num_seconds());
196 }
197
198 #[test]
199 fn warsaw_tz_name() {
200 let dt = UTC.with_ymd_and_hms(1915, 8, 4, 22, 35, 59).unwrap();
201 assert_eq!(dt.with_timezone(&Warsaw).format("%Z").to_string(), "WMT");
202 let dt = dt + Duration::seconds(1);
203 assert_eq!(dt.with_timezone(&Warsaw).format("%Z").to_string(), "CET");
204 }
205
206 #[test]
207 fn vilnius_utc_offset() {
208 let dt = UTC.with_ymd_and_hms(1916, 12, 31, 22, 35, 59).unwrap().with_timezone(&Vilnius);
209 assert_eq!(dt, Vilnius.with_ymd_and_hms(1916, 12, 31, 23, 59, 59).unwrap());
210 let dt = dt + Duration::seconds(1);
211 assert_eq!(dt, Vilnius.with_ymd_and_hms(1917, 1, 1, 0, 11, 36).unwrap());
212 }
213
214 #[test]
215 fn victorian_times() {
216 let dt = UTC.with_ymd_and_hms(1847, 12, 1, 0, 1, 14).unwrap().with_timezone(&London);
217 assert_eq!(dt, London.with_ymd_and_hms(1847, 11, 30, 23, 59, 59).unwrap());
218 let dt = dt + Duration::seconds(1);
219 assert_eq!(dt, London.with_ymd_and_hms(1847, 12, 1, 0, 1, 15).unwrap());
220 }
221
222 #[test]
223 fn london_dst() {
224 let dt = London.with_ymd_and_hms(2016, 3, 10, 5, 0, 0).unwrap();
225 let later = dt + Duration::days(180);
226 let expected = London.with_ymd_and_hms(2016, 9, 6, 6, 0, 0).unwrap();
227 assert_eq!(later, expected);
228 }
229
230 #[test]
231 fn international_date_line_change() {
232 let dt = UTC.with_ymd_and_hms(2011, 12, 30, 9, 59, 59).unwrap().with_timezone(&Apia);
233 assert_eq!(dt, Apia.with_ymd_and_hms(2011, 12, 29, 23, 59, 59).unwrap());
234 let dt = dt + Duration::seconds(1);
235 assert_eq!(dt, Apia.with_ymd_and_hms(2011, 12, 31, 0, 0, 0).unwrap());
236 }
237
238 #[test]
239 fn negative_offset_with_minutes_and_seconds() {
240 let dt = UTC.with_ymd_and_hms(1900, 1, 1, 12, 0, 0).unwrap().with_timezone(&Danmarkshavn);
241 assert_eq!(dt, Danmarkshavn.with_ymd_and_hms(1900, 1, 1, 10, 45, 20).unwrap());
242 }
243
244 #[test]
245 fn monotonicity() {
246 let mut dt = Noumea.with_ymd_and_hms(1800, 1, 1, 12, 0, 0).unwrap();
247 for _ in 0..24 * 356 * 400 {
248 let new = dt + Duration::hours(1);
249 assert!(new > dt);
250 assert!(new.with_timezone(&UTC) > dt.with_timezone(&UTC));
251 dt = new;
252 }
253 }
254
255 fn test_inverse<T: TimeZone>(tz: T, begin: i32, end: i32) {
256 for y in begin..end {
257 for d in 1..366 {
258 let date = NaiveDate::from_yo_opt(y, d).unwrap();
259 for h in 0..24 {
260 for m in 0..60 {
261 let dt = date.and_hms_opt(h, m, 0).unwrap().and_utc();
262 let with_tz = dt.with_timezone(&tz);
263 let utc = with_tz.with_timezone(&UTC);
264 assert_eq!(dt, utc);
265 }
266 }
267 }
268 }
269 }
270
271 #[test]
272 fn inverse_london() {
273 test_inverse(London, 1989, 1994);
274 }
275
276 #[test]
277 fn inverse_dhaka() {
278 test_inverse(Dhaka, 1995, 2000);
279 }
280
281 #[test]
282 fn inverse_apia() {
283 test_inverse(Apia, 2011, 2012);
284 }
285
286 #[test]
287 fn inverse_tahiti() {
288 test_inverse(Tahiti, 1911, 1914);
289 }
290
291 #[test]
292 fn string_representation() {
293 let dt = UTC.with_ymd_and_hms(2000, 9, 1, 12, 30, 15).unwrap().with_timezone(&Adelaide);
294 assert_eq!(dt.to_string(), "2000-09-01 22:00:15 ACST");
295 assert_eq!(format!("{:?}", dt), "2000-09-01T22:00:15ACST");
296 assert_eq!(dt.to_rfc3339(), "2000-09-01T22:00:15+09:30");
297 assert_eq!(format!("{}", dt), "2000-09-01 22:00:15 ACST");
298 }
299
300 #[test]
301 fn tahiti() {
302 let dt = UTC.with_ymd_and_hms(1912, 10, 1, 9, 58, 16).unwrap().with_timezone(&Tahiti);
303 let before = dt - Duration::hours(1);
304 assert_eq!(before, Tahiti.with_ymd_and_hms(1912, 9, 30, 23, 0, 0).unwrap());
305 let after = dt + Duration::hours(1);
306 assert_eq!(after, Tahiti.with_ymd_and_hms(1912, 10, 1, 0, 58, 16).unwrap());
307 }
308
309 #[test]
310 fn nonexistent_time() {
311 assert!(London.with_ymd_and_hms(2016, 3, 27, 1, 30, 0).single().is_none());
312 }
313
314 #[test]
315 fn nonexistent_time_2() {
316 assert!(London.with_ymd_and_hms(2016, 3, 27, 1, 0, 0).single().is_none());
317 }
318
319 #[test]
320 fn time_exists() {
321 assert!(London.with_ymd_and_hms(2016, 3, 27, 2, 0, 0).single().is_some());
322 }
323
324 #[test]
325 fn ambiguous_time() {
326 let ambiguous = London.with_ymd_and_hms(2016, 10, 30, 1, 0, 0);
327 let earliest_utc =
328 NaiveDate::from_ymd_opt(2016, 10, 30).unwrap().and_hms_opt(0, 0, 0).unwrap();
329 assert_eq!(ambiguous.earliest().unwrap(), London.from_utc_datetime(&earliest_utc));
330 let latest_utc =
331 NaiveDate::from_ymd_opt(2016, 10, 30).unwrap().and_hms_opt(1, 0, 0).unwrap();
332 assert_eq!(ambiguous.latest().unwrap(), London.from_utc_datetime(&latest_utc));
333 }
334
335 #[test]
336 fn ambiguous_time_2() {
337 let ambiguous = London.with_ymd_and_hms(2016, 10, 30, 1, 30, 0);
338 let earliest_utc =
339 NaiveDate::from_ymd_opt(2016, 10, 30).unwrap().and_hms_opt(0, 30, 0).unwrap();
340 assert_eq!(ambiguous.earliest().unwrap(), London.from_utc_datetime(&earliest_utc));
341 let latest_utc =
342 NaiveDate::from_ymd_opt(2016, 10, 30).unwrap().and_hms_opt(1, 30, 0).unwrap();
343 assert_eq!(ambiguous.latest().unwrap(), London.from_utc_datetime(&latest_utc));
344 }
345
346 #[test]
347 fn ambiguous_time_3() {
348 let ambiguous = Moscow.with_ymd_and_hms(2014, 10, 26, 1, 30, 0);
349 let earliest_utc =
350 NaiveDate::from_ymd_opt(2014, 10, 25).unwrap().and_hms_opt(21, 30, 0).unwrap();
351 assert_eq!(
352 ambiguous.earliest().unwrap().fixed_offset(),
353 Moscow.from_utc_datetime(&earliest_utc).fixed_offset()
354 );
355 let latest_utc =
356 NaiveDate::from_ymd_opt(2014, 10, 25).unwrap().and_hms_opt(22, 30, 0).unwrap();
357 assert_eq!(ambiguous.latest().unwrap(), Moscow.from_utc_datetime(&latest_utc));
358 }
359
360 #[test]
361 fn ambiguous_time_4() {
362 let ambiguous = Moscow.with_ymd_and_hms(2014, 10, 26, 1, 0, 0);
363 let earliest_utc =
364 NaiveDate::from_ymd_opt(2014, 10, 25).unwrap().and_hms_opt(21, 0, 0).unwrap();
365 assert_eq!(
366 ambiguous.earliest().unwrap().fixed_offset(),
367 Moscow.from_utc_datetime(&earliest_utc).fixed_offset()
368 );
369 let latest_utc =
370 NaiveDate::from_ymd_opt(2014, 10, 25).unwrap().and_hms_opt(22, 0, 0).unwrap();
371 assert_eq!(ambiguous.latest().unwrap(), Moscow.from_utc_datetime(&latest_utc));
372 }
373
374 #[test]
375 fn unambiguous_time() {
376 assert!(London.with_ymd_and_hms(2016, 10, 30, 2, 0, 0).single().is_some());
377 }
378
379 #[test]
380 fn unambiguous_time_2() {
381 assert!(Moscow.with_ymd_and_hms(2014, 10, 26, 2, 0, 0).single().is_some());
382 }
383
384 #[test]
385 fn test_get_name() {
386 assert_eq!(London.name(), "Europe/London");
387 assert_eq!(Tz::Africa__Abidjan.name(), "Africa/Abidjan");
388 assert_eq!(Tz::UTC.name(), "UTC");
389 assert_eq!(Tz::Zulu.name(), "Zulu");
390 }
391
392 #[test]
393 fn test_display() {
394 assert_eq!(format!("{}", London), "Europe/London");
395 assert_eq!(format!("{}", Tz::Africa__Abidjan), "Africa/Abidjan");
396 assert_eq!(format!("{}", Tz::UTC), "UTC");
397 assert_eq!(format!("{}", Tz::Zulu), "Zulu");
398 }
399
400 #[test]
401 fn test_impl_hash() {
402 #[allow(dead_code)]
403 #[derive(Hash)]
404 struct Foo(Tz);
405 }
406
407 #[test]
408 fn test_iana_tzdb_version() {
409 assert_eq!(5, IANA_TZDB_VERSION.len());
411 let numbers: Vec<&str> = IANA_TZDB_VERSION.matches(char::is_numeric).collect();
412 assert_eq!(4, numbers.len());
413 assert!(IANA_TZDB_VERSION.ends_with(|c: char| c.is_ascii_lowercase()));
414 }
415}