| 1 | // This is a part of Chrono. |
| 2 | // See README.md and LICENSE.txt for details. |
| 3 | |
| 4 | //! A collection of parsed date and time items. |
| 5 | //! They can be constructed incrementally while being checked for consistency. |
| 6 | |
| 7 | use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; |
| 8 | use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; |
| 9 | use crate::offset::{FixedOffset, MappedLocalTime, Offset, TimeZone}; |
| 10 | use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday}; |
| 11 | |
| 12 | /// A type to hold parsed fields of date and time that can check all fields are consistent. |
| 13 | /// |
| 14 | /// There are three classes of methods: |
| 15 | /// |
| 16 | /// - `set_*` methods to set fields you have available. They do a basic range check, and if the |
| 17 | /// same field is set more than once it is checked for consistency. |
| 18 | /// |
| 19 | /// - `to_*` methods try to make a concrete date and time value out of set fields. |
| 20 | /// They fully check that all fields are consistent and whether the date/datetime exists. |
| 21 | /// |
| 22 | /// - Methods to inspect the parsed fields. |
| 23 | /// |
| 24 | /// `Parsed` is used internally by all parsing functions in chrono. It is a public type so that it |
| 25 | /// can be used to write custom parsers that reuse the resolving algorithm, or to inspect the |
| 26 | /// results of a string parsed with chrono without converting it to concrete types. |
| 27 | /// |
| 28 | /// # Resolving algorithm |
| 29 | /// |
| 30 | /// Resolving date/time parts is littered with lots of corner cases, which is why common date/time |
| 31 | /// parsers do not implement it correctly. |
| 32 | /// |
| 33 | /// Chrono provides a complete resolution algorithm that checks all fields for consistency via the |
| 34 | /// `Parsed` type. |
| 35 | /// |
| 36 | /// As an easy example, consider RFC 2822. The [RFC 2822 date and time format] has a day of the week |
| 37 | /// part, which should be consistent with the other date parts. But a `strptime`-based parse would |
| 38 | /// happily accept inconsistent input: |
| 39 | /// |
| 40 | /// ```python |
| 41 | /// >>> import time |
| 42 | /// >>> time.strptime('Wed, 31 Dec 2014 04:26:40 +0000', |
| 43 | /// '%a, %d %b %Y %H:%M:%S +0000') |
| 44 | /// time.struct_time(tm_year=2014, tm_mon=12, tm_mday=31, |
| 45 | /// tm_hour=4, tm_min=26, tm_sec=40, |
| 46 | /// tm_wday=2, tm_yday=365, tm_isdst=-1) |
| 47 | /// >>> time.strptime('Thu, 31 Dec 2014 04:26:40 +0000', |
| 48 | /// '%a, %d %b %Y %H:%M:%S +0000') |
| 49 | /// time.struct_time(tm_year=2014, tm_mon=12, tm_mday=31, |
| 50 | /// tm_hour=4, tm_min=26, tm_sec=40, |
| 51 | /// tm_wday=3, tm_yday=365, tm_isdst=-1) |
| 52 | /// ``` |
| 53 | /// |
| 54 | /// [RFC 2822 date and time format]: https://tools.ietf.org/html/rfc2822#section-3.3 |
| 55 | /// |
| 56 | /// # Example |
| 57 | /// |
| 58 | /// Let's see how `Parsed` correctly detects the second RFC 2822 string from before is inconsistent. |
| 59 | /// |
| 60 | /// ``` |
| 61 | /// # #[cfg (feature = "alloc" )] { |
| 62 | /// use chrono::format::{ParseErrorKind, Parsed}; |
| 63 | /// use chrono::Weekday; |
| 64 | /// |
| 65 | /// let mut parsed = Parsed::new(); |
| 66 | /// parsed.set_weekday(Weekday::Wed)?; |
| 67 | /// parsed.set_day(31)?; |
| 68 | /// parsed.set_month(12)?; |
| 69 | /// parsed.set_year(2014)?; |
| 70 | /// parsed.set_hour(4)?; |
| 71 | /// parsed.set_minute(26)?; |
| 72 | /// parsed.set_second(40)?; |
| 73 | /// parsed.set_offset(0)?; |
| 74 | /// let dt = parsed.to_datetime()?; |
| 75 | /// assert_eq!(dt.to_rfc2822(), "Wed, 31 Dec 2014 04:26:40 +0000" ); |
| 76 | /// |
| 77 | /// let mut parsed = Parsed::new(); |
| 78 | /// parsed.set_weekday(Weekday::Thu)?; // changed to the wrong day |
| 79 | /// parsed.set_day(31)?; |
| 80 | /// parsed.set_month(12)?; |
| 81 | /// parsed.set_year(2014)?; |
| 82 | /// parsed.set_hour(4)?; |
| 83 | /// parsed.set_minute(26)?; |
| 84 | /// parsed.set_second(40)?; |
| 85 | /// parsed.set_offset(0)?; |
| 86 | /// let result = parsed.to_datetime(); |
| 87 | /// |
| 88 | /// assert!(result.is_err()); |
| 89 | /// if let Err(error) = result { |
| 90 | /// assert_eq!(error.kind(), ParseErrorKind::Impossible); |
| 91 | /// } |
| 92 | /// # } |
| 93 | /// # Ok::<(), chrono::ParseError>(()) |
| 94 | /// ``` |
| 95 | /// |
| 96 | /// The same using chrono's build-in parser for RFC 2822 (the [RFC2822 formatting item]) and |
| 97 | /// [`format::parse()`] showing how to inspect a field on failure. |
| 98 | /// |
| 99 | /// [RFC2822 formatting item]: crate::format::Fixed::RFC2822 |
| 100 | /// [`format::parse()`]: crate::format::parse() |
| 101 | /// |
| 102 | /// ``` |
| 103 | /// # #[cfg (feature = "alloc" )] { |
| 104 | /// use chrono::format::{parse, Fixed, Item, Parsed}; |
| 105 | /// use chrono::Weekday; |
| 106 | /// |
| 107 | /// let rfc_2822 = [Item::Fixed(Fixed::RFC2822)]; |
| 108 | /// |
| 109 | /// let mut parsed = Parsed::new(); |
| 110 | /// parse(&mut parsed, "Wed, 31 Dec 2014 04:26:40 +0000" , rfc_2822.iter())?; |
| 111 | /// let dt = parsed.to_datetime()?; |
| 112 | /// |
| 113 | /// assert_eq!(dt.to_rfc2822(), "Wed, 31 Dec 2014 04:26:40 +0000" ); |
| 114 | /// |
| 115 | /// let mut parsed = Parsed::new(); |
| 116 | /// parse(&mut parsed, "Thu, 31 Dec 2014 04:26:40 +0000" , rfc_2822.iter())?; |
| 117 | /// let result = parsed.to_datetime(); |
| 118 | /// |
| 119 | /// assert!(result.is_err()); |
| 120 | /// if result.is_err() { |
| 121 | /// // What is the weekday? |
| 122 | /// assert_eq!(parsed.weekday(), Some(Weekday::Thu)); |
| 123 | /// } |
| 124 | /// # } |
| 125 | /// # Ok::<(), chrono::ParseError>(()) |
| 126 | /// ``` |
| 127 | #[allow (clippy::manual_non_exhaustive)] |
| 128 | #[derive (Clone, PartialEq, Eq, Debug, Default, Hash)] |
| 129 | pub struct Parsed { |
| 130 | #[doc (hidden)] |
| 131 | pub year: Option<i32>, |
| 132 | #[doc (hidden)] |
| 133 | pub year_div_100: Option<i32>, |
| 134 | #[doc (hidden)] |
| 135 | pub year_mod_100: Option<i32>, |
| 136 | #[doc (hidden)] |
| 137 | pub isoyear: Option<i32>, |
| 138 | #[doc (hidden)] |
| 139 | pub isoyear_div_100: Option<i32>, |
| 140 | #[doc (hidden)] |
| 141 | pub isoyear_mod_100: Option<i32>, |
| 142 | #[doc (hidden)] |
| 143 | pub month: Option<u32>, |
| 144 | #[doc (hidden)] |
| 145 | pub week_from_sun: Option<u32>, |
| 146 | #[doc (hidden)] |
| 147 | pub week_from_mon: Option<u32>, |
| 148 | #[doc (hidden)] |
| 149 | pub isoweek: Option<u32>, |
| 150 | #[doc (hidden)] |
| 151 | pub weekday: Option<Weekday>, |
| 152 | #[doc (hidden)] |
| 153 | pub ordinal: Option<u32>, |
| 154 | #[doc (hidden)] |
| 155 | pub day: Option<u32>, |
| 156 | #[doc (hidden)] |
| 157 | pub hour_div_12: Option<u32>, |
| 158 | #[doc (hidden)] |
| 159 | pub hour_mod_12: Option<u32>, |
| 160 | #[doc (hidden)] |
| 161 | pub minute: Option<u32>, |
| 162 | #[doc (hidden)] |
| 163 | pub second: Option<u32>, |
| 164 | #[doc (hidden)] |
| 165 | pub nanosecond: Option<u32>, |
| 166 | #[doc (hidden)] |
| 167 | pub timestamp: Option<i64>, |
| 168 | #[doc (hidden)] |
| 169 | pub offset: Option<i32>, |
| 170 | #[doc (hidden)] |
| 171 | _dummy: (), |
| 172 | } |
| 173 | |
| 174 | /// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"), |
| 175 | /// and if it is empty, set `old` to `new` as well. |
| 176 | #[inline ] |
| 177 | fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> { |
| 178 | if let Some(ref old: &T) = *old { |
| 179 | if *old == new { |
| 180 | Ok(()) |
| 181 | } else { |
| 182 | Err(IMPOSSIBLE) |
| 183 | } |
| 184 | } else { |
| 185 | *old = Some(new); |
| 186 | Ok(()) |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | impl Parsed { |
| 191 | /// Returns the initial value of parsed parts. |
| 192 | #[must_use ] |
| 193 | pub fn new() -> Parsed { |
| 194 | Parsed::default() |
| 195 | } |
| 196 | |
| 197 | /// Set the [`year`](Parsed::year) field to the given value. |
| 198 | /// |
| 199 | /// The value can be negative, unlike the [`year_div_100`](Parsed::year_div_100) and |
| 200 | /// [`year_mod_100`](Parsed::year_mod_100) fields. |
| 201 | /// |
| 202 | /// # Errors |
| 203 | /// |
| 204 | /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. |
| 205 | /// |
| 206 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 207 | #[inline ] |
| 208 | pub fn set_year(&mut self, value: i64) -> ParseResult<()> { |
| 209 | set_if_consistent(&mut self.year, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) |
| 210 | } |
| 211 | |
| 212 | /// Set the [`year_div_100`](Parsed::year_div_100) field to the given value. |
| 213 | /// |
| 214 | /// # Errors |
| 215 | /// |
| 216 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`. |
| 217 | /// |
| 218 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 219 | #[inline ] |
| 220 | pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> { |
| 221 | if !(0..=i32::MAX as i64).contains(&value) { |
| 222 | return Err(OUT_OF_RANGE); |
| 223 | } |
| 224 | set_if_consistent(&mut self.year_div_100, value as i32) |
| 225 | } |
| 226 | |
| 227 | /// Set the [`year_mod_100`](Parsed::year_mod_100) field to the given value. |
| 228 | /// |
| 229 | /// When set it implies that the year is not negative. |
| 230 | /// |
| 231 | /// If this field is set while the [`year_div_100`](Parsed::year_div_100) field is missing (and |
| 232 | /// the full [`year`](Parsed::year) field is also not set), it assumes a default value for the |
| 233 | /// [`year_div_100`](Parsed::year_div_100) field. |
| 234 | /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise. |
| 235 | /// |
| 236 | /// # Errors |
| 237 | /// |
| 238 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99. |
| 239 | /// |
| 240 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 241 | #[inline ] |
| 242 | pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> { |
| 243 | if !(0..100).contains(&value) { |
| 244 | return Err(OUT_OF_RANGE); |
| 245 | } |
| 246 | set_if_consistent(&mut self.year_mod_100, value as i32) |
| 247 | } |
| 248 | |
| 249 | /// Set the [`isoyear`](Parsed::isoyear) field, that is part of an [ISO 8601 week date], to the |
| 250 | /// given value. |
| 251 | /// |
| 252 | /// The value can be negative, unlike the [`isoyear_div_100`](Parsed::isoyear_div_100) and |
| 253 | /// [`isoyear_mod_100`](Parsed::isoyear_mod_100) fields. |
| 254 | /// |
| 255 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 256 | /// |
| 257 | /// # Errors |
| 258 | /// |
| 259 | /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. |
| 260 | /// |
| 261 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 262 | #[inline ] |
| 263 | pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> { |
| 264 | set_if_consistent(&mut self.isoyear, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) |
| 265 | } |
| 266 | |
| 267 | /// Set the [`isoyear_div_100`](Parsed::isoyear_div_100) field, that is part of an |
| 268 | /// [ISO 8601 week date], to the given value. |
| 269 | /// |
| 270 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 271 | /// |
| 272 | /// # Errors |
| 273 | /// |
| 274 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`. |
| 275 | /// |
| 276 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 277 | #[inline ] |
| 278 | pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> { |
| 279 | if !(0..=i32::MAX as i64).contains(&value) { |
| 280 | return Err(OUT_OF_RANGE); |
| 281 | } |
| 282 | set_if_consistent(&mut self.isoyear_div_100, value as i32) |
| 283 | } |
| 284 | |
| 285 | /// Set the [`isoyear_mod_100`](Parsed::isoyear_mod_100) field, that is part of an |
| 286 | /// [ISO 8601 week date], to the given value. |
| 287 | /// |
| 288 | /// When set it implies that the year is not negative. |
| 289 | /// |
| 290 | /// If this field is set while the [`isoyear_div_100`](Parsed::isoyear_div_100) field is missing |
| 291 | /// (and the full [`isoyear`](Parsed::isoyear) field is also not set), it assumes a default |
| 292 | /// value for the [`isoyear_div_100`](Parsed::isoyear_div_100) field. |
| 293 | /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise. |
| 294 | /// |
| 295 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 296 | /// |
| 297 | /// # Errors |
| 298 | /// |
| 299 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99. |
| 300 | /// |
| 301 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 302 | #[inline ] |
| 303 | pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> { |
| 304 | if !(0..100).contains(&value) { |
| 305 | return Err(OUT_OF_RANGE); |
| 306 | } |
| 307 | set_if_consistent(&mut self.isoyear_mod_100, value as i32) |
| 308 | } |
| 309 | |
| 310 | /// Set the [`month`](Parsed::month) field to the given value. |
| 311 | /// |
| 312 | /// # Errors |
| 313 | /// |
| 314 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12. |
| 315 | /// |
| 316 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 317 | #[inline ] |
| 318 | pub fn set_month(&mut self, value: i64) -> ParseResult<()> { |
| 319 | if !(1..=12).contains(&value) { |
| 320 | return Err(OUT_OF_RANGE); |
| 321 | } |
| 322 | set_if_consistent(&mut self.month, value as u32) |
| 323 | } |
| 324 | |
| 325 | /// Set the [`week_from_sun`](Parsed::week_from_sun) week number field to the given value. |
| 326 | /// |
| 327 | /// Week 1 starts at the first Sunday of January. |
| 328 | /// |
| 329 | /// # Errors |
| 330 | /// |
| 331 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53. |
| 332 | /// |
| 333 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 334 | #[inline ] |
| 335 | pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> { |
| 336 | if !(0..=53).contains(&value) { |
| 337 | return Err(OUT_OF_RANGE); |
| 338 | } |
| 339 | set_if_consistent(&mut self.week_from_sun, value as u32) |
| 340 | } |
| 341 | |
| 342 | /// Set the [`week_from_mon`](Parsed::week_from_mon) week number field to the given value. |
| 343 | /// Set the 'week number starting with Monday' field to the given value. |
| 344 | /// |
| 345 | /// Week 1 starts at the first Monday of January. |
| 346 | /// |
| 347 | /// # Errors |
| 348 | /// |
| 349 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53. |
| 350 | /// |
| 351 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 352 | #[inline ] |
| 353 | pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> { |
| 354 | if !(0..=53).contains(&value) { |
| 355 | return Err(OUT_OF_RANGE); |
| 356 | } |
| 357 | set_if_consistent(&mut self.week_from_mon, value as u32) |
| 358 | } |
| 359 | |
| 360 | /// Set the [`isoweek`](Parsed::isoweek) field for an [ISO 8601 week date] to the given value. |
| 361 | /// |
| 362 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 363 | /// |
| 364 | /// # Errors |
| 365 | /// |
| 366 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-53. |
| 367 | /// |
| 368 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 369 | #[inline ] |
| 370 | pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> { |
| 371 | if !(1..=53).contains(&value) { |
| 372 | return Err(OUT_OF_RANGE); |
| 373 | } |
| 374 | set_if_consistent(&mut self.isoweek, value as u32) |
| 375 | } |
| 376 | |
| 377 | /// Set the [`weekday`](Parsed::weekday) field to the given value. |
| 378 | /// |
| 379 | /// # Errors |
| 380 | /// |
| 381 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 382 | #[inline ] |
| 383 | pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> { |
| 384 | set_if_consistent(&mut self.weekday, value) |
| 385 | } |
| 386 | |
| 387 | /// Set the [`ordinal`](Parsed::ordinal) (day of the year) field to the given value. |
| 388 | /// |
| 389 | /// # Errors |
| 390 | /// |
| 391 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-366. |
| 392 | /// |
| 393 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 394 | #[inline ] |
| 395 | pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> { |
| 396 | if !(1..=366).contains(&value) { |
| 397 | return Err(OUT_OF_RANGE); |
| 398 | } |
| 399 | set_if_consistent(&mut self.ordinal, value as u32) |
| 400 | } |
| 401 | |
| 402 | /// Set the [`day`](Parsed::day) of the month field to the given value. |
| 403 | /// |
| 404 | /// # Errors |
| 405 | /// |
| 406 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-31. |
| 407 | /// |
| 408 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 409 | #[inline ] |
| 410 | pub fn set_day(&mut self, value: i64) -> ParseResult<()> { |
| 411 | if !(1..=31).contains(&value) { |
| 412 | return Err(OUT_OF_RANGE); |
| 413 | } |
| 414 | set_if_consistent(&mut self.day, value as u32) |
| 415 | } |
| 416 | |
| 417 | /// Set the [`hour_div_12`](Parsed::hour_div_12) am/pm field to the given value. |
| 418 | /// |
| 419 | /// `false` indicates AM and `true` indicates PM. |
| 420 | /// |
| 421 | /// # Errors |
| 422 | /// |
| 423 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 424 | #[inline ] |
| 425 | pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> { |
| 426 | set_if_consistent(&mut self.hour_div_12, value as u32) |
| 427 | } |
| 428 | |
| 429 | /// Set the [`hour_mod_12`](Parsed::hour_mod_12) field, for the hour number in 12-hour clocks, |
| 430 | /// to the given value. |
| 431 | /// |
| 432 | /// Value must be in the canonical range of 1-12. |
| 433 | /// It will internally be stored as 0-11 (`value % 12`). |
| 434 | /// |
| 435 | /// # Errors |
| 436 | /// |
| 437 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12. |
| 438 | /// |
| 439 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 440 | #[inline ] |
| 441 | pub fn set_hour12(&mut self, mut value: i64) -> ParseResult<()> { |
| 442 | if !(1..=12).contains(&value) { |
| 443 | return Err(OUT_OF_RANGE); |
| 444 | } |
| 445 | if value == 12 { |
| 446 | value = 0 |
| 447 | } |
| 448 | set_if_consistent(&mut self.hour_mod_12, value as u32) |
| 449 | } |
| 450 | |
| 451 | /// Set the [`hour_div_12`](Parsed::hour_div_12) and [`hour_mod_12`](Parsed::hour_mod_12) |
| 452 | /// fields to the given value for a 24-hour clock. |
| 453 | /// |
| 454 | /// # Errors |
| 455 | /// |
| 456 | /// May return `OUT_OF_RANGE` if `value` is not in the range 0-23. |
| 457 | /// Currently only checks the value is not out of range for a `u32`. |
| 458 | /// |
| 459 | /// Returns `IMPOSSIBLE` one of the fields was already set to a different value. |
| 460 | #[inline ] |
| 461 | pub fn set_hour(&mut self, value: i64) -> ParseResult<()> { |
| 462 | let (hour_div_12, hour_mod_12) = match value { |
| 463 | hour @ 0..=11 => (0, hour as u32), |
| 464 | hour @ 12..=23 => (1, hour as u32 - 12), |
| 465 | _ => return Err(OUT_OF_RANGE), |
| 466 | }; |
| 467 | set_if_consistent(&mut self.hour_div_12, hour_div_12)?; |
| 468 | set_if_consistent(&mut self.hour_mod_12, hour_mod_12) |
| 469 | } |
| 470 | |
| 471 | /// Set the [`minute`](Parsed::minute) field to the given value. |
| 472 | /// |
| 473 | /// # Errors |
| 474 | /// |
| 475 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-59. |
| 476 | /// |
| 477 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 478 | #[inline ] |
| 479 | pub fn set_minute(&mut self, value: i64) -> ParseResult<()> { |
| 480 | if !(0..=59).contains(&value) { |
| 481 | return Err(OUT_OF_RANGE); |
| 482 | } |
| 483 | set_if_consistent(&mut self.minute, value as u32) |
| 484 | } |
| 485 | |
| 486 | /// Set the [`second`](Parsed::second) field to the given value. |
| 487 | /// |
| 488 | /// The value can be 60 in the case of a leap second. |
| 489 | /// |
| 490 | /// # Errors |
| 491 | /// |
| 492 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-60. |
| 493 | /// |
| 494 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 495 | #[inline ] |
| 496 | pub fn set_second(&mut self, value: i64) -> ParseResult<()> { |
| 497 | if !(0..=60).contains(&value) { |
| 498 | return Err(OUT_OF_RANGE); |
| 499 | } |
| 500 | set_if_consistent(&mut self.second, value as u32) |
| 501 | } |
| 502 | |
| 503 | /// Set the [`nanosecond`](Parsed::nanosecond) field to the given value. |
| 504 | /// |
| 505 | /// This is the number of nanoseconds since the whole second. |
| 506 | /// |
| 507 | /// # Errors |
| 508 | /// |
| 509 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-999,999,999. |
| 510 | /// |
| 511 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 512 | #[inline ] |
| 513 | pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> { |
| 514 | if !(0..=999_999_999).contains(&value) { |
| 515 | return Err(OUT_OF_RANGE); |
| 516 | } |
| 517 | set_if_consistent(&mut self.nanosecond, value as u32) |
| 518 | } |
| 519 | |
| 520 | /// Set the [`timestamp`](Parsed::timestamp) field to the given value. |
| 521 | /// |
| 522 | /// A Unix timestamp is defined as the number of non-leap seconds since midnight UTC on |
| 523 | /// January 1, 1970. |
| 524 | /// |
| 525 | /// # Errors |
| 526 | /// |
| 527 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 528 | #[inline ] |
| 529 | pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> { |
| 530 | set_if_consistent(&mut self.timestamp, value) |
| 531 | } |
| 532 | |
| 533 | /// Set the [`offset`](Parsed::offset) field to the given value. |
| 534 | /// |
| 535 | /// The offset is in seconds from local time to UTC. |
| 536 | /// |
| 537 | /// # Errors |
| 538 | /// |
| 539 | /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. |
| 540 | /// |
| 541 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
| 542 | #[inline ] |
| 543 | pub fn set_offset(&mut self, value: i64) -> ParseResult<()> { |
| 544 | set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) |
| 545 | } |
| 546 | |
| 547 | /// Returns a parsed naive date out of given fields. |
| 548 | /// |
| 549 | /// This method is able to determine the date from given subset of fields: |
| 550 | /// |
| 551 | /// - Year, month, day. |
| 552 | /// - Year, day of the year (ordinal). |
| 553 | /// - Year, week number counted from Sunday or Monday, day of the week. |
| 554 | /// - ISO week date. |
| 555 | /// |
| 556 | /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted, |
| 557 | /// the two-digit year is used to guess the century number then. |
| 558 | /// |
| 559 | /// It checks all given date fields are consistent with each other. |
| 560 | /// |
| 561 | /// # Errors |
| 562 | /// |
| 563 | /// This method returns: |
| 564 | /// - `IMPOSSIBLE` if any of the date fields conflict. |
| 565 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete date. |
| 566 | /// - `OUT_OF_RANGE` |
| 567 | /// - if any of the date fields of `Parsed` are set to a value beyond their acceptable range. |
| 568 | /// - if the value would be outside the range of a [`NaiveDate`]. |
| 569 | /// - if the date does not exist. |
| 570 | pub fn to_naive_date(&self) -> ParseResult<NaiveDate> { |
| 571 | fn resolve_year( |
| 572 | y: Option<i32>, |
| 573 | q: Option<i32>, |
| 574 | r: Option<i32>, |
| 575 | ) -> ParseResult<Option<i32>> { |
| 576 | match (y, q, r) { |
| 577 | // if there is no further information, simply return the given full year. |
| 578 | // this is a common case, so let's avoid division here. |
| 579 | (y, None, None) => Ok(y), |
| 580 | |
| 581 | // if there is a full year *and* also quotient and/or modulo, |
| 582 | // check if present quotient and/or modulo is consistent to the full year. |
| 583 | // since the presence of those fields means a positive full year, |
| 584 | // we should filter a negative full year first. |
| 585 | (Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => { |
| 586 | if y < 0 { |
| 587 | return Err(IMPOSSIBLE); |
| 588 | } |
| 589 | let q_ = y / 100; |
| 590 | let r_ = y % 100; |
| 591 | if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ { |
| 592 | Ok(Some(y)) |
| 593 | } else { |
| 594 | Err(IMPOSSIBLE) |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | // the full year is missing but we have quotient and modulo. |
| 599 | // reconstruct the full year. make sure that the result is always positive. |
| 600 | (None, Some(q), Some(r @ 0..=99)) => { |
| 601 | if q < 0 { |
| 602 | return Err(IMPOSSIBLE); |
| 603 | } |
| 604 | let y = q.checked_mul(100).and_then(|v| v.checked_add(r)); |
| 605 | Ok(Some(y.ok_or(OUT_OF_RANGE)?)) |
| 606 | } |
| 607 | |
| 608 | // we only have modulo. try to interpret a modulo as a conventional two-digit year. |
| 609 | // note: we are affected by Rust issue #18060. avoid multiple range patterns. |
| 610 | (None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })), |
| 611 | |
| 612 | // otherwise it is an out-of-bound or insufficient condition. |
| 613 | (None, Some(_), None) => Err(NOT_ENOUGH), |
| 614 | (_, _, Some(_)) => Err(OUT_OF_RANGE), |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?; |
| 619 | let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?; |
| 620 | |
| 621 | // verify the normal year-month-day date. |
| 622 | let verify_ymd = |date: NaiveDate| { |
| 623 | let year = date.year(); |
| 624 | let (year_div_100, year_mod_100) = if year >= 0 { |
| 625 | (Some(year / 100), Some(year % 100)) |
| 626 | } else { |
| 627 | (None, None) // they should be empty to be consistent |
| 628 | }; |
| 629 | let month = date.month(); |
| 630 | let day = date.day(); |
| 631 | self.year.unwrap_or(year) == year |
| 632 | && self.year_div_100.or(year_div_100) == year_div_100 |
| 633 | && self.year_mod_100.or(year_mod_100) == year_mod_100 |
| 634 | && self.month.unwrap_or(month) == month |
| 635 | && self.day.unwrap_or(day) == day |
| 636 | }; |
| 637 | |
| 638 | // verify the ISO week date. |
| 639 | let verify_isoweekdate = |date: NaiveDate| { |
| 640 | let week = date.iso_week(); |
| 641 | let isoyear = week.year(); |
| 642 | let isoweek = week.week(); |
| 643 | let weekday = date.weekday(); |
| 644 | let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 { |
| 645 | (Some(isoyear / 100), Some(isoyear % 100)) |
| 646 | } else { |
| 647 | (None, None) // they should be empty to be consistent |
| 648 | }; |
| 649 | self.isoyear.unwrap_or(isoyear) == isoyear |
| 650 | && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100 |
| 651 | && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100 |
| 652 | && self.isoweek.unwrap_or(isoweek) == isoweek |
| 653 | && self.weekday.unwrap_or(weekday) == weekday |
| 654 | }; |
| 655 | |
| 656 | // verify the ordinal and other (non-ISO) week dates. |
| 657 | let verify_ordinal = |date: NaiveDate| { |
| 658 | let ordinal = date.ordinal(); |
| 659 | let week_from_sun = date.weeks_from(Weekday::Sun); |
| 660 | let week_from_mon = date.weeks_from(Weekday::Mon); |
| 661 | self.ordinal.unwrap_or(ordinal) == ordinal |
| 662 | && self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun |
| 663 | && self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon |
| 664 | }; |
| 665 | |
| 666 | // test several possibilities. |
| 667 | // tries to construct a full `NaiveDate` as much as possible, then verifies that |
| 668 | // it is consistent with other given fields. |
| 669 | let (verified, parsed_date) = match (given_year, given_isoyear, self) { |
| 670 | (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => { |
| 671 | // year, month, day |
| 672 | let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?; |
| 673 | (verify_isoweekdate(date) && verify_ordinal(date), date) |
| 674 | } |
| 675 | |
| 676 | (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => { |
| 677 | // year, day of the year |
| 678 | let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?; |
| 679 | (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) |
| 680 | } |
| 681 | |
| 682 | (Some(year), _, &Parsed { week_from_sun: Some(week), weekday: Some(weekday), .. }) => { |
| 683 | // year, week (starting at 1st Sunday), day of the week |
| 684 | let date = resolve_week_date(year, week, weekday, Weekday::Sun)?; |
| 685 | (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) |
| 686 | } |
| 687 | |
| 688 | (Some(year), _, &Parsed { week_from_mon: Some(week), weekday: Some(weekday), .. }) => { |
| 689 | // year, week (starting at 1st Monday), day of the week |
| 690 | let date = resolve_week_date(year, week, weekday, Weekday::Mon)?; |
| 691 | (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) |
| 692 | } |
| 693 | |
| 694 | (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => { |
| 695 | // ISO year, week, day of the week |
| 696 | let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday); |
| 697 | let date = date.ok_or(OUT_OF_RANGE)?; |
| 698 | (verify_ymd(date) && verify_ordinal(date), date) |
| 699 | } |
| 700 | |
| 701 | (_, _, _) => return Err(NOT_ENOUGH), |
| 702 | }; |
| 703 | |
| 704 | if verified { |
| 705 | Ok(parsed_date) |
| 706 | } else { |
| 707 | Err(IMPOSSIBLE) |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | /// Returns a parsed naive time out of given fields. |
| 712 | /// |
| 713 | /// This method is able to determine the time from given subset of fields: |
| 714 | /// |
| 715 | /// - Hour, minute. (second and nanosecond assumed to be 0) |
| 716 | /// - Hour, minute, second. (nanosecond assumed to be 0) |
| 717 | /// - Hour, minute, second, nanosecond. |
| 718 | /// |
| 719 | /// It is able to handle leap seconds when given second is 60. |
| 720 | /// |
| 721 | /// # Errors |
| 722 | /// |
| 723 | /// This method returns: |
| 724 | /// - `OUT_OF_RANGE` if any of the time fields of `Parsed` are set to a value beyond |
| 725 | /// their acceptable range. |
| 726 | /// - `NOT_ENOUGH` if an hour field is missing, if AM/PM is missing in a 12-hour clock, |
| 727 | /// if minutes are missing, or if seconds are missing while the nanosecond field is present. |
| 728 | pub fn to_naive_time(&self) -> ParseResult<NaiveTime> { |
| 729 | let hour_div_12 = match self.hour_div_12 { |
| 730 | Some(v @ 0..=1) => v, |
| 731 | Some(_) => return Err(OUT_OF_RANGE), |
| 732 | None => return Err(NOT_ENOUGH), |
| 733 | }; |
| 734 | let hour_mod_12 = match self.hour_mod_12 { |
| 735 | Some(v @ 0..=11) => v, |
| 736 | Some(_) => return Err(OUT_OF_RANGE), |
| 737 | None => return Err(NOT_ENOUGH), |
| 738 | }; |
| 739 | let hour = hour_div_12 * 12 + hour_mod_12; |
| 740 | |
| 741 | let minute = match self.minute { |
| 742 | Some(v @ 0..=59) => v, |
| 743 | Some(_) => return Err(OUT_OF_RANGE), |
| 744 | None => return Err(NOT_ENOUGH), |
| 745 | }; |
| 746 | |
| 747 | // we allow omitting seconds or nanoseconds, but they should be in the range. |
| 748 | let (second, mut nano) = match self.second.unwrap_or(0) { |
| 749 | v @ 0..=59 => (v, 0), |
| 750 | 60 => (59, 1_000_000_000), |
| 751 | _ => return Err(OUT_OF_RANGE), |
| 752 | }; |
| 753 | nano += match self.nanosecond { |
| 754 | Some(v @ 0..=999_999_999) if self.second.is_some() => v, |
| 755 | Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing |
| 756 | Some(_) => return Err(OUT_OF_RANGE), |
| 757 | None => 0, |
| 758 | }; |
| 759 | |
| 760 | NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE) |
| 761 | } |
| 762 | |
| 763 | /// Returns a parsed naive date and time out of given fields, except for the offset field. |
| 764 | /// |
| 765 | /// The offset is assumed to have a given value. It is not compared against the offset field set |
| 766 | /// in the `Parsed` type, so it is allowed to be inconsistent. |
| 767 | /// |
| 768 | /// This method is able to determine the combined date and time from date and time fields or |
| 769 | /// from a single timestamp field. It checks all fields are consistent with each other. |
| 770 | /// |
| 771 | /// # Errors |
| 772 | /// |
| 773 | /// This method returns: |
| 774 | /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of |
| 775 | /// the other fields. |
| 776 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime. |
| 777 | /// - `OUT_OF_RANGE` |
| 778 | /// - if any of the date or time fields of `Parsed` are set to a value beyond their acceptable |
| 779 | /// range. |
| 780 | /// - if the value would be outside the range of a [`NaiveDateTime`]. |
| 781 | /// - if the date does not exist. |
| 782 | pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult<NaiveDateTime> { |
| 783 | let date = self.to_naive_date(); |
| 784 | let time = self.to_naive_time(); |
| 785 | if let (Ok(date), Ok(time)) = (date, time) { |
| 786 | let datetime = date.and_time(time); |
| 787 | |
| 788 | // verify the timestamp field if any |
| 789 | // the following is safe, `timestamp` is very limited in range |
| 790 | let timestamp = datetime.and_utc().timestamp() - i64::from(offset); |
| 791 | if let Some(given_timestamp) = self.timestamp { |
| 792 | // if `datetime` represents a leap second, it might be off by one second. |
| 793 | if given_timestamp != timestamp |
| 794 | && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1) |
| 795 | { |
| 796 | return Err(IMPOSSIBLE); |
| 797 | } |
| 798 | } |
| 799 | |
| 800 | Ok(datetime) |
| 801 | } else if let Some(timestamp) = self.timestamp { |
| 802 | use super::ParseError as PE; |
| 803 | use super::ParseErrorKind::{Impossible, OutOfRange}; |
| 804 | |
| 805 | // if date and time is problematic already, there is no point proceeding. |
| 806 | // we at least try to give a correct error though. |
| 807 | match (date, time) { |
| 808 | (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE), |
| 809 | (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE), |
| 810 | (_, _) => {} // one of them is insufficient |
| 811 | } |
| 812 | |
| 813 | // reconstruct date and time fields from timestamp |
| 814 | let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?; |
| 815 | let mut datetime = DateTime::from_timestamp(ts, 0).ok_or(OUT_OF_RANGE)?.naive_utc(); |
| 816 | |
| 817 | // fill year, ordinal, hour, minute and second fields from timestamp. |
| 818 | // if existing fields are consistent, this will allow the full date/time reconstruction. |
| 819 | let mut parsed = self.clone(); |
| 820 | if parsed.second == Some(60) { |
| 821 | // `datetime.second()` cannot be 60, so this is the only case for a leap second. |
| 822 | match datetime.second() { |
| 823 | // it's okay, just do not try to overwrite the existing field. |
| 824 | 59 => {} |
| 825 | // `datetime` is known to be off by one second. |
| 826 | 0 => { |
| 827 | datetime -= TimeDelta::try_seconds(1).unwrap(); |
| 828 | } |
| 829 | // otherwise it is impossible. |
| 830 | _ => return Err(IMPOSSIBLE), |
| 831 | } |
| 832 | // ...and we have the correct candidates for other fields. |
| 833 | } else { |
| 834 | parsed.set_second(i64::from(datetime.second()))?; |
| 835 | } |
| 836 | parsed.set_year(i64::from(datetime.year()))?; |
| 837 | parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd |
| 838 | parsed.set_hour(i64::from(datetime.hour()))?; |
| 839 | parsed.set_minute(i64::from(datetime.minute()))?; |
| 840 | |
| 841 | // validate other fields (e.g. week) and return |
| 842 | let date = parsed.to_naive_date()?; |
| 843 | let time = parsed.to_naive_time()?; |
| 844 | Ok(date.and_time(time)) |
| 845 | } else { |
| 846 | // reproduce the previous error(s) |
| 847 | date?; |
| 848 | time?; |
| 849 | unreachable!() |
| 850 | } |
| 851 | } |
| 852 | |
| 853 | /// Returns a parsed fixed time zone offset out of given fields. |
| 854 | /// |
| 855 | /// # Errors |
| 856 | /// |
| 857 | /// This method returns: |
| 858 | /// - `OUT_OF_RANGE` if the offset is out of range for a `FixedOffset`. |
| 859 | /// - `NOT_ENOUGH` if the offset field is not set. |
| 860 | pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> { |
| 861 | FixedOffset::east_opt(self.offset.ok_or(NOT_ENOUGH)?).ok_or(OUT_OF_RANGE) |
| 862 | } |
| 863 | |
| 864 | /// Returns a parsed timezone-aware date and time out of given fields. |
| 865 | /// |
| 866 | /// This method is able to determine the combined date and time from date, time and offset |
| 867 | /// fields, and/or from a single timestamp field. It checks all fields are consistent with each |
| 868 | /// other. |
| 869 | /// |
| 870 | /// # Errors |
| 871 | /// |
| 872 | /// This method returns: |
| 873 | /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of |
| 874 | /// the other fields. |
| 875 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime |
| 876 | /// including offset from UTC. |
| 877 | /// - `OUT_OF_RANGE` |
| 878 | /// - if any of the fields of `Parsed` are set to a value beyond their acceptable |
| 879 | /// range. |
| 880 | /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`]. |
| 881 | /// - if the date does not exist. |
| 882 | pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> { |
| 883 | // If there is no explicit offset, consider a timestamp value as indication of a UTC value. |
| 884 | let offset = match (self.offset, self.timestamp) { |
| 885 | (Some(off), _) => off, |
| 886 | (None, Some(_)) => 0, // UNIX timestamp may assume 0 offset |
| 887 | (None, None) => return Err(NOT_ENOUGH), |
| 888 | }; |
| 889 | let datetime = self.to_naive_datetime_with_offset(offset)?; |
| 890 | let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?; |
| 891 | |
| 892 | match offset.from_local_datetime(&datetime) { |
| 893 | MappedLocalTime::None => Err(IMPOSSIBLE), |
| 894 | MappedLocalTime::Single(t) => Ok(t), |
| 895 | MappedLocalTime::Ambiguous(..) => Err(NOT_ENOUGH), |
| 896 | } |
| 897 | } |
| 898 | |
| 899 | /// Returns a parsed timezone-aware date and time out of given fields, |
| 900 | /// with an additional [`TimeZone`] used to interpret and validate the local date. |
| 901 | /// |
| 902 | /// This method is able to determine the combined date and time from date and time, and/or from |
| 903 | /// a single timestamp field. It checks all fields are consistent with each other. |
| 904 | /// |
| 905 | /// If the parsed fields include an UTC offset, it also has to be consistent with the offset in |
| 906 | /// the provided `tz` time zone for that datetime. |
| 907 | /// |
| 908 | /// # Errors |
| 909 | /// |
| 910 | /// This method returns: |
| 911 | /// - `IMPOSSIBLE` |
| 912 | /// - if any of the date fields conflict, if a timestamp conflicts with any of the other |
| 913 | /// fields, or if the offset field is set but differs from the offset at that time in the |
| 914 | /// `tz` time zone. |
| 915 | /// - if the local datetime does not exists in the provided time zone (because it falls in a |
| 916 | /// transition due to for example DST). |
| 917 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime, or if |
| 918 | /// the local time in the provided time zone is ambiguous (because it falls in a transition |
| 919 | /// due to for example DST) while there is no offset field or timestamp field set. |
| 920 | /// - `OUT_OF_RANGE` |
| 921 | /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`]. |
| 922 | /// - if any of the fields of `Parsed` are set to a value beyond their acceptable range. |
| 923 | /// - if the date does not exist. |
| 924 | pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> { |
| 925 | // if we have `timestamp` specified, guess an offset from that. |
| 926 | let mut guessed_offset = 0; |
| 927 | if let Some(timestamp) = self.timestamp { |
| 928 | // make a naive `DateTime` from given timestamp and (if any) nanosecond. |
| 929 | // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine. |
| 930 | let nanosecond = self.nanosecond.unwrap_or(0); |
| 931 | let dt = |
| 932 | DateTime::from_timestamp(timestamp, nanosecond).ok_or(OUT_OF_RANGE)?.naive_utc(); |
| 933 | guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc(); |
| 934 | } |
| 935 | |
| 936 | // checks if the given `DateTime` has a consistent `Offset` with given `self.offset`. |
| 937 | let check_offset = |dt: &DateTime<Tz>| { |
| 938 | if let Some(offset) = self.offset { |
| 939 | dt.offset().fix().local_minus_utc() == offset |
| 940 | } else { |
| 941 | true |
| 942 | } |
| 943 | }; |
| 944 | |
| 945 | // `guessed_offset` should be correct when `self.timestamp` is given. |
| 946 | // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case. |
| 947 | let datetime = self.to_naive_datetime_with_offset(guessed_offset)?; |
| 948 | match tz.from_local_datetime(&datetime) { |
| 949 | MappedLocalTime::None => Err(IMPOSSIBLE), |
| 950 | MappedLocalTime::Single(t) => { |
| 951 | if check_offset(&t) { |
| 952 | Ok(t) |
| 953 | } else { |
| 954 | Err(IMPOSSIBLE) |
| 955 | } |
| 956 | } |
| 957 | MappedLocalTime::Ambiguous(min, max) => { |
| 958 | // try to disambiguate two possible local dates by offset. |
| 959 | match (check_offset(&min), check_offset(&max)) { |
| 960 | (false, false) => Err(IMPOSSIBLE), |
| 961 | (false, true) => Ok(max), |
| 962 | (true, false) => Ok(min), |
| 963 | (true, true) => Err(NOT_ENOUGH), |
| 964 | } |
| 965 | } |
| 966 | } |
| 967 | } |
| 968 | |
| 969 | /// Get the `year` field if set. |
| 970 | /// |
| 971 | /// See also [`set_year()`](Parsed::set_year). |
| 972 | #[inline ] |
| 973 | pub fn year(&self) -> Option<i32> { |
| 974 | self.year |
| 975 | } |
| 976 | |
| 977 | /// Get the `year_div_100` field if set. |
| 978 | /// |
| 979 | /// See also [`set_year_div_100()`](Parsed::set_year_div_100). |
| 980 | #[inline ] |
| 981 | pub fn year_div_100(&self) -> Option<i32> { |
| 982 | self.year_div_100 |
| 983 | } |
| 984 | |
| 985 | /// Get the `year_mod_100` field if set. |
| 986 | /// |
| 987 | /// See also [`set_year_mod_100()`](Parsed::set_year_mod_100). |
| 988 | #[inline ] |
| 989 | pub fn year_mod_100(&self) -> Option<i32> { |
| 990 | self.year_mod_100 |
| 991 | } |
| 992 | |
| 993 | /// Get the `isoyear` field that is part of an [ISO 8601 week date] if set. |
| 994 | /// |
| 995 | /// See also [`set_isoyear()`](Parsed::set_isoyear). |
| 996 | /// |
| 997 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 998 | #[inline ] |
| 999 | pub fn isoyear(&self) -> Option<i32> { |
| 1000 | self.isoyear |
| 1001 | } |
| 1002 | |
| 1003 | /// Get the `isoyear_div_100` field that is part of an [ISO 8601 week date] if set. |
| 1004 | /// |
| 1005 | /// See also [`set_isoyear_div_100()`](Parsed::set_isoyear_div_100). |
| 1006 | /// |
| 1007 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 1008 | #[inline ] |
| 1009 | pub fn isoyear_div_100(&self) -> Option<i32> { |
| 1010 | self.isoyear_div_100 |
| 1011 | } |
| 1012 | |
| 1013 | /// Get the `isoyear_mod_100` field that is part of an [ISO 8601 week date] if set. |
| 1014 | /// |
| 1015 | /// See also [`set_isoyear_mod_100()`](Parsed::set_isoyear_mod_100). |
| 1016 | /// |
| 1017 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 1018 | #[inline ] |
| 1019 | pub fn isoyear_mod_100(&self) -> Option<i32> { |
| 1020 | self.isoyear_mod_100 |
| 1021 | } |
| 1022 | |
| 1023 | /// Get the `month` field if set. |
| 1024 | /// |
| 1025 | /// See also [`set_month()`](Parsed::set_month). |
| 1026 | #[inline ] |
| 1027 | pub fn month(&self) -> Option<u32> { |
| 1028 | self.month |
| 1029 | } |
| 1030 | |
| 1031 | /// Get the `week_from_sun` field if set. |
| 1032 | /// |
| 1033 | /// See also [`set_week_from_sun()`](Parsed::set_week_from_sun). |
| 1034 | #[inline ] |
| 1035 | pub fn week_from_sun(&self) -> Option<u32> { |
| 1036 | self.week_from_sun |
| 1037 | } |
| 1038 | |
| 1039 | /// Get the `week_from_mon` field if set. |
| 1040 | /// |
| 1041 | /// See also [`set_week_from_mon()`](Parsed::set_week_from_mon). |
| 1042 | #[inline ] |
| 1043 | pub fn week_from_mon(&self) -> Option<u32> { |
| 1044 | self.week_from_mon |
| 1045 | } |
| 1046 | |
| 1047 | /// Get the `isoweek` field that is part of an [ISO 8601 week date] if set. |
| 1048 | /// |
| 1049 | /// See also [`set_isoweek()`](Parsed::set_isoweek). |
| 1050 | /// |
| 1051 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
| 1052 | #[inline ] |
| 1053 | pub fn isoweek(&self) -> Option<u32> { |
| 1054 | self.isoweek |
| 1055 | } |
| 1056 | |
| 1057 | /// Get the `weekday` field if set. |
| 1058 | /// |
| 1059 | /// See also [`set_weekday()`](Parsed::set_weekday). |
| 1060 | #[inline ] |
| 1061 | pub fn weekday(&self) -> Option<Weekday> { |
| 1062 | self.weekday |
| 1063 | } |
| 1064 | |
| 1065 | /// Get the `ordinal` (day of the year) field if set. |
| 1066 | /// |
| 1067 | /// See also [`set_ordinal()`](Parsed::set_ordinal). |
| 1068 | #[inline ] |
| 1069 | pub fn ordinal(&self) -> Option<u32> { |
| 1070 | self.ordinal |
| 1071 | } |
| 1072 | |
| 1073 | /// Get the `day` of the month field if set. |
| 1074 | /// |
| 1075 | /// See also [`set_day()`](Parsed::set_day). |
| 1076 | #[inline ] |
| 1077 | pub fn day(&self) -> Option<u32> { |
| 1078 | self.day |
| 1079 | } |
| 1080 | |
| 1081 | /// Get the `hour_div_12` field (am/pm) if set. |
| 1082 | /// |
| 1083 | /// 0 indicates AM and 1 indicates PM. |
| 1084 | /// |
| 1085 | /// See also [`set_ampm()`](Parsed::set_ampm) and [`set_hour()`](Parsed::set_hour). |
| 1086 | #[inline ] |
| 1087 | pub fn hour_div_12(&self) -> Option<u32> { |
| 1088 | self.hour_div_12 |
| 1089 | } |
| 1090 | |
| 1091 | /// Get the `hour_mod_12` field if set. |
| 1092 | /// |
| 1093 | /// See also [`set_hour12()`](Parsed::set_hour12) and [`set_hour()`](Parsed::set_hour). |
| 1094 | pub fn hour_mod_12(&self) -> Option<u32> { |
| 1095 | self.hour_mod_12 |
| 1096 | } |
| 1097 | |
| 1098 | /// Get the `minute` field if set. |
| 1099 | /// |
| 1100 | /// See also [`set_minute()`](Parsed::set_minute). |
| 1101 | #[inline ] |
| 1102 | pub fn minute(&self) -> Option<u32> { |
| 1103 | self.minute |
| 1104 | } |
| 1105 | |
| 1106 | /// Get the `second` field if set. |
| 1107 | /// |
| 1108 | /// See also [`set_second()`](Parsed::set_second). |
| 1109 | #[inline ] |
| 1110 | pub fn second(&self) -> Option<u32> { |
| 1111 | self.second |
| 1112 | } |
| 1113 | |
| 1114 | /// Get the `nanosecond` field if set. |
| 1115 | /// |
| 1116 | /// See also [`set_nanosecond()`](Parsed::set_nanosecond). |
| 1117 | #[inline ] |
| 1118 | pub fn nanosecond(&self) -> Option<u32> { |
| 1119 | self.nanosecond |
| 1120 | } |
| 1121 | |
| 1122 | /// Get the `timestamp` field if set. |
| 1123 | /// |
| 1124 | /// See also [`set_timestamp()`](Parsed::set_timestamp). |
| 1125 | #[inline ] |
| 1126 | pub fn timestamp(&self) -> Option<i64> { |
| 1127 | self.timestamp |
| 1128 | } |
| 1129 | |
| 1130 | /// Get the `offset` field if set. |
| 1131 | /// |
| 1132 | /// See also [`set_offset()`](Parsed::set_offset). |
| 1133 | #[inline ] |
| 1134 | pub fn offset(&self) -> Option<i32> { |
| 1135 | self.offset |
| 1136 | } |
| 1137 | } |
| 1138 | |
| 1139 | /// Create a `NaiveDate` when given a year, week, weekday, and the definition at which day of the |
| 1140 | /// week a week starts. |
| 1141 | /// |
| 1142 | /// Returns `IMPOSSIBLE` if `week` is `0` or `53` and the `weekday` falls outside the year. |
| 1143 | fn resolve_week_date( |
| 1144 | year: i32, |
| 1145 | week: u32, |
| 1146 | weekday: Weekday, |
| 1147 | week_start_day: Weekday, |
| 1148 | ) -> ParseResult<NaiveDate> { |
| 1149 | if week > 53 { |
| 1150 | return Err(OUT_OF_RANGE); |
| 1151 | } |
| 1152 | |
| 1153 | let first_day_of_year: NaiveDate = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?; |
| 1154 | // Ordinal of the day at which week 1 starts. |
| 1155 | let first_week_start: i32 = 1 + week_start_day.days_since(first_day_of_year.weekday()) as i32; |
| 1156 | // Number of the `weekday`, which is 0 for the first day of the week. |
| 1157 | let weekday: i32 = weekday.days_since(week_start_day) as i32; |
| 1158 | let ordinal: i32 = first_week_start + (week as i32 - 1) * 7 + weekday; |
| 1159 | if ordinal <= 0 { |
| 1160 | return Err(IMPOSSIBLE); |
| 1161 | } |
| 1162 | first_day_of_year.with_ordinal(ordinal as u32).ok_or(IMPOSSIBLE) |
| 1163 | } |
| 1164 | |
| 1165 | #[cfg (test)] |
| 1166 | mod tests { |
| 1167 | use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; |
| 1168 | use super::Parsed; |
| 1169 | use crate::naive::{NaiveDate, NaiveTime}; |
| 1170 | use crate::offset::{FixedOffset, TimeZone, Utc}; |
| 1171 | use crate::Datelike; |
| 1172 | use crate::Weekday::*; |
| 1173 | |
| 1174 | #[test ] |
| 1175 | fn test_parsed_set_fields() { |
| 1176 | // year*, isoyear* |
| 1177 | let mut p = Parsed::new(); |
| 1178 | assert_eq!(p.set_year(1987), Ok(())); |
| 1179 | assert_eq!(p.set_year(1986), Err(IMPOSSIBLE)); |
| 1180 | assert_eq!(p.set_year(1988), Err(IMPOSSIBLE)); |
| 1181 | assert_eq!(p.set_year(1987), Ok(())); |
| 1182 | assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year` |
| 1183 | assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE)); |
| 1184 | assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE)); |
| 1185 | assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto |
| 1186 | assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE)); |
| 1187 | assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE)); |
| 1188 | |
| 1189 | let mut p = Parsed::new(); |
| 1190 | assert_eq!(p.set_year(0), Ok(())); |
| 1191 | assert_eq!(p.set_year_div_100(0), Ok(())); |
| 1192 | assert_eq!(p.set_year_mod_100(0), Ok(())); |
| 1193 | |
| 1194 | let mut p = Parsed::new(); |
| 1195 | assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE)); |
| 1196 | assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE)); |
| 1197 | assert_eq!(p.set_year(-1), Ok(())); |
| 1198 | assert_eq!(p.set_year(-2), Err(IMPOSSIBLE)); |
| 1199 | assert_eq!(p.set_year(0), Err(IMPOSSIBLE)); |
| 1200 | |
| 1201 | let mut p = Parsed::new(); |
| 1202 | assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); |
| 1203 | assert_eq!(p.set_year_div_100(8), Ok(())); |
| 1204 | assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); |
| 1205 | |
| 1206 | // month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset |
| 1207 | let mut p = Parsed::new(); |
| 1208 | assert_eq!(p.set_month(7), Ok(())); |
| 1209 | assert_eq!(p.set_month(1), Err(IMPOSSIBLE)); |
| 1210 | assert_eq!(p.set_month(6), Err(IMPOSSIBLE)); |
| 1211 | assert_eq!(p.set_month(8), Err(IMPOSSIBLE)); |
| 1212 | assert_eq!(p.set_month(12), Err(IMPOSSIBLE)); |
| 1213 | |
| 1214 | let mut p = Parsed::new(); |
| 1215 | assert_eq!(p.set_month(8), Ok(())); |
| 1216 | assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE)); |
| 1217 | |
| 1218 | // hour |
| 1219 | let mut p = Parsed::new(); |
| 1220 | assert_eq!(p.set_hour(12), Ok(())); |
| 1221 | assert_eq!(p.set_hour(11), Err(IMPOSSIBLE)); |
| 1222 | assert_eq!(p.set_hour(13), Err(IMPOSSIBLE)); |
| 1223 | assert_eq!(p.set_hour(12), Ok(())); |
| 1224 | assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE)); |
| 1225 | assert_eq!(p.set_ampm(true), Ok(())); |
| 1226 | assert_eq!(p.set_hour12(12), Ok(())); |
| 1227 | assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation |
| 1228 | assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE)); |
| 1229 | assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE)); |
| 1230 | |
| 1231 | let mut p = Parsed::new(); |
| 1232 | assert_eq!(p.set_ampm(true), Ok(())); |
| 1233 | assert_eq!(p.set_hour12(7), Ok(())); |
| 1234 | assert_eq!(p.set_hour(7), Err(IMPOSSIBLE)); |
| 1235 | assert_eq!(p.set_hour(18), Err(IMPOSSIBLE)); |
| 1236 | assert_eq!(p.set_hour(19), Ok(())); |
| 1237 | |
| 1238 | // timestamp |
| 1239 | let mut p = Parsed::new(); |
| 1240 | assert_eq!(p.set_timestamp(1_234_567_890), Ok(())); |
| 1241 | assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE)); |
| 1242 | assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE)); |
| 1243 | } |
| 1244 | |
| 1245 | #[test ] |
| 1246 | fn test_parsed_set_range() { |
| 1247 | assert_eq!(Parsed::new().set_year(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); |
| 1248 | assert!(Parsed::new().set_year(i32::MIN as i64).is_ok()); |
| 1249 | assert!(Parsed::new().set_year(i32::MAX as i64).is_ok()); |
| 1250 | assert_eq!(Parsed::new().set_year(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
| 1251 | |
| 1252 | assert_eq!(Parsed::new().set_year_div_100(-1), Err(OUT_OF_RANGE)); |
| 1253 | assert!(Parsed::new().set_year_div_100(0).is_ok()); |
| 1254 | assert!(Parsed::new().set_year_div_100(i32::MAX as i64).is_ok()); |
| 1255 | assert_eq!(Parsed::new().set_year_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
| 1256 | |
| 1257 | assert_eq!(Parsed::new().set_year_mod_100(-1), Err(OUT_OF_RANGE)); |
| 1258 | assert!(Parsed::new().set_year_mod_100(0).is_ok()); |
| 1259 | assert!(Parsed::new().set_year_mod_100(99).is_ok()); |
| 1260 | assert_eq!(Parsed::new().set_year_mod_100(100), Err(OUT_OF_RANGE)); |
| 1261 | |
| 1262 | assert_eq!(Parsed::new().set_isoyear(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); |
| 1263 | assert!(Parsed::new().set_isoyear(i32::MIN as i64).is_ok()); |
| 1264 | assert!(Parsed::new().set_isoyear(i32::MAX as i64).is_ok()); |
| 1265 | assert_eq!(Parsed::new().set_isoyear(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
| 1266 | |
| 1267 | assert_eq!(Parsed::new().set_isoyear_div_100(-1), Err(OUT_OF_RANGE)); |
| 1268 | assert!(Parsed::new().set_isoyear_div_100(0).is_ok()); |
| 1269 | assert!(Parsed::new().set_isoyear_div_100(99).is_ok()); |
| 1270 | assert_eq!(Parsed::new().set_isoyear_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
| 1271 | |
| 1272 | assert_eq!(Parsed::new().set_isoyear_mod_100(-1), Err(OUT_OF_RANGE)); |
| 1273 | assert!(Parsed::new().set_isoyear_mod_100(0).is_ok()); |
| 1274 | assert!(Parsed::new().set_isoyear_mod_100(99).is_ok()); |
| 1275 | assert_eq!(Parsed::new().set_isoyear_mod_100(100), Err(OUT_OF_RANGE)); |
| 1276 | |
| 1277 | assert_eq!(Parsed::new().set_month(0), Err(OUT_OF_RANGE)); |
| 1278 | assert!(Parsed::new().set_month(1).is_ok()); |
| 1279 | assert!(Parsed::new().set_month(12).is_ok()); |
| 1280 | assert_eq!(Parsed::new().set_month(13), Err(OUT_OF_RANGE)); |
| 1281 | |
| 1282 | assert_eq!(Parsed::new().set_week_from_sun(-1), Err(OUT_OF_RANGE)); |
| 1283 | assert!(Parsed::new().set_week_from_sun(0).is_ok()); |
| 1284 | assert!(Parsed::new().set_week_from_sun(53).is_ok()); |
| 1285 | assert_eq!(Parsed::new().set_week_from_sun(54), Err(OUT_OF_RANGE)); |
| 1286 | |
| 1287 | assert_eq!(Parsed::new().set_week_from_mon(-1), Err(OUT_OF_RANGE)); |
| 1288 | assert!(Parsed::new().set_week_from_mon(0).is_ok()); |
| 1289 | assert!(Parsed::new().set_week_from_mon(53).is_ok()); |
| 1290 | assert_eq!(Parsed::new().set_week_from_mon(54), Err(OUT_OF_RANGE)); |
| 1291 | |
| 1292 | assert_eq!(Parsed::new().set_isoweek(0), Err(OUT_OF_RANGE)); |
| 1293 | assert!(Parsed::new().set_isoweek(1).is_ok()); |
| 1294 | assert!(Parsed::new().set_isoweek(53).is_ok()); |
| 1295 | assert_eq!(Parsed::new().set_isoweek(54), Err(OUT_OF_RANGE)); |
| 1296 | |
| 1297 | assert_eq!(Parsed::new().set_ordinal(0), Err(OUT_OF_RANGE)); |
| 1298 | assert!(Parsed::new().set_ordinal(1).is_ok()); |
| 1299 | assert!(Parsed::new().set_ordinal(366).is_ok()); |
| 1300 | assert_eq!(Parsed::new().set_ordinal(367), Err(OUT_OF_RANGE)); |
| 1301 | |
| 1302 | assert_eq!(Parsed::new().set_day(0), Err(OUT_OF_RANGE)); |
| 1303 | assert!(Parsed::new().set_day(1).is_ok()); |
| 1304 | assert!(Parsed::new().set_day(31).is_ok()); |
| 1305 | assert_eq!(Parsed::new().set_day(32), Err(OUT_OF_RANGE)); |
| 1306 | |
| 1307 | assert_eq!(Parsed::new().set_hour12(0), Err(OUT_OF_RANGE)); |
| 1308 | assert!(Parsed::new().set_hour12(1).is_ok()); |
| 1309 | assert!(Parsed::new().set_hour12(12).is_ok()); |
| 1310 | assert_eq!(Parsed::new().set_hour12(13), Err(OUT_OF_RANGE)); |
| 1311 | |
| 1312 | assert_eq!(Parsed::new().set_hour(-1), Err(OUT_OF_RANGE)); |
| 1313 | assert!(Parsed::new().set_hour(0).is_ok()); |
| 1314 | assert!(Parsed::new().set_hour(23).is_ok()); |
| 1315 | assert_eq!(Parsed::new().set_hour(24), Err(OUT_OF_RANGE)); |
| 1316 | |
| 1317 | assert_eq!(Parsed::new().set_minute(-1), Err(OUT_OF_RANGE)); |
| 1318 | assert!(Parsed::new().set_minute(0).is_ok()); |
| 1319 | assert!(Parsed::new().set_minute(59).is_ok()); |
| 1320 | assert_eq!(Parsed::new().set_minute(60), Err(OUT_OF_RANGE)); |
| 1321 | |
| 1322 | assert_eq!(Parsed::new().set_second(-1), Err(OUT_OF_RANGE)); |
| 1323 | assert!(Parsed::new().set_second(0).is_ok()); |
| 1324 | assert!(Parsed::new().set_second(60).is_ok()); |
| 1325 | assert_eq!(Parsed::new().set_second(61), Err(OUT_OF_RANGE)); |
| 1326 | |
| 1327 | assert_eq!(Parsed::new().set_nanosecond(-1), Err(OUT_OF_RANGE)); |
| 1328 | assert!(Parsed::new().set_nanosecond(0).is_ok()); |
| 1329 | assert!(Parsed::new().set_nanosecond(999_999_999).is_ok()); |
| 1330 | assert_eq!(Parsed::new().set_nanosecond(1_000_000_000), Err(OUT_OF_RANGE)); |
| 1331 | |
| 1332 | assert!(Parsed::new().set_timestamp(i64::MIN).is_ok()); |
| 1333 | assert!(Parsed::new().set_timestamp(i64::MAX).is_ok()); |
| 1334 | |
| 1335 | assert_eq!(Parsed::new().set_offset(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); |
| 1336 | assert!(Parsed::new().set_offset(i32::MIN as i64).is_ok()); |
| 1337 | assert!(Parsed::new().set_offset(i32::MAX as i64).is_ok()); |
| 1338 | assert_eq!(Parsed::new().set_offset(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
| 1339 | } |
| 1340 | |
| 1341 | #[test ] |
| 1342 | fn test_parsed_to_naive_date() { |
| 1343 | macro_rules! parse { |
| 1344 | ($($k:ident: $v:expr),*) => ( |
| 1345 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date() |
| 1346 | ) |
| 1347 | } |
| 1348 | |
| 1349 | let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap()); |
| 1350 | |
| 1351 | // ymd: omission of fields |
| 1352 | assert_eq!(parse!(), Err(NOT_ENOUGH)); |
| 1353 | assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH)); |
| 1354 | assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH)); |
| 1355 | assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2)); |
| 1356 | assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH)); |
| 1357 | assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH)); |
| 1358 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH)); |
| 1359 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH)); |
| 1360 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2)); |
| 1361 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH)); |
| 1362 | assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH)); |
| 1363 | assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2)); |
| 1364 | assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2)); |
| 1365 | |
| 1366 | // ymd: out-of-range conditions |
| 1367 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29)); |
| 1368 | assert_eq!( |
| 1369 | parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29), |
| 1370 | Err(OUT_OF_RANGE) |
| 1371 | ); |
| 1372 | assert_eq!( |
| 1373 | parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1), |
| 1374 | Err(OUT_OF_RANGE) |
| 1375 | ); |
| 1376 | assert_eq!( |
| 1377 | parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31), |
| 1378 | ymd(1983, 12, 31) |
| 1379 | ); |
| 1380 | assert_eq!( |
| 1381 | parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32), |
| 1382 | Err(OUT_OF_RANGE) |
| 1383 | ); |
| 1384 | assert_eq!( |
| 1385 | parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0), |
| 1386 | Err(OUT_OF_RANGE) |
| 1387 | ); |
| 1388 | assert_eq!( |
| 1389 | parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1), |
| 1390 | Err(OUT_OF_RANGE) |
| 1391 | ); |
| 1392 | assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE)); |
| 1393 | assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1)); |
| 1394 | assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(IMPOSSIBLE)); |
| 1395 | let max_year = NaiveDate::MAX.year(); |
| 1396 | assert_eq!( |
| 1397 | parse!(year_div_100: max_year / 100, |
| 1398 | year_mod_100: max_year % 100, month: 1, day: 1), |
| 1399 | ymd(max_year, 1, 1) |
| 1400 | ); |
| 1401 | assert_eq!( |
| 1402 | parse!(year_div_100: (max_year + 1) / 100, |
| 1403 | year_mod_100: (max_year + 1) % 100, month: 1, day: 1), |
| 1404 | Err(OUT_OF_RANGE) |
| 1405 | ); |
| 1406 | |
| 1407 | // ymd: conflicting inputs |
| 1408 | assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1)); |
| 1409 | assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE)); |
| 1410 | assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1)); |
| 1411 | assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE)); |
| 1412 | assert_eq!( |
| 1413 | parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1), |
| 1414 | ymd(1984, 1, 1) |
| 1415 | ); |
| 1416 | assert_eq!( |
| 1417 | parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1), |
| 1418 | Err(IMPOSSIBLE) |
| 1419 | ); |
| 1420 | assert_eq!( |
| 1421 | parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1), |
| 1422 | Err(OUT_OF_RANGE) |
| 1423 | ); |
| 1424 | assert_eq!( |
| 1425 | parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1), |
| 1426 | Err(OUT_OF_RANGE) |
| 1427 | ); |
| 1428 | assert_eq!( |
| 1429 | parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1), |
| 1430 | Err(IMPOSSIBLE) |
| 1431 | ); |
| 1432 | assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(IMPOSSIBLE)); |
| 1433 | assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(IMPOSSIBLE)); |
| 1434 | |
| 1435 | // weekdates |
| 1436 | assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH)); |
| 1437 | assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH)); |
| 1438 | assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH)); |
| 1439 | assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(IMPOSSIBLE)); |
| 1440 | assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(IMPOSSIBLE)); |
| 1441 | assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1)); |
| 1442 | assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1)); |
| 1443 | assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2)); |
| 1444 | assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2)); |
| 1445 | assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3)); |
| 1446 | assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3)); |
| 1447 | assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8)); |
| 1448 | assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8)); |
| 1449 | assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9)); |
| 1450 | assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9)); |
| 1451 | assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10)); |
| 1452 | assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30)); |
| 1453 | assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31)); |
| 1454 | assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(IMPOSSIBLE)); |
| 1455 | assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE)); |
| 1456 | assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(IMPOSSIBLE)); |
| 1457 | assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1)); |
| 1458 | |
| 1459 | // weekdates: conflicting inputs |
| 1460 | assert_eq!( |
| 1461 | parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat), |
| 1462 | ymd(2000, 1, 8) |
| 1463 | ); |
| 1464 | assert_eq!( |
| 1465 | parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun), |
| 1466 | ymd(2000, 1, 9) |
| 1467 | ); |
| 1468 | assert_eq!( |
| 1469 | parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun), |
| 1470 | Err(IMPOSSIBLE) |
| 1471 | ); |
| 1472 | assert_eq!( |
| 1473 | parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun), |
| 1474 | Err(IMPOSSIBLE) |
| 1475 | ); |
| 1476 | |
| 1477 | // ISO weekdates |
| 1478 | assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH)); |
| 1479 | assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31)); |
| 1480 | assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1)); |
| 1481 | assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE)); |
| 1482 | assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE)); |
| 1483 | assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3)); |
| 1484 | assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH)); |
| 1485 | |
| 1486 | // year and ordinal |
| 1487 | assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH)); |
| 1488 | assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE)); |
| 1489 | assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1)); |
| 1490 | assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29)); |
| 1491 | assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1)); |
| 1492 | assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31)); |
| 1493 | assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE)); |
| 1494 | assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); |
| 1495 | assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE)); |
| 1496 | assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1)); |
| 1497 | assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28)); |
| 1498 | assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1)); |
| 1499 | assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31)); |
| 1500 | assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE)); |
| 1501 | assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); |
| 1502 | |
| 1503 | // more complex cases |
| 1504 | assert_eq!( |
| 1505 | parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1, |
| 1506 | week_from_sun: 52, week_from_mon: 52, weekday: Wed), |
| 1507 | ymd(2014, 12, 31) |
| 1508 | ); |
| 1509 | assert_eq!( |
| 1510 | parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1, |
| 1511 | week_from_sun: 52, week_from_mon: 52), |
| 1512 | ymd(2014, 12, 31) |
| 1513 | ); |
| 1514 | assert_eq!( |
| 1515 | parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53, |
| 1516 | week_from_sun: 52, week_from_mon: 52, weekday: Wed), |
| 1517 | Err(IMPOSSIBLE) |
| 1518 | ); // no ISO week date 2014-W53-3 |
| 1519 | assert_eq!( |
| 1520 | parse!(year: 2012, isoyear: 2015, isoweek: 1, |
| 1521 | week_from_sun: 52, week_from_mon: 52), |
| 1522 | Err(NOT_ENOUGH) |
| 1523 | ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31) |
| 1524 | assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH)); |
| 1525 | // technically unique (2014-12-31) but Chrono gives up |
| 1526 | } |
| 1527 | |
| 1528 | #[test ] |
| 1529 | fn test_parsed_to_naive_time() { |
| 1530 | macro_rules! parse { |
| 1531 | ($($k:ident: $v:expr),*) => ( |
| 1532 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time() |
| 1533 | ) |
| 1534 | } |
| 1535 | |
| 1536 | let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap()); |
| 1537 | let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap()); |
| 1538 | |
| 1539 | // omission of fields |
| 1540 | assert_eq!(parse!(), Err(NOT_ENOUGH)); |
| 1541 | assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH)); |
| 1542 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH)); |
| 1543 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0)); |
| 1544 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45)); |
| 1545 | assert_eq!( |
| 1546 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45, |
| 1547 | nanosecond: 678_901_234), |
| 1548 | hmsn(1, 23, 45, 678_901_234) |
| 1549 | ); |
| 1550 | assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6)); |
| 1551 | assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH)); |
| 1552 | assert_eq!( |
| 1553 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012), |
| 1554 | Err(NOT_ENOUGH) |
| 1555 | ); |
| 1556 | |
| 1557 | // out-of-range conditions |
| 1558 | assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE)); |
| 1559 | assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE)); |
| 1560 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE)); |
| 1561 | assert_eq!( |
| 1562 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61), |
| 1563 | Err(OUT_OF_RANGE) |
| 1564 | ); |
| 1565 | assert_eq!( |
| 1566 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34, |
| 1567 | nanosecond: 1_000_000_000), |
| 1568 | Err(OUT_OF_RANGE) |
| 1569 | ); |
| 1570 | |
| 1571 | // leap seconds |
| 1572 | assert_eq!( |
| 1573 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60), |
| 1574 | hmsn(1, 23, 59, 1_000_000_000) |
| 1575 | ); |
| 1576 | assert_eq!( |
| 1577 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60, |
| 1578 | nanosecond: 999_999_999), |
| 1579 | hmsn(1, 23, 59, 1_999_999_999) |
| 1580 | ); |
| 1581 | } |
| 1582 | |
| 1583 | #[test ] |
| 1584 | fn test_parsed_to_naive_datetime_with_offset() { |
| 1585 | macro_rules! parse { |
| 1586 | (offset = $offset:expr; $($k:ident: $v:expr),*) => ( |
| 1587 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset) |
| 1588 | ); |
| 1589 | ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*)) |
| 1590 | } |
| 1591 | |
| 1592 | let ymdhms = |y, m, d, h, n, s| { |
| 1593 | Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap()) |
| 1594 | }; |
| 1595 | let ymdhmsn = |y, m, d, h, n, s, nano| { |
| 1596 | Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap()) |
| 1597 | }; |
| 1598 | |
| 1599 | // omission of fields |
| 1600 | assert_eq!(parse!(), Err(NOT_ENOUGH)); |
| 1601 | assert_eq!( |
| 1602 | parse!(year: 2015, month: 1, day: 30, |
| 1603 | hour_div_12: 1, hour_mod_12: 2, minute: 38), |
| 1604 | ymdhms(2015, 1, 30, 14, 38, 0) |
| 1605 | ); |
| 1606 | assert_eq!( |
| 1607 | parse!(year: 1997, month: 1, day: 30, |
| 1608 | hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5), |
| 1609 | ymdhms(1997, 1, 30, 14, 38, 5) |
| 1610 | ); |
| 1611 | assert_eq!( |
| 1612 | parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5, |
| 1613 | minute: 6, second: 7, nanosecond: 890_123_456), |
| 1614 | ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456) |
| 1615 | ); |
| 1616 | assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0)); |
| 1617 | assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1)); |
| 1618 | assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1)); |
| 1619 | assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40)); |
| 1620 | assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44)); |
| 1621 | |
| 1622 | // full fields |
| 1623 | assert_eq!( |
| 1624 | parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, |
| 1625 | ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, |
| 1626 | isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, |
| 1627 | hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, |
| 1628 | nanosecond: 12_345_678, timestamp: 1_420_000_000), |
| 1629 | ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) |
| 1630 | ); |
| 1631 | assert_eq!( |
| 1632 | parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, |
| 1633 | ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, |
| 1634 | isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, |
| 1635 | hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, |
| 1636 | nanosecond: 12_345_678, timestamp: 1_419_999_999), |
| 1637 | Err(IMPOSSIBLE) |
| 1638 | ); |
| 1639 | assert_eq!( |
| 1640 | parse!(offset = 32400; |
| 1641 | year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, |
| 1642 | ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, |
| 1643 | isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, |
| 1644 | hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, |
| 1645 | nanosecond: 12_345_678, timestamp: 1_419_967_600), |
| 1646 | ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) |
| 1647 | ); |
| 1648 | |
| 1649 | // more timestamps |
| 1650 | let max_days_from_year_1970 = |
| 1651 | NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); |
| 1652 | let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1) |
| 1653 | .unwrap() |
| 1654 | .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); |
| 1655 | let min_days_from_year_1970 = |
| 1656 | NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); |
| 1657 | assert_eq!( |
| 1658 | parse!(timestamp: min_days_from_year_1970.num_seconds()), |
| 1659 | ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0) |
| 1660 | ); |
| 1661 | assert_eq!( |
| 1662 | parse!(timestamp: year_0_from_year_1970.num_seconds()), |
| 1663 | ymdhms(0, 1, 1, 0, 0, 0) |
| 1664 | ); |
| 1665 | assert_eq!( |
| 1666 | parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399), |
| 1667 | ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59) |
| 1668 | ); |
| 1669 | |
| 1670 | // leap seconds #1: partial fields |
| 1671 | assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE)); |
| 1672 | assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59)); |
| 1673 | assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); |
| 1674 | assert_eq!( |
| 1675 | parse!(second: 60, timestamp: 1_341_100_799), |
| 1676 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
| 1677 | ); |
| 1678 | assert_eq!( |
| 1679 | parse!(second: 60, timestamp: 1_341_100_800), |
| 1680 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
| 1681 | ); |
| 1682 | assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0)); |
| 1683 | assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); |
| 1684 | assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE)); |
| 1685 | |
| 1686 | // leap seconds #2: full fields |
| 1687 | // we need to have separate tests for them since it uses another control flow. |
| 1688 | assert_eq!( |
| 1689 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
| 1690 | minute: 59, second: 59, timestamp: 1_341_100_798), |
| 1691 | Err(IMPOSSIBLE) |
| 1692 | ); |
| 1693 | assert_eq!( |
| 1694 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
| 1695 | minute: 59, second: 59, timestamp: 1_341_100_799), |
| 1696 | ymdhms(2012, 6, 30, 23, 59, 59) |
| 1697 | ); |
| 1698 | assert_eq!( |
| 1699 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
| 1700 | minute: 59, second: 59, timestamp: 1_341_100_800), |
| 1701 | Err(IMPOSSIBLE) |
| 1702 | ); |
| 1703 | assert_eq!( |
| 1704 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
| 1705 | minute: 59, second: 60, timestamp: 1_341_100_799), |
| 1706 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
| 1707 | ); |
| 1708 | assert_eq!( |
| 1709 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
| 1710 | minute: 59, second: 60, timestamp: 1_341_100_800), |
| 1711 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
| 1712 | ); |
| 1713 | assert_eq!( |
| 1714 | parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, |
| 1715 | minute: 0, second: 0, timestamp: 1_341_100_800), |
| 1716 | ymdhms(2012, 7, 1, 0, 0, 0) |
| 1717 | ); |
| 1718 | assert_eq!( |
| 1719 | parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, |
| 1720 | minute: 0, second: 1, timestamp: 1_341_100_800), |
| 1721 | Err(IMPOSSIBLE) |
| 1722 | ); |
| 1723 | assert_eq!( |
| 1724 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
| 1725 | minute: 59, second: 60, timestamp: 1_341_100_801), |
| 1726 | Err(IMPOSSIBLE) |
| 1727 | ); |
| 1728 | |
| 1729 | // error codes |
| 1730 | assert_eq!( |
| 1731 | parse!(year: 2015, month: 1, day: 20, weekday: Tue, |
| 1732 | hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20), |
| 1733 | Err(OUT_OF_RANGE) |
| 1734 | ); // `hour_div_12` is out of range |
| 1735 | } |
| 1736 | |
| 1737 | #[test ] |
| 1738 | fn test_parsed_to_datetime() { |
| 1739 | macro_rules! parse { |
| 1740 | ($($k:ident: $v:expr),*) => ( |
| 1741 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime() |
| 1742 | ) |
| 1743 | } |
| 1744 | |
| 1745 | let ymdhmsn = |y, m, d, h, n, s, nano, off| { |
| 1746 | Ok(FixedOffset::east_opt(off) |
| 1747 | .unwrap() |
| 1748 | .from_local_datetime( |
| 1749 | &NaiveDate::from_ymd_opt(y, m, d) |
| 1750 | .unwrap() |
| 1751 | .and_hms_nano_opt(h, n, s, nano) |
| 1752 | .unwrap(), |
| 1753 | ) |
| 1754 | .unwrap()) |
| 1755 | }; |
| 1756 | |
| 1757 | assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH)); |
| 1758 | assert_eq!( |
| 1759 | parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
| 1760 | minute: 26, second: 40, nanosecond: 12_345_678), |
| 1761 | Err(NOT_ENOUGH) |
| 1762 | ); |
| 1763 | assert_eq!( |
| 1764 | parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
| 1765 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), |
| 1766 | ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0) |
| 1767 | ); |
| 1768 | assert_eq!( |
| 1769 | parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, |
| 1770 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), |
| 1771 | ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400) |
| 1772 | ); |
| 1773 | assert_eq!( |
| 1774 | parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1, |
| 1775 | minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876), |
| 1776 | ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876) |
| 1777 | ); |
| 1778 | assert_eq!( |
| 1779 | parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4, |
| 1780 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400), |
| 1781 | Err(OUT_OF_RANGE) |
| 1782 | ); // `FixedOffset` does not support such huge offset |
| 1783 | } |
| 1784 | |
| 1785 | #[test ] |
| 1786 | fn test_parsed_to_datetime_with_timezone() { |
| 1787 | macro_rules! parse { |
| 1788 | ($tz:expr; $($k:ident: $v:expr),*) => ( |
| 1789 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz) |
| 1790 | ) |
| 1791 | } |
| 1792 | |
| 1793 | // single result from ymdhms |
| 1794 | assert_eq!( |
| 1795 | parse!(Utc; |
| 1796 | year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
| 1797 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), |
| 1798 | Ok(Utc |
| 1799 | .from_local_datetime( |
| 1800 | &NaiveDate::from_ymd_opt(2014, 12, 31) |
| 1801 | .unwrap() |
| 1802 | .and_hms_nano_opt(4, 26, 40, 12_345_678) |
| 1803 | .unwrap() |
| 1804 | ) |
| 1805 | .unwrap()) |
| 1806 | ); |
| 1807 | assert_eq!( |
| 1808 | parse!(Utc; |
| 1809 | year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, |
| 1810 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), |
| 1811 | Err(IMPOSSIBLE) |
| 1812 | ); |
| 1813 | assert_eq!( |
| 1814 | parse!(FixedOffset::east_opt(32400).unwrap(); |
| 1815 | year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
| 1816 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), |
| 1817 | Err(IMPOSSIBLE) |
| 1818 | ); |
| 1819 | assert_eq!( |
| 1820 | parse!(FixedOffset::east_opt(32400).unwrap(); |
| 1821 | year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, |
| 1822 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), |
| 1823 | Ok(FixedOffset::east_opt(32400) |
| 1824 | .unwrap() |
| 1825 | .from_local_datetime( |
| 1826 | &NaiveDate::from_ymd_opt(2014, 12, 31) |
| 1827 | .unwrap() |
| 1828 | .and_hms_nano_opt(13, 26, 40, 12_345_678) |
| 1829 | .unwrap() |
| 1830 | ) |
| 1831 | .unwrap()) |
| 1832 | ); |
| 1833 | |
| 1834 | // single result from timestamp |
| 1835 | assert_eq!( |
| 1836 | parse!(Utc; timestamp: 1_420_000_000, offset: 0), |
| 1837 | Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap()) |
| 1838 | ); |
| 1839 | assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE)); |
| 1840 | assert_eq!( |
| 1841 | parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0), |
| 1842 | Err(IMPOSSIBLE) |
| 1843 | ); |
| 1844 | assert_eq!( |
| 1845 | parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400), |
| 1846 | Ok(FixedOffset::east_opt(32400) |
| 1847 | .unwrap() |
| 1848 | .with_ymd_and_hms(2014, 12, 31, 13, 26, 40) |
| 1849 | .unwrap()) |
| 1850 | ); |
| 1851 | |
| 1852 | // TODO test with a variable time zone (for None and Ambiguous cases) |
| 1853 | } |
| 1854 | |
| 1855 | #[test ] |
| 1856 | fn issue_551() { |
| 1857 | use crate::Weekday; |
| 1858 | let mut parsed = Parsed::new(); |
| 1859 | |
| 1860 | parsed.year = Some(2002); |
| 1861 | parsed.week_from_mon = Some(22); |
| 1862 | parsed.weekday = Some(Weekday::Mon); |
| 1863 | assert_eq!(NaiveDate::from_ymd_opt(2002, 6, 3).unwrap(), parsed.to_naive_date().unwrap()); |
| 1864 | |
| 1865 | parsed.year = Some(2001); |
| 1866 | assert_eq!(NaiveDate::from_ymd_opt(2001, 5, 28).unwrap(), parsed.to_naive_date().unwrap()); |
| 1867 | } |
| 1868 | } |
| 1869 | |