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, MappedLocalTime, 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/// ```
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)]
129pub 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]
177fn 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
190impl 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.
1143fn 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)]
1166mod 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