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