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