1// This is a part of Chrono.
2// See README.md and LICENSE.txt for details.
34//! A collection of parsed date and time items.
5//! They can be constructed incrementally while being checked for consistency.
67use super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE, ParseResult};
8use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
9use crate::offset::{FixedOffset, MappedLocalTime, Offset, TimeZone};
10use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday};
1112/// 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 built-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(#[automatically_derived]
#[allow(clippy::manual_non_exhaustive)]
impl ::core::clone::Clone for Parsed {
#[inline]
fn clone(&self) -> Parsed {
Parsed {
year: ::core::clone::Clone::clone(&self.year),
year_div_100: ::core::clone::Clone::clone(&self.year_div_100),
year_mod_100: ::core::clone::Clone::clone(&self.year_mod_100),
isoyear: ::core::clone::Clone::clone(&self.isoyear),
isoyear_div_100: ::core::clone::Clone::clone(&self.isoyear_div_100),
isoyear_mod_100: ::core::clone::Clone::clone(&self.isoyear_mod_100),
quarter: ::core::clone::Clone::clone(&self.quarter),
month: ::core::clone::Clone::clone(&self.month),
week_from_sun: ::core::clone::Clone::clone(&self.week_from_sun),
week_from_mon: ::core::clone::Clone::clone(&self.week_from_mon),
isoweek: ::core::clone::Clone::clone(&self.isoweek),
weekday: ::core::clone::Clone::clone(&self.weekday),
ordinal: ::core::clone::Clone::clone(&self.ordinal),
day: ::core::clone::Clone::clone(&self.day),
hour_div_12: ::core::clone::Clone::clone(&self.hour_div_12),
hour_mod_12: ::core::clone::Clone::clone(&self.hour_mod_12),
minute: ::core::clone::Clone::clone(&self.minute),
second: ::core::clone::Clone::clone(&self.second),
nanosecond: ::core::clone::Clone::clone(&self.nanosecond),
timestamp: ::core::clone::Clone::clone(&self.timestamp),
offset: ::core::clone::Clone::clone(&self.offset),
_dummy: ::core::clone::Clone::clone(&self._dummy),
}
}
}Clone, #[automatically_derived]
#[allow(clippy::manual_non_exhaustive)]
impl ::core::cmp::PartialEq for Parsed {
#[inline]
fn eq(&self, other: &Parsed) -> bool {
self._dummy == other._dummy && self.year == other.year &&
self.year_div_100 == other.year_div_100 &&
self.year_mod_100 == other.year_mod_100 &&
self.isoyear == other.isoyear &&
self.isoyear_div_100 == other.isoyear_div_100 &&
self.isoyear_mod_100 == other.isoyear_mod_100 &&
self.quarter == other.quarter && self.month == other.month
&& self.week_from_sun == other.week_from_sun &&
self.week_from_mon == other.week_from_mon &&
self.isoweek == other.isoweek &&
self.weekday == other.weekday &&
self.ordinal == other.ordinal && self.day == other.day &&
self.hour_div_12 == other.hour_div_12 &&
self.hour_mod_12 == other.hour_mod_12 &&
self.minute == other.minute && self.second == other.second
&& self.nanosecond == other.nanosecond &&
self.timestamp == other.timestamp &&
self.offset == other.offset
}
}PartialEq, #[automatically_derived]
#[allow(clippy::manual_non_exhaustive)]
impl ::core::cmp::Eq for Parsed {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<Weekday>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
let _: ::core::cmp::AssertParamIsEq<Option<i64>>;
let _: ::core::cmp::AssertParamIsEq<Option<i32>>;
let _: ::core::cmp::AssertParamIsEq<()>;
}
}Eq, #[automatically_derived]
#[allow(clippy::manual_non_exhaustive)]
impl ::core::fmt::Debug for Parsed {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["year", "year_div_100", "year_mod_100", "isoyear",
"isoyear_div_100", "isoyear_mod_100", "quarter", "month",
"week_from_sun", "week_from_mon", "isoweek", "weekday",
"ordinal", "day", "hour_div_12", "hour_mod_12", "minute",
"second", "nanosecond", "timestamp", "offset", "_dummy"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self.year, &self.year_div_100, &self.year_mod_100,
&self.isoyear, &self.isoyear_div_100, &self.isoyear_mod_100,
&self.quarter, &self.month, &self.week_from_sun,
&self.week_from_mon, &self.isoweek, &self.weekday,
&self.ordinal, &self.day, &self.hour_div_12,
&self.hour_mod_12, &self.minute, &self.second,
&self.nanosecond, &self.timestamp, &self.offset,
&&self._dummy];
::core::fmt::Formatter::debug_struct_fields_finish(f, "Parsed", names,
values)
}
}Debug, #[automatically_derived]
#[allow(clippy::manual_non_exhaustive)]
impl ::core::default::Default for Parsed {
#[inline]
fn default() -> Parsed {
Parsed {
year: ::core::default::Default::default(),
year_div_100: ::core::default::Default::default(),
year_mod_100: ::core::default::Default::default(),
isoyear: ::core::default::Default::default(),
isoyear_div_100: ::core::default::Default::default(),
isoyear_mod_100: ::core::default::Default::default(),
quarter: ::core::default::Default::default(),
month: ::core::default::Default::default(),
week_from_sun: ::core::default::Default::default(),
week_from_mon: ::core::default::Default::default(),
isoweek: ::core::default::Default::default(),
weekday: ::core::default::Default::default(),
ordinal: ::core::default::Default::default(),
day: ::core::default::Default::default(),
hour_div_12: ::core::default::Default::default(),
hour_mod_12: ::core::default::Default::default(),
minute: ::core::default::Default::default(),
second: ::core::default::Default::default(),
nanosecond: ::core::default::Default::default(),
timestamp: ::core::default::Default::default(),
offset: ::core::default::Default::default(),
_dummy: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]
#[allow(clippy::manual_non_exhaustive)]
impl ::core::hash::Hash for Parsed {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.year, state);
::core::hash::Hash::hash(&self.year_div_100, state);
::core::hash::Hash::hash(&self.year_mod_100, state);
::core::hash::Hash::hash(&self.isoyear, state);
::core::hash::Hash::hash(&self.isoyear_div_100, state);
::core::hash::Hash::hash(&self.isoyear_mod_100, state);
::core::hash::Hash::hash(&self.quarter, state);
::core::hash::Hash::hash(&self.month, state);
::core::hash::Hash::hash(&self.week_from_sun, state);
::core::hash::Hash::hash(&self.week_from_mon, state);
::core::hash::Hash::hash(&self.isoweek, state);
::core::hash::Hash::hash(&self.weekday, state);
::core::hash::Hash::hash(&self.ordinal, state);
::core::hash::Hash::hash(&self.day, state);
::core::hash::Hash::hash(&self.hour_div_12, state);
::core::hash::Hash::hash(&self.hour_mod_12, state);
::core::hash::Hash::hash(&self.minute, state);
::core::hash::Hash::hash(&self.second, state);
::core::hash::Hash::hash(&self.nanosecond, state);
::core::hash::Hash::hash(&self.timestamp, state);
::core::hash::Hash::hash(&self.offset, state);
::core::hash::Hash::hash(&self._dummy, state)
}
}Hash)]
129#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130pub struct Parsed {
131#[doc(hidden)]
132pub year: Option<i32>,
133#[doc(hidden)]
134pub year_div_100: Option<i32>,
135#[doc(hidden)]
136pub year_mod_100: Option<i32>,
137#[doc(hidden)]
138pub isoyear: Option<i32>,
139#[doc(hidden)]
140pub isoyear_div_100: Option<i32>,
141#[doc(hidden)]
142pub isoyear_mod_100: Option<i32>,
143#[doc(hidden)]
144pub quarter: Option<u32>,
145#[doc(hidden)]
146pub month: Option<u32>,
147#[doc(hidden)]
148pub week_from_sun: Option<u32>,
149#[doc(hidden)]
150pub week_from_mon: Option<u32>,
151#[doc(hidden)]
152pub isoweek: Option<u32>,
153#[doc(hidden)]
154pub weekday: Option<Weekday>,
155#[doc(hidden)]
156pub ordinal: Option<u32>,
157#[doc(hidden)]
158pub day: Option<u32>,
159#[doc(hidden)]
160pub hour_div_12: Option<u32>,
161#[doc(hidden)]
162pub hour_mod_12: Option<u32>,
163#[doc(hidden)]
164pub minute: Option<u32>,
165#[doc(hidden)]
166pub second: Option<u32>,
167#[doc(hidden)]
168pub nanosecond: Option<u32>,
169#[doc(hidden)]
170pub timestamp: Option<i64>,
171#[doc(hidden)]
172pub offset: Option<i32>,
173#[doc(hidden)]
174_dummy: (),
175}
176177/// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"),
178/// and if it is empty, set `old` to `new` as well.
179#[inline]
180fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
181match old {
182Some(old) if *old != new => Err(IMPOSSIBLE),
183_ => {
184*old = Some(new);
185Ok(())
186 }
187 }
188}
189190impl Parsed {
191/// Returns the initial value of parsed parts.
192#[must_use]
193pub fn new() -> Parsed {
194Parsed::default()
195 }
196197/// 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]
208pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
209set_if_consistent(&mut self.year, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
210 }
211212/// 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]
220pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
221if !(0..=i32::MAXas i64).contains(&value) {
222return Err(OUT_OF_RANGE);
223 }
224set_if_consistent(&mut self.year_div_100, valueas i32)
225 }
226227/// 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]
242pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
243if !(0..100).contains(&value) {
244return Err(OUT_OF_RANGE);
245 }
246set_if_consistent(&mut self.year_mod_100, valueas i32)
247 }
248249/// 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]
263pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
264set_if_consistent(&mut self.isoyear, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
265 }
266267/// 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]
278pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
279if !(0..=i32::MAXas i64).contains(&value) {
280return Err(OUT_OF_RANGE);
281 }
282set_if_consistent(&mut self.isoyear_div_100, valueas i32)
283 }
284285/// 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]
303pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
304if !(0..100).contains(&value) {
305return Err(OUT_OF_RANGE);
306 }
307set_if_consistent(&mut self.isoyear_mod_100, valueas i32)
308 }
309310/// Set the [`quarter`](Parsed::quarter) field to the given value.
311 ///
312 /// Quarter 1 starts in January.
313 ///
314 /// # Errors
315 ///
316 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-4.
317 ///
318 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
319#[inline]
320pub fn set_quarter(&mut self, value: i64) -> ParseResult<()> {
321if !(1..=4).contains(&value) {
322return Err(OUT_OF_RANGE);
323 }
324set_if_consistent(&mut self.quarter, valueas u32)
325 }
326327/// Set the [`month`](Parsed::month) field to the given value.
328 ///
329 /// # Errors
330 ///
331 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12.
332 ///
333 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
334#[inline]
335pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
336if !(1..=12).contains(&value) {
337return Err(OUT_OF_RANGE);
338 }
339set_if_consistent(&mut self.month, valueas u32)
340 }
341342/// Set the [`week_from_sun`](Parsed::week_from_sun) week number field to the given value.
343 ///
344 /// Week 1 starts at the first Sunday of January.
345 ///
346 /// # Errors
347 ///
348 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53.
349 ///
350 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
351#[inline]
352pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
353if !(0..=53).contains(&value) {
354return Err(OUT_OF_RANGE);
355 }
356set_if_consistent(&mut self.week_from_sun, valueas u32)
357 }
358359/// Set the [`week_from_mon`](Parsed::week_from_mon) week number field to the given value.
360 /// Set the 'week number starting with Monday' field to the given value.
361 ///
362 /// Week 1 starts at the first Monday of January.
363 ///
364 /// # Errors
365 ///
366 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53.
367 ///
368 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
369#[inline]
370pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
371if !(0..=53).contains(&value) {
372return Err(OUT_OF_RANGE);
373 }
374set_if_consistent(&mut self.week_from_mon, valueas u32)
375 }
376377/// Set the [`isoweek`](Parsed::isoweek) field for an [ISO 8601 week date] to the given value.
378 ///
379 /// [ISO 8601 week date]: crate::NaiveDate#week-date
380 ///
381 /// # Errors
382 ///
383 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-53.
384 ///
385 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
386#[inline]
387pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
388if !(1..=53).contains(&value) {
389return Err(OUT_OF_RANGE);
390 }
391set_if_consistent(&mut self.isoweek, valueas u32)
392 }
393394/// Set the [`weekday`](Parsed::weekday) field to the given value.
395 ///
396 /// # Errors
397 ///
398 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
399#[inline]
400pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
401set_if_consistent(&mut self.weekday, value)
402 }
403404/// Set the [`ordinal`](Parsed::ordinal) (day of the year) field to the given value.
405 ///
406 /// # Errors
407 ///
408 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-366.
409 ///
410 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
411#[inline]
412pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
413if !(1..=366).contains(&value) {
414return Err(OUT_OF_RANGE);
415 }
416set_if_consistent(&mut self.ordinal, valueas u32)
417 }
418419/// Set the [`day`](Parsed::day) of the month field to the given value.
420 ///
421 /// # Errors
422 ///
423 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-31.
424 ///
425 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
426#[inline]
427pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
428if !(1..=31).contains(&value) {
429return Err(OUT_OF_RANGE);
430 }
431set_if_consistent(&mut self.day, valueas u32)
432 }
433434/// Set the [`hour_div_12`](Parsed::hour_div_12) am/pm field to the given value.
435 ///
436 /// `false` indicates AM and `true` indicates PM.
437 ///
438 /// # Errors
439 ///
440 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
441#[inline]
442pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
443set_if_consistent(&mut self.hour_div_12, valueas u32)
444 }
445446/// Set the [`hour_mod_12`](Parsed::hour_mod_12) field, for the hour number in 12-hour clocks,
447 /// to the given value.
448 ///
449 /// Value must be in the canonical range of 1-12.
450 /// It will internally be stored as 0-11 (`value % 12`).
451 ///
452 /// # Errors
453 ///
454 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12.
455 ///
456 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
457#[inline]
458pub fn set_hour12(&mut self, mut value: i64) -> ParseResult<()> {
459if !(1..=12).contains(&value) {
460return Err(OUT_OF_RANGE);
461 }
462if value == 12 {
463value = 0
464}
465set_if_consistent(&mut self.hour_mod_12, valueas u32)
466 }
467468/// Set the [`hour_div_12`](Parsed::hour_div_12) and [`hour_mod_12`](Parsed::hour_mod_12)
469 /// fields to the given value for a 24-hour clock.
470 ///
471 /// # Errors
472 ///
473 /// May return `OUT_OF_RANGE` if `value` is not in the range 0-23.
474 /// Currently only checks the value is not out of range for a `u32`.
475 ///
476 /// Returns `IMPOSSIBLE` one of the fields was already set to a different value.
477#[inline]
478pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
479let (hour_div_12, hour_mod_12) = match value {
480 hour @ 0..=11 => (0, houras u32),
481 hour @ 12..=23 => (1, houras u32 - 12),
482_ => return Err(OUT_OF_RANGE),
483 };
484set_if_consistent(&mut self.hour_div_12, hour_div_12)?;
485set_if_consistent(&mut self.hour_mod_12, hour_mod_12)
486 }
487488/// Set the [`minute`](Parsed::minute) field to the given value.
489 ///
490 /// # Errors
491 ///
492 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-59.
493 ///
494 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
495#[inline]
496pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
497if !(0..=59).contains(&value) {
498return Err(OUT_OF_RANGE);
499 }
500set_if_consistent(&mut self.minute, valueas u32)
501 }
502503/// Set the [`second`](Parsed::second) field to the given value.
504 ///
505 /// The value can be 60 in the case of a leap second.
506 ///
507 /// # Errors
508 ///
509 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-60.
510 ///
511 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
512#[inline]
513pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
514if !(0..=60).contains(&value) {
515return Err(OUT_OF_RANGE);
516 }
517set_if_consistent(&mut self.second, valueas u32)
518 }
519520/// Set the [`nanosecond`](Parsed::nanosecond) field to the given value.
521 ///
522 /// This is the number of nanoseconds since the whole second.
523 ///
524 /// # Errors
525 ///
526 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-999,999,999.
527 ///
528 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
529#[inline]
530pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
531if !(0..=999_999_999).contains(&value) {
532return Err(OUT_OF_RANGE);
533 }
534set_if_consistent(&mut self.nanosecond, valueas u32)
535 }
536537/// Set the [`timestamp`](Parsed::timestamp) field to the given value.
538 ///
539 /// A Unix timestamp is defined as the number of non-leap seconds since midnight UTC on
540 /// January 1, 1970.
541 ///
542 /// # Errors
543 ///
544 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
545#[inline]
546pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
547set_if_consistent(&mut self.timestamp, value)
548 }
549550/// Set the [`offset`](Parsed::offset) field to the given value.
551 ///
552 /// The offset is in seconds from local time to UTC.
553 ///
554 /// # Errors
555 ///
556 /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`.
557 ///
558 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
559#[inline]
560pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
561set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
562 }
563564/// Returns a parsed naive date out of given fields.
565 ///
566 /// This method is able to determine the date from given subset of fields:
567 ///
568 /// - Year, month, day.
569 /// - Year, day of the year (ordinal).
570 /// - Year, week number counted from Sunday or Monday, day of the week.
571 /// - ISO week date.
572 ///
573 /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted,
574 /// the two-digit year is used to guess the century number then.
575 ///
576 /// It checks all given date fields are consistent with each other.
577 ///
578 /// # Errors
579 ///
580 /// This method returns:
581 /// - `IMPOSSIBLE` if any of the date fields conflict.
582 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete date.
583 /// - `OUT_OF_RANGE`
584 /// - if any of the date fields of `Parsed` are set to a value beyond their acceptable range.
585 /// - if the value would be outside the range of a [`NaiveDate`].
586 /// - if the date does not exist.
587pub fn to_naive_date(&self) -> ParseResult<NaiveDate> {
588fn resolve_year(
589 y: Option<i32>,
590 q: Option<i32>,
591 r: Option<i32>,
592 ) -> ParseResult<Option<i32>> {
593match (y, q, r) {
594// if there is no further information, simply return the given full year.
595 // this is a common case, so let's avoid division here.
596(y, None, None) => Ok(y),
597598// if there is a full year *and* also quotient and/or modulo,
599 // check if present quotient and/or modulo is consistent to the full year.
600 // since the presence of those fields means a positive full year,
601 // we should filter a negative full year first.
602(Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => {
603if y < 0 {
604return Err(IMPOSSIBLE);
605 }
606let q_ = y / 100;
607let r_ = y % 100;
608if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ {
609Ok(Some(y))
610 } else {
611Err(IMPOSSIBLE)
612 }
613 }
614615// the full year is missing but we have quotient and modulo.
616 // reconstruct the full year. make sure that the result is always positive.
617(None, Some(q), Some(r @ 0..=99)) => {
618if q < 0 {
619return Err(IMPOSSIBLE);
620 }
621let y = q.checked_mul(100).and_then(|v| v.checked_add(r));
622Ok(Some(y.ok_or(OUT_OF_RANGE)?))
623 }
624625// we only have modulo. try to interpret a modulo as a conventional two-digit year.
626 // note: we are affected by Rust issue #18060. avoid multiple range patterns.
627(None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })),
628629// otherwise it is an out-of-bound or insufficient condition.
630(None, Some(_), None) => Err(NOT_ENOUGH),
631 (_, _, Some(_)) => Err(OUT_OF_RANGE),
632 }
633 }
634635let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?;
636let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?;
637638// verify the normal year-month-day date.
639let verify_ymd = |date: NaiveDate| {
640let year = date.year();
641let (year_div_100, year_mod_100) = if year >= 0 {
642 (Some(year / 100), Some(year % 100))
643 } else {
644 (None, None) // they should be empty to be consistent
645};
646let month = date.month();
647let day = date.day();
648self.year.unwrap_or(year) == year649 && self.year_div_100.or(year_div_100) == year_div_100650 && self.year_mod_100.or(year_mod_100) == year_mod_100651 && self.month.unwrap_or(month) == month652 && self.day.unwrap_or(day) == day653 };
654655// verify the ISO week date.
656let verify_isoweekdate = |date: NaiveDate| {
657let week = date.iso_week();
658let isoyear = week.year();
659let isoweek = week.week();
660let weekday = date.weekday();
661let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 {
662 (Some(isoyear / 100), Some(isoyear % 100))
663 } else {
664 (None, None) // they should be empty to be consistent
665};
666self.isoyear.unwrap_or(isoyear) == isoyear667 && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100668 && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100669 && self.isoweek.unwrap_or(isoweek) == isoweek670 && self.weekday.unwrap_or(weekday) == weekday671 };
672673// verify the ordinal and other (non-ISO) week dates.
674let verify_ordinal = |date: NaiveDate| {
675let ordinal = date.ordinal();
676let week_from_sun = date.weeks_from(Weekday::Sun);
677let week_from_mon = date.weeks_from(Weekday::Mon);
678self.ordinal.unwrap_or(ordinal) == ordinal679 && self.week_from_sun.map_or(week_from_sun, |v| vas i32) == week_from_sun680 && self.week_from_mon.map_or(week_from_mon, |v| vas i32) == week_from_mon681 };
682683// test several possibilities.
684 // tries to construct a full `NaiveDate` as much as possible, then verifies that
685 // it is consistent with other given fields.
686let (verified, parsed_date) = match (given_year, given_isoyear, self) {
687 (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => {
688// year, month, day
689let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?;
690 (verify_isoweekdate(date) && verify_ordinal(date), date)
691 }
692693 (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
694// year, day of the year
695let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?;
696 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
697 }
698699 (Some(year), _, &Parsed { week_from_sun: Some(week), weekday: Some(weekday), .. }) => {
700// year, week (starting at 1st Sunday), day of the week
701let date = resolve_week_date(year, week, weekday, Weekday::Sun)?;
702 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
703 }
704705 (Some(year), _, &Parsed { week_from_mon: Some(week), weekday: Some(weekday), .. }) => {
706// year, week (starting at 1st Monday), day of the week
707let date = resolve_week_date(year, week, weekday, Weekday::Mon)?;
708 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
709 }
710711 (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => {
712// ISO year, week, day of the week
713let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday);
714let date = date.ok_or(OUT_OF_RANGE)?;
715 (verify_ymd(date) && verify_ordinal(date), date)
716 }
717718 (_, _, _) => return Err(NOT_ENOUGH),
719 };
720721if !verified {
722return Err(IMPOSSIBLE);
723 } else if let Some(parsed) = self.quarter {
724if parsed != parsed_date.quarter() {
725return Err(IMPOSSIBLE);
726 }
727 }
728729Ok(parsed_date)
730 }
731732/// Returns a parsed naive time out of given fields.
733 ///
734 /// This method is able to determine the time from given subset of fields:
735 ///
736 /// - Hour, minute. (second and nanosecond assumed to be 0)
737 /// - Hour, minute, second. (nanosecond assumed to be 0)
738 /// - Hour, minute, second, nanosecond.
739 ///
740 /// It is able to handle leap seconds when given second is 60.
741 ///
742 /// # Errors
743 ///
744 /// This method returns:
745 /// - `OUT_OF_RANGE` if any of the time fields of `Parsed` are set to a value beyond
746 /// their acceptable range.
747 /// - `NOT_ENOUGH` if an hour field is missing, if AM/PM is missing in a 12-hour clock,
748 /// if minutes are missing, or if seconds are missing while the nanosecond field is present.
749pub fn to_naive_time(&self) -> ParseResult<NaiveTime> {
750let hour_div_12 = match self.hour_div_12 {
751Some(v @ 0..=1) => v,
752Some(_) => return Err(OUT_OF_RANGE),
753None => return Err(NOT_ENOUGH),
754 };
755let hour_mod_12 = match self.hour_mod_12 {
756Some(v @ 0..=11) => v,
757Some(_) => return Err(OUT_OF_RANGE),
758None => return Err(NOT_ENOUGH),
759 };
760let hour = hour_div_12 * 12 + hour_mod_12;
761762let minute = match self.minute {
763Some(v @ 0..=59) => v,
764Some(_) => return Err(OUT_OF_RANGE),
765None => return Err(NOT_ENOUGH),
766 };
767768// we allow omitting seconds or nanoseconds, but they should be in the range.
769let (second, mut nano) = match self.second.unwrap_or(0) {
770 v @ 0..=59 => (v, 0),
77160 => (59, 1_000_000_000),
772_ => return Err(OUT_OF_RANGE),
773 };
774nano += match self.nanosecond {
775Some(v @ 0..=999_999_999) if self.second.is_some() => v,
776Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing
777Some(_) => return Err(OUT_OF_RANGE),
778None => 0,
779 };
780781NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE)
782 }
783784/// Returns a parsed naive date and time out of given fields, except for the offset field.
785 ///
786 /// The offset is assumed to have a given value. It is not compared against the offset field set
787 /// in the `Parsed` type, so it is allowed to be inconsistent.
788 ///
789 /// This method is able to determine the combined date and time from date and time fields or
790 /// from a single timestamp field. It checks all fields are consistent with each other.
791 ///
792 /// # Errors
793 ///
794 /// This method returns:
795 /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of
796 /// the other fields.
797 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime.
798 /// - `OUT_OF_RANGE`
799 /// - if any of the date or time fields of `Parsed` are set to a value beyond their acceptable
800 /// range.
801 /// - if the value would be outside the range of a [`NaiveDateTime`].
802 /// - if the date does not exist.
803pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult<NaiveDateTime> {
804let date = self.to_naive_date();
805let time = self.to_naive_time();
806if let (Ok(date), Ok(time)) = (date, time) {
807let datetime = date.and_time(time);
808809// verify the timestamp field if any
810 // the following is safe, `timestamp` is very limited in range
811let timestamp = datetime.and_utc().timestamp() - i64::from(offset);
812if let Some(given_timestamp) = self.timestamp {
813// if `datetime` represents a leap second, it might be off by one second.
814if given_timestamp != timestamp815 && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1)
816 {
817return Err(IMPOSSIBLE);
818 }
819 }
820821Ok(datetime)
822 } else if let Some(timestamp) = self.timestamp {
823use super::ParseErroras PE;
824use super::ParseErrorKind::{Impossible, OutOfRange};
825826// if date and time is problematic already, there is no point proceeding.
827 // we at least try to give a correct error though.
828match (date, time) {
829 (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE),
830 (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE),
831 (_, _) => {} // one of them is insufficient
832}
833834// reconstruct date and time fields from timestamp
835let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?;
836let mut datetime = DateTime::from_timestamp_secs(ts).ok_or(OUT_OF_RANGE)?.naive_utc();
837838// fill year, ordinal, hour, minute and second fields from timestamp.
839 // if existing fields are consistent, this will allow the full date/time reconstruction.
840let mut parsed = self.clone();
841if parsed.second == Some(60) {
842// `datetime.second()` cannot be 60, so this is the only case for a leap second.
843match datetime.second() {
844// it's okay, just do not try to overwrite the existing field.
84559 => {}
846// `datetime` is known to be off by one second.
8470 => {
848datetime -= TimeDelta::try_seconds(1).unwrap();
849 }
850// otherwise it is impossible.
851_ => return Err(IMPOSSIBLE),
852 }
853// ...and we have the correct candidates for other fields.
854} else {
855parsed.set_second(i64::from(datetime.second()))?;
856 }
857parsed.set_year(i64::from(datetime.year()))?;
858parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd
859parsed.set_hour(i64::from(datetime.hour()))?;
860parsed.set_minute(i64::from(datetime.minute()))?;
861862// validate other fields (e.g. week) and return
863let date = parsed.to_naive_date()?;
864let time = parsed.to_naive_time()?;
865Ok(date.and_time(time))
866 } else {
867// reproduce the previous error(s)
868date?;
869time?;
870::core::panicking::panic("internal error: entered unreachable code")unreachable!()871 }
872 }
873874/// Returns a parsed fixed time zone offset out of given fields.
875 ///
876 /// # Errors
877 ///
878 /// This method returns:
879 /// - `OUT_OF_RANGE` if the offset is out of range for a `FixedOffset`.
880 /// - `NOT_ENOUGH` if the offset field is not set.
881pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> {
882FixedOffset::east_opt(self.offset.ok_or(NOT_ENOUGH)?).ok_or(OUT_OF_RANGE)
883 }
884885/// Returns a parsed timezone-aware date and time out of given fields.
886 ///
887 /// This method is able to determine the combined date and time from date, time and offset
888 /// fields, and/or from a single timestamp field. It checks all fields are consistent with each
889 /// other.
890 ///
891 /// # Errors
892 ///
893 /// This method returns:
894 /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of
895 /// the other fields.
896 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime
897 /// including offset from UTC.
898 /// - `OUT_OF_RANGE`
899 /// - if any of the fields of `Parsed` are set to a value beyond their acceptable
900 /// range.
901 /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`].
902 /// - if the date does not exist.
903pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
904// If there is no explicit offset, consider a timestamp value as indication of a UTC value.
905let offset = match (self.offset, self.timestamp) {
906 (Some(off), _) => off,
907 (None, Some(_)) => 0, // UNIX timestamp may assume 0 offset
908(None, None) => return Err(NOT_ENOUGH),
909 };
910let datetime = self.to_naive_datetime_with_offset(offset)?;
911let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?;
912913match offset.from_local_datetime(&datetime) {
914MappedLocalTime::None => Err(IMPOSSIBLE),
915MappedLocalTime::Single(t) => Ok(t),
916MappedLocalTime::Ambiguous(..) => Err(NOT_ENOUGH),
917 }
918 }
919920/// Returns a parsed timezone-aware date and time out of given fields,
921 /// with an additional [`TimeZone`] used to interpret and validate the local date.
922 ///
923 /// This method is able to determine the combined date and time from date and time, and/or from
924 /// a single timestamp field. It checks all fields are consistent with each other.
925 ///
926 /// If the parsed fields include an UTC offset, it also has to be consistent with the offset in
927 /// the provided `tz` time zone for that datetime.
928 ///
929 /// # Errors
930 ///
931 /// This method returns:
932 /// - `IMPOSSIBLE`
933 /// - if any of the date fields conflict, if a timestamp conflicts with any of the other
934 /// fields, or if the offset field is set but differs from the offset at that time in the
935 /// `tz` time zone.
936 /// - if the local datetime does not exists in the provided time zone (because it falls in a
937 /// transition due to for example DST).
938 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime, or if
939 /// the local time in the provided time zone is ambiguous (because it falls in a transition
940 /// due to for example DST) while there is no offset field or timestamp field set.
941 /// - `OUT_OF_RANGE`
942 /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`].
943 /// - if any of the fields of `Parsed` are set to a value beyond their acceptable range.
944 /// - if the date does not exist.
945pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> {
946// if we have `timestamp` specified, guess an offset from that.
947let mut guessed_offset = 0;
948if let Some(timestamp) = self.timestamp {
949// make a naive `DateTime` from given timestamp and (if any) nanosecond.
950 // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
951let nanosecond = self.nanosecond.unwrap_or(0);
952let dt =
953DateTime::from_timestamp(timestamp, nanosecond).ok_or(OUT_OF_RANGE)?.naive_utc();
954guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
955 }
956957// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
958let check_offset = |dt: &DateTime<Tz>| {
959if let Some(offset) = self.offset {
960dt.offset().fix().local_minus_utc() == offset961 } else {
962true
963}
964 };
965966// `guessed_offset` should be correct when `self.timestamp` is given.
967 // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case.
968let datetime = self.to_naive_datetime_with_offset(guessed_offset)?;
969match tz.from_local_datetime(&datetime) {
970MappedLocalTime::None => Err(IMPOSSIBLE),
971MappedLocalTime::Single(t) => {
972if check_offset(&t) {
973Ok(t)
974 } else {
975Err(IMPOSSIBLE)
976 }
977 }
978MappedLocalTime::Ambiguous(min, max) => {
979// try to disambiguate two possible local dates by offset.
980match (check_offset(&min), check_offset(&max)) {
981 (false, false) => Err(IMPOSSIBLE),
982 (false, true) => Ok(max),
983 (true, false) => Ok(min),
984 (true, true) => Err(NOT_ENOUGH),
985 }
986 }
987 }
988 }
989990/// Get the `year` field if set.
991 ///
992 /// See also [`set_year()`](Parsed::set_year).
993#[inline]
994pub fn year(&self) -> Option<i32> {
995self.year
996 }
997998/// Get the `year_div_100` field if set.
999 ///
1000 /// See also [`set_year_div_100()`](Parsed::set_year_div_100).
1001#[inline]
1002pub fn year_div_100(&self) -> Option<i32> {
1003self.year_div_100
1004 }
10051006/// Get the `year_mod_100` field if set.
1007 ///
1008 /// See also [`set_year_mod_100()`](Parsed::set_year_mod_100).
1009#[inline]
1010pub fn year_mod_100(&self) -> Option<i32> {
1011self.year_mod_100
1012 }
10131014/// Get the `isoyear` field that is part of an [ISO 8601 week date] if set.
1015 ///
1016 /// See also [`set_isoyear()`](Parsed::set_isoyear).
1017 ///
1018 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1019#[inline]
1020pub fn isoyear(&self) -> Option<i32> {
1021self.isoyear
1022 }
10231024/// Get the `isoyear_div_100` field that is part of an [ISO 8601 week date] if set.
1025 ///
1026 /// See also [`set_isoyear_div_100()`](Parsed::set_isoyear_div_100).
1027 ///
1028 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1029#[inline]
1030pub fn isoyear_div_100(&self) -> Option<i32> {
1031self.isoyear_div_100
1032 }
10331034/// Get the `isoyear_mod_100` field that is part of an [ISO 8601 week date] if set.
1035 ///
1036 /// See also [`set_isoyear_mod_100()`](Parsed::set_isoyear_mod_100).
1037 ///
1038 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1039#[inline]
1040pub fn isoyear_mod_100(&self) -> Option<i32> {
1041self.isoyear_mod_100
1042 }
10431044/// Get the `quarter` field if set.
1045 ///
1046 /// See also [`set_quarter()`](Parsed::set_quarter).
1047#[inline]
1048pub fn quarter(&self) -> Option<u32> {
1049self.quarter
1050 }
10511052/// Get the `month` field if set.
1053 ///
1054 /// See also [`set_month()`](Parsed::set_month).
1055#[inline]
1056pub fn month(&self) -> Option<u32> {
1057self.month
1058 }
10591060/// Get the `week_from_sun` field if set.
1061 ///
1062 /// See also [`set_week_from_sun()`](Parsed::set_week_from_sun).
1063#[inline]
1064pub fn week_from_sun(&self) -> Option<u32> {
1065self.week_from_sun
1066 }
10671068/// Get the `week_from_mon` field if set.
1069 ///
1070 /// See also [`set_week_from_mon()`](Parsed::set_week_from_mon).
1071#[inline]
1072pub fn week_from_mon(&self) -> Option<u32> {
1073self.week_from_mon
1074 }
10751076/// Get the `isoweek` field that is part of an [ISO 8601 week date] if set.
1077 ///
1078 /// See also [`set_isoweek()`](Parsed::set_isoweek).
1079 ///
1080 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1081#[inline]
1082pub fn isoweek(&self) -> Option<u32> {
1083self.isoweek
1084 }
10851086/// Get the `weekday` field if set.
1087 ///
1088 /// See also [`set_weekday()`](Parsed::set_weekday).
1089#[inline]
1090pub fn weekday(&self) -> Option<Weekday> {
1091self.weekday
1092 }
10931094/// Get the `ordinal` (day of the year) field if set.
1095 ///
1096 /// See also [`set_ordinal()`](Parsed::set_ordinal).
1097#[inline]
1098pub fn ordinal(&self) -> Option<u32> {
1099self.ordinal
1100 }
11011102/// Get the `day` of the month field if set.
1103 ///
1104 /// See also [`set_day()`](Parsed::set_day).
1105#[inline]
1106pub fn day(&self) -> Option<u32> {
1107self.day
1108 }
11091110/// Get the `hour_div_12` field (am/pm) if set.
1111 ///
1112 /// 0 indicates AM and 1 indicates PM.
1113 ///
1114 /// See also [`set_ampm()`](Parsed::set_ampm) and [`set_hour()`](Parsed::set_hour).
1115#[inline]
1116pub fn hour_div_12(&self) -> Option<u32> {
1117self.hour_div_12
1118 }
11191120/// Get the `hour_mod_12` field if set.
1121 ///
1122 /// See also [`set_hour12()`](Parsed::set_hour12) and [`set_hour()`](Parsed::set_hour).
1123pub fn hour_mod_12(&self) -> Option<u32> {
1124self.hour_mod_12
1125 }
11261127/// Get the `minute` field if set.
1128 ///
1129 /// See also [`set_minute()`](Parsed::set_minute).
1130#[inline]
1131pub fn minute(&self) -> Option<u32> {
1132self.minute
1133 }
11341135/// Get the `second` field if set.
1136 ///
1137 /// See also [`set_second()`](Parsed::set_second).
1138#[inline]
1139pub fn second(&self) -> Option<u32> {
1140self.second
1141 }
11421143/// Get the `nanosecond` field if set.
1144 ///
1145 /// See also [`set_nanosecond()`](Parsed::set_nanosecond).
1146#[inline]
1147pub fn nanosecond(&self) -> Option<u32> {
1148self.nanosecond
1149 }
11501151/// Get the `timestamp` field if set.
1152 ///
1153 /// See also [`set_timestamp()`](Parsed::set_timestamp).
1154#[inline]
1155pub fn timestamp(&self) -> Option<i64> {
1156self.timestamp
1157 }
11581159/// Get the `offset` field if set.
1160 ///
1161 /// See also [`set_offset()`](Parsed::set_offset).
1162#[inline]
1163pub fn offset(&self) -> Option<i32> {
1164self.offset
1165 }
1166}
11671168/// Create a `NaiveDate` when given a year, week, weekday, and the definition at which day of the
1169/// week a week starts.
1170///
1171/// Returns `IMPOSSIBLE` if `week` is `0` or `53` and the `weekday` falls outside the year.
1172fn resolve_week_date(
1173 year: i32,
1174 week: u32,
1175 weekday: Weekday,
1176 week_start_day: Weekday,
1177) -> ParseResult<NaiveDate> {
1178if week > 53 {
1179return Err(OUT_OF_RANGE);
1180 }
11811182let first_day_of_year = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
1183// Ordinal of the day at which week 1 starts.
1184let first_week_start = 1 + week_start_day.days_since(first_day_of_year.weekday()) as i32;
1185// Number of the `weekday`, which is 0 for the first day of the week.
1186let weekday = weekday.days_since(week_start_day) as i32;
1187let ordinal = first_week_start + (weekas i32 - 1) * 7 + weekday;
1188if ordinal <= 0 {
1189return Err(IMPOSSIBLE);
1190 }
1191first_day_of_year.with_ordinal(ordinalas u32).ok_or(IMPOSSIBLE)
1192}
11931194#[cfg(test)]
1195mod tests {
1196use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
1197use super::Parsed;
1198use crate::Datelike;
1199use crate::Weekday::*;
1200use crate::naive::{NaiveDate, NaiveTime};
1201use crate::offset::{FixedOffset, TimeZone, Utc};
12021203#[test]
1204fn test_parsed_set_fields() {
1205// year*, isoyear*
1206let mut p = Parsed::new();
1207assert_eq!(p.set_year(1987), Ok(()));
1208assert_eq!(p.set_year(1986), Err(IMPOSSIBLE));
1209assert_eq!(p.set_year(1988), Err(IMPOSSIBLE));
1210assert_eq!(p.set_year(1987), Ok(()));
1211assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year`
1212assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE));
1213assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE));
1214assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto
1215assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE));
1216assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE));
12171218let mut p = Parsed::new();
1219assert_eq!(p.set_year(0), Ok(()));
1220assert_eq!(p.set_year_div_100(0), Ok(()));
1221assert_eq!(p.set_year_mod_100(0), Ok(()));
12221223let mut p = Parsed::new();
1224assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE));
1225assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE));
1226assert_eq!(p.set_year(-1), Ok(()));
1227assert_eq!(p.set_year(-2), Err(IMPOSSIBLE));
1228assert_eq!(p.set_year(0), Err(IMPOSSIBLE));
12291230let mut p = Parsed::new();
1231assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
1232assert_eq!(p.set_year_div_100(8), Ok(()));
1233assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
12341235// month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset
1236let mut p = Parsed::new();
1237assert_eq!(p.set_month(7), Ok(()));
1238assert_eq!(p.set_month(1), Err(IMPOSSIBLE));
1239assert_eq!(p.set_month(6), Err(IMPOSSIBLE));
1240assert_eq!(p.set_month(8), Err(IMPOSSIBLE));
1241assert_eq!(p.set_month(12), Err(IMPOSSIBLE));
12421243let mut p = Parsed::new();
1244assert_eq!(p.set_month(8), Ok(()));
1245assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE));
12461247// hour
1248let mut p = Parsed::new();
1249assert_eq!(p.set_hour(12), Ok(()));
1250assert_eq!(p.set_hour(11), Err(IMPOSSIBLE));
1251assert_eq!(p.set_hour(13), Err(IMPOSSIBLE));
1252assert_eq!(p.set_hour(12), Ok(()));
1253assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE));
1254assert_eq!(p.set_ampm(true), Ok(()));
1255assert_eq!(p.set_hour12(12), Ok(()));
1256assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation
1257assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE));
1258assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE));
12591260let mut p = Parsed::new();
1261assert_eq!(p.set_ampm(true), Ok(()));
1262assert_eq!(p.set_hour12(7), Ok(()));
1263assert_eq!(p.set_hour(7), Err(IMPOSSIBLE));
1264assert_eq!(p.set_hour(18), Err(IMPOSSIBLE));
1265assert_eq!(p.set_hour(19), Ok(()));
12661267// timestamp
1268let mut p = Parsed::new();
1269assert_eq!(p.set_timestamp(1_234_567_890), Ok(()));
1270assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE));
1271assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE));
1272 }
12731274#[test]
1275fn test_parsed_set_range() {
1276assert_eq!(Parsed::new().set_year(i32::MIN as i64 - 1), Err(OUT_OF_RANGE));
1277assert!(Parsed::new().set_year(i32::MIN as i64).is_ok());
1278assert!(Parsed::new().set_year(i32::MAX as i64).is_ok());
1279assert_eq!(Parsed::new().set_year(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
12801281assert_eq!(Parsed::new().set_year_div_100(-1), Err(OUT_OF_RANGE));
1282assert!(Parsed::new().set_year_div_100(0).is_ok());
1283assert!(Parsed::new().set_year_div_100(i32::MAX as i64).is_ok());
1284assert_eq!(Parsed::new().set_year_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
12851286assert_eq!(Parsed::new().set_year_mod_100(-1), Err(OUT_OF_RANGE));
1287assert!(Parsed::new().set_year_mod_100(0).is_ok());
1288assert!(Parsed::new().set_year_mod_100(99).is_ok());
1289assert_eq!(Parsed::new().set_year_mod_100(100), Err(OUT_OF_RANGE));
12901291assert_eq!(Parsed::new().set_isoyear(i32::MIN as i64 - 1), Err(OUT_OF_RANGE));
1292assert!(Parsed::new().set_isoyear(i32::MIN as i64).is_ok());
1293assert!(Parsed::new().set_isoyear(i32::MAX as i64).is_ok());
1294assert_eq!(Parsed::new().set_isoyear(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
12951296assert_eq!(Parsed::new().set_isoyear_div_100(-1), Err(OUT_OF_RANGE));
1297assert!(Parsed::new().set_isoyear_div_100(0).is_ok());
1298assert!(Parsed::new().set_isoyear_div_100(99).is_ok());
1299assert_eq!(Parsed::new().set_isoyear_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
13001301assert_eq!(Parsed::new().set_isoyear_mod_100(-1), Err(OUT_OF_RANGE));
1302assert!(Parsed::new().set_isoyear_mod_100(0).is_ok());
1303assert!(Parsed::new().set_isoyear_mod_100(99).is_ok());
1304assert_eq!(Parsed::new().set_isoyear_mod_100(100), Err(OUT_OF_RANGE));
13051306assert_eq!(Parsed::new().set_quarter(0), Err(OUT_OF_RANGE));
1307assert!(Parsed::new().set_quarter(1).is_ok());
1308assert!(Parsed::new().set_quarter(4).is_ok());
1309assert_eq!(Parsed::new().set_quarter(5), Err(OUT_OF_RANGE));
13101311assert_eq!(Parsed::new().set_month(0), Err(OUT_OF_RANGE));
1312assert!(Parsed::new().set_month(1).is_ok());
1313assert!(Parsed::new().set_month(12).is_ok());
1314assert_eq!(Parsed::new().set_month(13), Err(OUT_OF_RANGE));
13151316assert_eq!(Parsed::new().set_week_from_sun(-1), Err(OUT_OF_RANGE));
1317assert!(Parsed::new().set_week_from_sun(0).is_ok());
1318assert!(Parsed::new().set_week_from_sun(53).is_ok());
1319assert_eq!(Parsed::new().set_week_from_sun(54), Err(OUT_OF_RANGE));
13201321assert_eq!(Parsed::new().set_week_from_mon(-1), Err(OUT_OF_RANGE));
1322assert!(Parsed::new().set_week_from_mon(0).is_ok());
1323assert!(Parsed::new().set_week_from_mon(53).is_ok());
1324assert_eq!(Parsed::new().set_week_from_mon(54), Err(OUT_OF_RANGE));
13251326assert_eq!(Parsed::new().set_isoweek(0), Err(OUT_OF_RANGE));
1327assert!(Parsed::new().set_isoweek(1).is_ok());
1328assert!(Parsed::new().set_isoweek(53).is_ok());
1329assert_eq!(Parsed::new().set_isoweek(54), Err(OUT_OF_RANGE));
13301331assert_eq!(Parsed::new().set_ordinal(0), Err(OUT_OF_RANGE));
1332assert!(Parsed::new().set_ordinal(1).is_ok());
1333assert!(Parsed::new().set_ordinal(366).is_ok());
1334assert_eq!(Parsed::new().set_ordinal(367), Err(OUT_OF_RANGE));
13351336assert_eq!(Parsed::new().set_day(0), Err(OUT_OF_RANGE));
1337assert!(Parsed::new().set_day(1).is_ok());
1338assert!(Parsed::new().set_day(31).is_ok());
1339assert_eq!(Parsed::new().set_day(32), Err(OUT_OF_RANGE));
13401341assert_eq!(Parsed::new().set_hour12(0), Err(OUT_OF_RANGE));
1342assert!(Parsed::new().set_hour12(1).is_ok());
1343assert!(Parsed::new().set_hour12(12).is_ok());
1344assert_eq!(Parsed::new().set_hour12(13), Err(OUT_OF_RANGE));
13451346assert_eq!(Parsed::new().set_hour(-1), Err(OUT_OF_RANGE));
1347assert!(Parsed::new().set_hour(0).is_ok());
1348assert!(Parsed::new().set_hour(23).is_ok());
1349assert_eq!(Parsed::new().set_hour(24), Err(OUT_OF_RANGE));
13501351assert_eq!(Parsed::new().set_minute(-1), Err(OUT_OF_RANGE));
1352assert!(Parsed::new().set_minute(0).is_ok());
1353assert!(Parsed::new().set_minute(59).is_ok());
1354assert_eq!(Parsed::new().set_minute(60), Err(OUT_OF_RANGE));
13551356assert_eq!(Parsed::new().set_second(-1), Err(OUT_OF_RANGE));
1357assert!(Parsed::new().set_second(0).is_ok());
1358assert!(Parsed::new().set_second(60).is_ok());
1359assert_eq!(Parsed::new().set_second(61), Err(OUT_OF_RANGE));
13601361assert_eq!(Parsed::new().set_nanosecond(-1), Err(OUT_OF_RANGE));
1362assert!(Parsed::new().set_nanosecond(0).is_ok());
1363assert!(Parsed::new().set_nanosecond(999_999_999).is_ok());
1364assert_eq!(Parsed::new().set_nanosecond(1_000_000_000), Err(OUT_OF_RANGE));
13651366assert!(Parsed::new().set_timestamp(i64::MIN).is_ok());
1367assert!(Parsed::new().set_timestamp(i64::MAX).is_ok());
13681369assert_eq!(Parsed::new().set_offset(i32::MIN as i64 - 1), Err(OUT_OF_RANGE));
1370assert!(Parsed::new().set_offset(i32::MIN as i64).is_ok());
1371assert!(Parsed::new().set_offset(i32::MAX as i64).is_ok());
1372assert_eq!(Parsed::new().set_offset(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
1373 }
13741375#[test]
1376fn test_parsed_to_naive_date() {
1377macro_rules! parse {
1378 ($($k:ident: $v:expr),*) => (
1379 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date()
1380 )
1381 }
13821383let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap());
13841385// ymd: omission of fields
1386assert_eq!(parse!(), Err(NOT_ENOUGH));
1387assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH));
1388assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH));
1389assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2));
1390assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH));
1391assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH));
1392assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH));
1393assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH));
1394assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2));
1395assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH));
1396assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH));
1397assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2));
1398assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2));
13991400// ymd: out-of-range conditions
1401assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29));
1402assert_eq!(
1403parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29),
1404Err(OUT_OF_RANGE)
1405 );
1406assert_eq!(
1407parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1),
1408Err(OUT_OF_RANGE)
1409 );
1410assert_eq!(
1411parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31),
1412 ymd(1983, 12, 31)
1413 );
1414assert_eq!(
1415parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32),
1416Err(OUT_OF_RANGE)
1417 );
1418assert_eq!(
1419parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0),
1420Err(OUT_OF_RANGE)
1421 );
1422assert_eq!(
1423parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1),
1424Err(OUT_OF_RANGE)
1425 );
1426assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE));
1427assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1));
1428assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(IMPOSSIBLE));
1429let max_year = NaiveDate::MAX.year();
1430assert_eq!(
1431parse!(year_div_100: max_year / 100,
1432 year_mod_100: max_year % 100, month: 1, day: 1),
1433 ymd(max_year, 1, 1)
1434 );
1435assert_eq!(
1436parse!(year_div_100: (max_year + 1) / 100,
1437 year_mod_100: (max_year + 1) % 100, month: 1, day: 1),
1438Err(OUT_OF_RANGE)
1439 );
14401441// ymd: conflicting inputs
1442assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1));
1443assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE));
1444assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1));
1445assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE));
1446assert_eq!(
1447parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1),
1448 ymd(1984, 1, 1)
1449 );
1450assert_eq!(
1451parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1),
1452Err(IMPOSSIBLE)
1453 );
1454assert_eq!(
1455parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1),
1456Err(OUT_OF_RANGE)
1457 );
1458assert_eq!(
1459parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1),
1460Err(OUT_OF_RANGE)
1461 );
1462assert_eq!(
1463parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1),
1464Err(IMPOSSIBLE)
1465 );
1466assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(IMPOSSIBLE));
1467assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(IMPOSSIBLE));
14681469// quarters
1470assert_eq!(parse!(year: 2000, quarter: 1), Err(NOT_ENOUGH));
1471assert_eq!(parse!(year: 2000, quarter: 1, month: 1, day: 1), ymd(2000, 1, 1));
1472assert_eq!(parse!(year: 2000, quarter: 2, month: 4, day: 1), ymd(2000, 4, 1));
1473assert_eq!(parse!(year: 2000, quarter: 3, month: 7, day: 1), ymd(2000, 7, 1));
1474assert_eq!(parse!(year: 2000, quarter: 4, month: 10, day: 1), ymd(2000, 10, 1));
14751476// quarter: conflicting inputs
1477assert_eq!(parse!(year: 2000, quarter: 2, month: 3, day: 31), Err(IMPOSSIBLE));
1478assert_eq!(parse!(year: 2000, quarter: 4, month: 3, day: 31), Err(IMPOSSIBLE));
14791480// weekdates
1481assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH));
1482assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH));
1483assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH));
1484assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(IMPOSSIBLE));
1485assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(IMPOSSIBLE));
1486assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1));
1487assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1));
1488assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2));
1489assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2));
1490assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3));
1491assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3));
1492assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8));
1493assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8));
1494assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9));
1495assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9));
1496assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10));
1497assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30));
1498assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31));
1499assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(IMPOSSIBLE));
1500assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE));
1501assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(IMPOSSIBLE));
1502assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1));
15031504// weekdates: conflicting inputs
1505assert_eq!(
1506parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat),
1507 ymd(2000, 1, 8)
1508 );
1509assert_eq!(
1510parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun),
1511 ymd(2000, 1, 9)
1512 );
1513assert_eq!(
1514parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun),
1515Err(IMPOSSIBLE)
1516 );
1517assert_eq!(
1518parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun),
1519Err(IMPOSSIBLE)
1520 );
15211522// ISO weekdates
1523assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH));
1524assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31));
1525assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1));
1526assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE));
1527assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE));
1528assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3));
1529assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH));
15301531// year and ordinal
1532assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH));
1533assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE));
1534assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1));
1535assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29));
1536assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1));
1537assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31));
1538assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE));
1539assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
1540assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE));
1541assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1));
1542assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28));
1543assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1));
1544assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31));
1545assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE));
1546assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
15471548// more complex cases
1549assert_eq!(
1550parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1,
1551 week_from_sun: 52, week_from_mon: 52, weekday: Wed),
1552 ymd(2014, 12, 31)
1553 );
1554assert_eq!(
1555parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1,
1556 week_from_sun: 52, week_from_mon: 52),
1557 ymd(2014, 12, 31)
1558 );
1559assert_eq!(
1560parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53,
1561 week_from_sun: 52, week_from_mon: 52, weekday: Wed),
1562Err(IMPOSSIBLE)
1563 ); // no ISO week date 2014-W53-3
1564assert_eq!(
1565parse!(year: 2012, isoyear: 2015, isoweek: 1,
1566 week_from_sun: 52, week_from_mon: 52),
1567Err(NOT_ENOUGH)
1568 ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31)
1569assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH));
1570// technically unique (2014-12-31) but Chrono gives up
1571}
15721573#[test]
1574fn test_parsed_to_naive_time() {
1575macro_rules! parse {
1576 ($($k:ident: $v:expr),*) => (
1577 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time()
1578 )
1579 }
15801581let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap());
1582let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap());
15831584// omission of fields
1585assert_eq!(parse!(), Err(NOT_ENOUGH));
1586assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH));
1587assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH));
1588assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0));
1589assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45));
1590assert_eq!(
1591parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45,
1592 nanosecond: 678_901_234),
1593 hmsn(1, 23, 45, 678_901_234)
1594 );
1595assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6));
1596assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH));
1597assert_eq!(
1598parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012),
1599Err(NOT_ENOUGH)
1600 );
16011602// out-of-range conditions
1603assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE));
1604assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE));
1605assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE));
1606assert_eq!(
1607parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61),
1608Err(OUT_OF_RANGE)
1609 );
1610assert_eq!(
1611parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34,
1612 nanosecond: 1_000_000_000),
1613Err(OUT_OF_RANGE)
1614 );
16151616// leap seconds
1617assert_eq!(
1618parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60),
1619 hmsn(1, 23, 59, 1_000_000_000)
1620 );
1621assert_eq!(
1622parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60,
1623 nanosecond: 999_999_999),
1624 hmsn(1, 23, 59, 1_999_999_999)
1625 );
1626 }
16271628#[test]
1629fn test_parsed_to_naive_datetime_with_offset() {
1630macro_rules! parse {
1631 (offset = $offset:expr; $($k:ident: $v:expr),*) => (
1632 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset)
1633 );
1634 ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*))
1635 }
16361637let ymdhms = |y, m, d, h, n, s| {
1638Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap())
1639 };
1640let ymdhmsn = |y, m, d, h, n, s, nano| {
1641Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap())
1642 };
16431644// omission of fields
1645assert_eq!(parse!(), Err(NOT_ENOUGH));
1646assert_eq!(
1647parse!(year: 2015, month: 1, day: 30,
1648 hour_div_12: 1, hour_mod_12: 2, minute: 38),
1649 ymdhms(2015, 1, 30, 14, 38, 0)
1650 );
1651assert_eq!(
1652parse!(year: 1997, month: 1, day: 30,
1653 hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5),
1654 ymdhms(1997, 1, 30, 14, 38, 5)
1655 );
1656assert_eq!(
1657parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5,
1658 minute: 6, second: 7, nanosecond: 890_123_456),
1659 ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456)
1660 );
1661assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0));
1662assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1));
1663assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1));
1664assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40));
1665assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44));
16661667// full fields
1668assert_eq!(
1669parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1670 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1671 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1672 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1673 nanosecond: 12_345_678, timestamp: 1_420_000_000),
1674 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
1675 );
1676assert_eq!(
1677parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1678 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1679 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1680 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1681 nanosecond: 12_345_678, timestamp: 1_419_999_999),
1682Err(IMPOSSIBLE)
1683 );
1684assert_eq!(
1685parse!(offset = 32400;
1686 year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1687 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1688 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1689 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1690 nanosecond: 12_345_678, timestamp: 1_419_967_600),
1691 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
1692 );
16931694// more timestamps
1695let max_days_from_year_1970 =
1696 NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1697let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1)
1698 .unwrap()
1699 .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1700let min_days_from_year_1970 =
1701 NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1702assert_eq!(
1703parse!(timestamp: min_days_from_year_1970.num_seconds()),
1704 ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0)
1705 );
1706assert_eq!(
1707parse!(timestamp: year_0_from_year_1970.num_seconds()),
1708 ymdhms(0, 1, 1, 0, 0, 0)
1709 );
1710assert_eq!(
1711parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
1712 ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59)
1713 );
17141715// leap seconds #1: partial fields
1716assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
1717assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59));
1718assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
1719assert_eq!(
1720parse!(second: 60, timestamp: 1_341_100_799),
1721 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1722 );
1723assert_eq!(
1724parse!(second: 60, timestamp: 1_341_100_800),
1725 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1726 );
1727assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0));
1728assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
1729assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE));
17301731// leap seconds #2: full fields
1732 // we need to have separate tests for them since it uses another control flow.
1733assert_eq!(
1734parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1735 minute: 59, second: 59, timestamp: 1_341_100_798),
1736Err(IMPOSSIBLE)
1737 );
1738assert_eq!(
1739parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1740 minute: 59, second: 59, timestamp: 1_341_100_799),
1741 ymdhms(2012, 6, 30, 23, 59, 59)
1742 );
1743assert_eq!(
1744parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1745 minute: 59, second: 59, timestamp: 1_341_100_800),
1746Err(IMPOSSIBLE)
1747 );
1748assert_eq!(
1749parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1750 minute: 59, second: 60, timestamp: 1_341_100_799),
1751 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1752 );
1753assert_eq!(
1754parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1755 minute: 59, second: 60, timestamp: 1_341_100_800),
1756 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1757 );
1758assert_eq!(
1759parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
1760 minute: 0, second: 0, timestamp: 1_341_100_800),
1761 ymdhms(2012, 7, 1, 0, 0, 0)
1762 );
1763assert_eq!(
1764parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
1765 minute: 0, second: 1, timestamp: 1_341_100_800),
1766Err(IMPOSSIBLE)
1767 );
1768assert_eq!(
1769parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1770 minute: 59, second: 60, timestamp: 1_341_100_801),
1771Err(IMPOSSIBLE)
1772 );
17731774// error codes
1775assert_eq!(
1776parse!(year: 2015, month: 1, day: 20, weekday: Tue,
1777 hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20),
1778Err(OUT_OF_RANGE)
1779 ); // `hour_div_12` is out of range
1780}
17811782#[test]
1783fn test_parsed_to_datetime() {
1784macro_rules! parse {
1785 ($($k:ident: $v:expr),*) => (
1786 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime()
1787 )
1788 }
17891790let ymdhmsn = |y, m, d, h, n, s, nano, off| {
1791Ok(FixedOffset::east_opt(off)
1792 .unwrap()
1793 .from_local_datetime(
1794&NaiveDate::from_ymd_opt(y, m, d)
1795 .unwrap()
1796 .and_hms_nano_opt(h, n, s, nano)
1797 .unwrap(),
1798 )
1799 .unwrap())
1800 };
18011802assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH));
1803assert_eq!(
1804parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1805 minute: 26, second: 40, nanosecond: 12_345_678),
1806Err(NOT_ENOUGH)
1807 );
1808assert_eq!(
1809parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1810 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1811 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0)
1812 );
1813assert_eq!(
1814parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1815 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1816 ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400)
1817 );
1818assert_eq!(
1819parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1,
1820 minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876),
1821 ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876)
1822 );
1823assert_eq!(
1824parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4,
1825 minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400),
1826Err(OUT_OF_RANGE)
1827 ); // `FixedOffset` does not support such huge offset
1828}
18291830#[test]
1831fn test_parsed_to_datetime_with_timezone() {
1832macro_rules! parse {
1833 ($tz:expr; $($k:ident: $v:expr),*) => (
1834 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz)
1835 )
1836 }
18371838// single result from ymdhms
1839assert_eq!(
1840parse!(Utc;
1841 year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1842 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1843Ok(Utc
1844 .from_local_datetime(
1845&NaiveDate::from_ymd_opt(2014, 12, 31)
1846 .unwrap()
1847 .and_hms_nano_opt(4, 26, 40, 12_345_678)
1848 .unwrap()
1849 )
1850 .unwrap())
1851 );
1852assert_eq!(
1853parse!(Utc;
1854 year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1855 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1856Err(IMPOSSIBLE)
1857 );
1858assert_eq!(
1859parse!(FixedOffset::east_opt(32400).unwrap();
1860 year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1861 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1862Err(IMPOSSIBLE)
1863 );
1864assert_eq!(
1865parse!(FixedOffset::east_opt(32400).unwrap();
1866 year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1867 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1868Ok(FixedOffset::east_opt(32400)
1869 .unwrap()
1870 .from_local_datetime(
1871&NaiveDate::from_ymd_opt(2014, 12, 31)
1872 .unwrap()
1873 .and_hms_nano_opt(13, 26, 40, 12_345_678)
1874 .unwrap()
1875 )
1876 .unwrap())
1877 );
18781879// single result from timestamp
1880assert_eq!(
1881parse!(Utc; timestamp: 1_420_000_000, offset: 0),
1882Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap())
1883 );
1884assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE));
1885assert_eq!(
1886parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0),
1887Err(IMPOSSIBLE)
1888 );
1889assert_eq!(
1890parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400),
1891Ok(FixedOffset::east_opt(32400)
1892 .unwrap()
1893 .with_ymd_and_hms(2014, 12, 31, 13, 26, 40)
1894 .unwrap())
1895 );
18961897// TODO test with a variable time zone (for None and Ambiguous cases)
1898}
18991900#[test]
1901fn issue_551() {
1902use crate::Weekday;
1903let mut parsed = Parsed::new();
19041905 parsed.year = Some(2002);
1906 parsed.week_from_mon = Some(22);
1907 parsed.weekday = Some(Weekday::Mon);
1908assert_eq!(NaiveDate::from_ymd_opt(2002, 6, 3).unwrap(), parsed.to_naive_date().unwrap());
19091910 parsed.year = Some(2001);
1911assert_eq!(NaiveDate::from_ymd_opt(2001, 5, 28).unwrap(), parsed.to_naive_date().unwrap());
1912 }
1913}