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)]
129pub struct Parsed {
130#[doc(hidden)]
131pub year: Option<i32>,
132#[doc(hidden)]
133pub year_div_100: Option<i32>,
134#[doc(hidden)]
135pub year_mod_100: Option<i32>,
136#[doc(hidden)]
137pub isoyear: Option<i32>,
138#[doc(hidden)]
139pub isoyear_div_100: Option<i32>,
140#[doc(hidden)]
141pub isoyear_mod_100: Option<i32>,
142#[doc(hidden)]
143pub quarter: Option<u32>,
144#[doc(hidden)]
145pub month: Option<u32>,
146#[doc(hidden)]
147pub week_from_sun: Option<u32>,
148#[doc(hidden)]
149pub week_from_mon: Option<u32>,
150#[doc(hidden)]
151pub isoweek: Option<u32>,
152#[doc(hidden)]
153pub weekday: Option<Weekday>,
154#[doc(hidden)]
155pub ordinal: Option<u32>,
156#[doc(hidden)]
157pub day: Option<u32>,
158#[doc(hidden)]
159pub hour_div_12: Option<u32>,
160#[doc(hidden)]
161pub hour_mod_12: Option<u32>,
162#[doc(hidden)]
163pub minute: Option<u32>,
164#[doc(hidden)]
165pub second: Option<u32>,
166#[doc(hidden)]
167pub nanosecond: Option<u32>,
168#[doc(hidden)]
169pub timestamp: Option<i64>,
170#[doc(hidden)]
171pub offset: Option<i32>,
172#[doc(hidden)]
173_dummy: (),
174}
175176/// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"),
177/// and if it is empty, set `old` to `new` as well.
178#[inline]
179fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
180match old {
181Some(old) if *old != new => Err(IMPOSSIBLE),
182_ => {
183*old = Some(new);
184Ok(())
185 }
186 }
187}
188189impl Parsed {
190/// Returns the initial value of parsed parts.
191#[must_use]
192pub fn new() -> Parsed {
193Parsed::default()
194 }
195196/// Set the [`year`](Parsed::year) field to the given value.
197 ///
198 /// The value can be negative, unlike the [`year_div_100`](Parsed::year_div_100) and
199 /// [`year_mod_100`](Parsed::year_mod_100) fields.
200 ///
201 /// # Errors
202 ///
203 /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`.
204 ///
205 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
206#[inline]
207pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
208set_if_consistent(&mut self.year, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
209 }
210211/// Set the [`year_div_100`](Parsed::year_div_100) field to the given value.
212 ///
213 /// # Errors
214 ///
215 /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`.
216 ///
217 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
218#[inline]
219pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
220if !(0..=i32::MAXas i64).contains(&value) {
221return Err(OUT_OF_RANGE);
222 }
223set_if_consistent(&mut self.year_div_100, valueas i32)
224 }
225226/// Set the [`year_mod_100`](Parsed::year_mod_100) field to the given value.
227 ///
228 /// When set it implies that the year is not negative.
229 ///
230 /// If this field is set while the [`year_div_100`](Parsed::year_div_100) field is missing (and
231 /// the full [`year`](Parsed::year) field is also not set), it assumes a default value for the
232 /// [`year_div_100`](Parsed::year_div_100) field.
233 /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise.
234 ///
235 /// # Errors
236 ///
237 /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99.
238 ///
239 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
240#[inline]
241pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
242if !(0..100).contains(&value) {
243return Err(OUT_OF_RANGE);
244 }
245set_if_consistent(&mut self.year_mod_100, valueas i32)
246 }
247248/// Set the [`isoyear`](Parsed::isoyear) field, that is part of an [ISO 8601 week date], to the
249 /// given value.
250 ///
251 /// The value can be negative, unlike the [`isoyear_div_100`](Parsed::isoyear_div_100) and
252 /// [`isoyear_mod_100`](Parsed::isoyear_mod_100) fields.
253 ///
254 /// [ISO 8601 week date]: crate::NaiveDate#week-date
255 ///
256 /// # Errors
257 ///
258 /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`.
259 ///
260 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
261#[inline]
262pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
263set_if_consistent(&mut self.isoyear, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
264 }
265266/// Set the [`isoyear_div_100`](Parsed::isoyear_div_100) field, that is part of an
267 /// [ISO 8601 week date], to the given value.
268 ///
269 /// [ISO 8601 week date]: crate::NaiveDate#week-date
270 ///
271 /// # Errors
272 ///
273 /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`.
274 ///
275 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
276#[inline]
277pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
278if !(0..=i32::MAXas i64).contains(&value) {
279return Err(OUT_OF_RANGE);
280 }
281set_if_consistent(&mut self.isoyear_div_100, valueas i32)
282 }
283284/// Set the [`isoyear_mod_100`](Parsed::isoyear_mod_100) field, that is part of an
285 /// [ISO 8601 week date], to the given value.
286 ///
287 /// When set it implies that the year is not negative.
288 ///
289 /// If this field is set while the [`isoyear_div_100`](Parsed::isoyear_div_100) field is missing
290 /// (and the full [`isoyear`](Parsed::isoyear) field is also not set), it assumes a default
291 /// value for the [`isoyear_div_100`](Parsed::isoyear_div_100) field.
292 /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise.
293 ///
294 /// [ISO 8601 week date]: crate::NaiveDate#week-date
295 ///
296 /// # Errors
297 ///
298 /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99.
299 ///
300 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
301#[inline]
302pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
303if !(0..100).contains(&value) {
304return Err(OUT_OF_RANGE);
305 }
306set_if_consistent(&mut self.isoyear_mod_100, valueas i32)
307 }
308309/// Set the [`quarter`](Parsed::quarter) field to the given value.
310 ///
311 /// Quarter 1 starts in January.
312 ///
313 /// # Errors
314 ///
315 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-4.
316 ///
317 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
318#[inline]
319pub fn set_quarter(&mut self, value: i64) -> ParseResult<()> {
320if !(1..=4).contains(&value) {
321return Err(OUT_OF_RANGE);
322 }
323set_if_consistent(&mut self.quarter, valueas u32)
324 }
325326/// Set the [`month`](Parsed::month) field to the given value.
327 ///
328 /// # Errors
329 ///
330 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12.
331 ///
332 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
333#[inline]
334pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
335if !(1..=12).contains(&value) {
336return Err(OUT_OF_RANGE);
337 }
338set_if_consistent(&mut self.month, valueas u32)
339 }
340341/// Set the [`week_from_sun`](Parsed::week_from_sun) week number field to the given value.
342 ///
343 /// Week 1 starts at the first Sunday of January.
344 ///
345 /// # Errors
346 ///
347 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53.
348 ///
349 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
350#[inline]
351pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
352if !(0..=53).contains(&value) {
353return Err(OUT_OF_RANGE);
354 }
355set_if_consistent(&mut self.week_from_sun, valueas u32)
356 }
357358/// Set the [`week_from_mon`](Parsed::week_from_mon) week number field to the given value.
359 /// Set the 'week number starting with Monday' field to the given value.
360 ///
361 /// Week 1 starts at the first Monday of January.
362 ///
363 /// # Errors
364 ///
365 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53.
366 ///
367 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
368#[inline]
369pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
370if !(0..=53).contains(&value) {
371return Err(OUT_OF_RANGE);
372 }
373set_if_consistent(&mut self.week_from_mon, valueas u32)
374 }
375376/// Set the [`isoweek`](Parsed::isoweek) field for an [ISO 8601 week date] to the given value.
377 ///
378 /// [ISO 8601 week date]: crate::NaiveDate#week-date
379 ///
380 /// # Errors
381 ///
382 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-53.
383 ///
384 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
385#[inline]
386pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
387if !(1..=53).contains(&value) {
388return Err(OUT_OF_RANGE);
389 }
390set_if_consistent(&mut self.isoweek, valueas u32)
391 }
392393/// Set the [`weekday`](Parsed::weekday) field to the given value.
394 ///
395 /// # Errors
396 ///
397 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
398#[inline]
399pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
400set_if_consistent(&mut self.weekday, value)
401 }
402403/// Set the [`ordinal`](Parsed::ordinal) (day of the year) field to the given value.
404 ///
405 /// # Errors
406 ///
407 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-366.
408 ///
409 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
410#[inline]
411pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
412if !(1..=366).contains(&value) {
413return Err(OUT_OF_RANGE);
414 }
415set_if_consistent(&mut self.ordinal, valueas u32)
416 }
417418/// Set the [`day`](Parsed::day) of the month field to the given value.
419 ///
420 /// # Errors
421 ///
422 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-31.
423 ///
424 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
425#[inline]
426pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
427if !(1..=31).contains(&value) {
428return Err(OUT_OF_RANGE);
429 }
430set_if_consistent(&mut self.day, valueas u32)
431 }
432433/// Set the [`hour_div_12`](Parsed::hour_div_12) am/pm field to the given value.
434 ///
435 /// `false` indicates AM and `true` indicates PM.
436 ///
437 /// # Errors
438 ///
439 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
440#[inline]
441pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
442set_if_consistent(&mut self.hour_div_12, valueas u32)
443 }
444445/// Set the [`hour_mod_12`](Parsed::hour_mod_12) field, for the hour number in 12-hour clocks,
446 /// to the given value.
447 ///
448 /// Value must be in the canonical range of 1-12.
449 /// It will internally be stored as 0-11 (`value % 12`).
450 ///
451 /// # Errors
452 ///
453 /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12.
454 ///
455 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
456#[inline]
457pub fn set_hour12(&mut self, mut value: i64) -> ParseResult<()> {
458if !(1..=12).contains(&value) {
459return Err(OUT_OF_RANGE);
460 }
461if value == 12 {
462value = 0
463}
464set_if_consistent(&mut self.hour_mod_12, valueas u32)
465 }
466467/// Set the [`hour_div_12`](Parsed::hour_div_12) and [`hour_mod_12`](Parsed::hour_mod_12)
468 /// fields to the given value for a 24-hour clock.
469 ///
470 /// # Errors
471 ///
472 /// May return `OUT_OF_RANGE` if `value` is not in the range 0-23.
473 /// Currently only checks the value is not out of range for a `u32`.
474 ///
475 /// Returns `IMPOSSIBLE` one of the fields was already set to a different value.
476#[inline]
477pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
478let (hour_div_12, hour_mod_12) = match value {
479 hour @ 0..=11 => (0, houras u32),
480 hour @ 12..=23 => (1, houras u32 - 12),
481_ => return Err(OUT_OF_RANGE),
482 };
483set_if_consistent(&mut self.hour_div_12, hour_div_12)?;
484set_if_consistent(&mut self.hour_mod_12, hour_mod_12)
485 }
486487/// Set the [`minute`](Parsed::minute) field to the given value.
488 ///
489 /// # Errors
490 ///
491 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-59.
492 ///
493 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
494#[inline]
495pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
496if !(0..=59).contains(&value) {
497return Err(OUT_OF_RANGE);
498 }
499set_if_consistent(&mut self.minute, valueas u32)
500 }
501502/// Set the [`second`](Parsed::second) field to the given value.
503 ///
504 /// The value can be 60 in the case of a leap second.
505 ///
506 /// # Errors
507 ///
508 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-60.
509 ///
510 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
511#[inline]
512pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
513if !(0..=60).contains(&value) {
514return Err(OUT_OF_RANGE);
515 }
516set_if_consistent(&mut self.second, valueas u32)
517 }
518519/// Set the [`nanosecond`](Parsed::nanosecond) field to the given value.
520 ///
521 /// This is the number of nanoseconds since the whole second.
522 ///
523 /// # Errors
524 ///
525 /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-999,999,999.
526 ///
527 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
528#[inline]
529pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
530if !(0..=999_999_999).contains(&value) {
531return Err(OUT_OF_RANGE);
532 }
533set_if_consistent(&mut self.nanosecond, valueas u32)
534 }
535536/// Set the [`timestamp`](Parsed::timestamp) field to the given value.
537 ///
538 /// A Unix timestamp is defined as the number of non-leap seconds since midnight UTC on
539 /// January 1, 1970.
540 ///
541 /// # Errors
542 ///
543 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
544#[inline]
545pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
546set_if_consistent(&mut self.timestamp, value)
547 }
548549/// Set the [`offset`](Parsed::offset) field to the given value.
550 ///
551 /// The offset is in seconds from local time to UTC.
552 ///
553 /// # Errors
554 ///
555 /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`.
556 ///
557 /// Returns `IMPOSSIBLE` if this field was already set to a different value.
558#[inline]
559pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
560set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
561 }
562563/// Returns a parsed naive date out of given fields.
564 ///
565 /// This method is able to determine the date from given subset of fields:
566 ///
567 /// - Year, month, day.
568 /// - Year, day of the year (ordinal).
569 /// - Year, week number counted from Sunday or Monday, day of the week.
570 /// - ISO week date.
571 ///
572 /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted,
573 /// the two-digit year is used to guess the century number then.
574 ///
575 /// It checks all given date fields are consistent with each other.
576 ///
577 /// # Errors
578 ///
579 /// This method returns:
580 /// - `IMPOSSIBLE` if any of the date fields conflict.
581 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete date.
582 /// - `OUT_OF_RANGE`
583 /// - if any of the date fields of `Parsed` are set to a value beyond their acceptable range.
584 /// - if the value would be outside the range of a [`NaiveDate`].
585 /// - if the date does not exist.
586pub fn to_naive_date(&self) -> ParseResult<NaiveDate> {
587fn resolve_year(
588 y: Option<i32>,
589 q: Option<i32>,
590 r: Option<i32>,
591 ) -> ParseResult<Option<i32>> {
592match (y, q, r) {
593// if there is no further information, simply return the given full year.
594 // this is a common case, so let's avoid division here.
595(y, None, None) => Ok(y),
596597// if there is a full year *and* also quotient and/or modulo,
598 // check if present quotient and/or modulo is consistent to the full year.
599 // since the presence of those fields means a positive full year,
600 // we should filter a negative full year first.
601(Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => {
602if y < 0 {
603return Err(IMPOSSIBLE);
604 }
605let q_ = y / 100;
606let r_ = y % 100;
607if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ {
608Ok(Some(y))
609 } else {
610Err(IMPOSSIBLE)
611 }
612 }
613614// the full year is missing but we have quotient and modulo.
615 // reconstruct the full year. make sure that the result is always positive.
616(None, Some(q), Some(r @ 0..=99)) => {
617if q < 0 {
618return Err(IMPOSSIBLE);
619 }
620let y = q.checked_mul(100).and_then(|v| v.checked_add(r));
621Ok(Some(y.ok_or(OUT_OF_RANGE)?))
622 }
623624// we only have modulo. try to interpret a modulo as a conventional two-digit year.
625 // note: we are affected by Rust issue #18060. avoid multiple range patterns.
626(None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })),
627628// otherwise it is an out-of-bound or insufficient condition.
629(None, Some(_), None) => Err(NOT_ENOUGH),
630 (_, _, Some(_)) => Err(OUT_OF_RANGE),
631 }
632 }
633634let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?;
635let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?;
636637// verify the normal year-month-day date.
638let verify_ymd = |date: NaiveDate| {
639let year = date.year();
640let (year_div_100, year_mod_100) = if year >= 0 {
641 (Some(year / 100), Some(year % 100))
642 } else {
643 (None, None) // they should be empty to be consistent
644};
645let month = date.month();
646let day = date.day();
647self.year.unwrap_or(year) == year648 && self.year_div_100.or(year_div_100) == year_div_100649 && self.year_mod_100.or(year_mod_100) == year_mod_100650 && self.month.unwrap_or(month) == month651 && self.day.unwrap_or(day) == day652 };
653654// verify the ISO week date.
655let verify_isoweekdate = |date: NaiveDate| {
656let week = date.iso_week();
657let isoyear = week.year();
658let isoweek = week.week();
659let weekday = date.weekday();
660let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 {
661 (Some(isoyear / 100), Some(isoyear % 100))
662 } else {
663 (None, None) // they should be empty to be consistent
664};
665self.isoyear.unwrap_or(isoyear) == isoyear666 && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100667 && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100668 && self.isoweek.unwrap_or(isoweek) == isoweek669 && self.weekday.unwrap_or(weekday) == weekday670 };
671672// verify the ordinal and other (non-ISO) week dates.
673let verify_ordinal = |date: NaiveDate| {
674let ordinal = date.ordinal();
675let week_from_sun = date.weeks_from(Weekday::Sun);
676let week_from_mon = date.weeks_from(Weekday::Mon);
677self.ordinal.unwrap_or(ordinal) == ordinal678 && self.week_from_sun.map_or(week_from_sun, |v| vas i32) == week_from_sun679 && self.week_from_mon.map_or(week_from_mon, |v| vas i32) == week_from_mon680 };
681682// test several possibilities.
683 // tries to construct a full `NaiveDate` as much as possible, then verifies that
684 // it is consistent with other given fields.
685let (verified, parsed_date) = match (given_year, given_isoyear, self) {
686 (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => {
687// year, month, day
688let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?;
689 (verify_isoweekdate(date) && verify_ordinal(date), date)
690 }
691692 (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
693// year, day of the year
694let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?;
695 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
696 }
697698 (Some(year), _, &Parsed { week_from_sun: Some(week), weekday: Some(weekday), .. }) => {
699// year, week (starting at 1st Sunday), day of the week
700let date = resolve_week_date(year, week, weekday, Weekday::Sun)?;
701 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
702 }
703704 (Some(year), _, &Parsed { week_from_mon: Some(week), weekday: Some(weekday), .. }) => {
705// year, week (starting at 1st Monday), day of the week
706let date = resolve_week_date(year, week, weekday, Weekday::Mon)?;
707 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
708 }
709710 (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => {
711// ISO year, week, day of the week
712let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday);
713let date = date.ok_or(OUT_OF_RANGE)?;
714 (verify_ymd(date) && verify_ordinal(date), date)
715 }
716717 (_, _, _) => return Err(NOT_ENOUGH),
718 };
719720if !verified {
721return Err(IMPOSSIBLE);
722 } else if let Some(parsed) = self.quarter {
723if parsed != parsed_date.quarter() {
724return Err(IMPOSSIBLE);
725 }
726 }
727728Ok(parsed_date)
729 }
730731/// Returns a parsed naive time out of given fields.
732 ///
733 /// This method is able to determine the time from given subset of fields:
734 ///
735 /// - Hour, minute. (second and nanosecond assumed to be 0)
736 /// - Hour, minute, second. (nanosecond assumed to be 0)
737 /// - Hour, minute, second, nanosecond.
738 ///
739 /// It is able to handle leap seconds when given second is 60.
740 ///
741 /// # Errors
742 ///
743 /// This method returns:
744 /// - `OUT_OF_RANGE` if any of the time fields of `Parsed` are set to a value beyond
745 /// their acceptable range.
746 /// - `NOT_ENOUGH` if an hour field is missing, if AM/PM is missing in a 12-hour clock,
747 /// if minutes are missing, or if seconds are missing while the nanosecond field is present.
748pub fn to_naive_time(&self) -> ParseResult<NaiveTime> {
749let hour_div_12 = match self.hour_div_12 {
750Some(v @ 0..=1) => v,
751Some(_) => return Err(OUT_OF_RANGE),
752None => return Err(NOT_ENOUGH),
753 };
754let hour_mod_12 = match self.hour_mod_12 {
755Some(v @ 0..=11) => v,
756Some(_) => return Err(OUT_OF_RANGE),
757None => return Err(NOT_ENOUGH),
758 };
759let hour = hour_div_12 * 12 + hour_mod_12;
760761let minute = match self.minute {
762Some(v @ 0..=59) => v,
763Some(_) => return Err(OUT_OF_RANGE),
764None => return Err(NOT_ENOUGH),
765 };
766767// we allow omitting seconds or nanoseconds, but they should be in the range.
768let (second, mut nano) = match self.second.unwrap_or(0) {
769 v @ 0..=59 => (v, 0),
77060 => (59, 1_000_000_000),
771_ => return Err(OUT_OF_RANGE),
772 };
773nano += match self.nanosecond {
774Some(v @ 0..=999_999_999) if self.second.is_some() => v,
775Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing
776Some(_) => return Err(OUT_OF_RANGE),
777None => 0,
778 };
779780NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE)
781 }
782783/// Returns a parsed naive date and time out of given fields, except for the offset field.
784 ///
785 /// The offset is assumed to have a given value. It is not compared against the offset field set
786 /// in the `Parsed` type, so it is allowed to be inconsistent.
787 ///
788 /// This method is able to determine the combined date and time from date and time fields or
789 /// from a single timestamp field. It checks all fields are consistent with each other.
790 ///
791 /// # Errors
792 ///
793 /// This method returns:
794 /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of
795 /// the other fields.
796 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime.
797 /// - `OUT_OF_RANGE`
798 /// - if any of the date or time fields of `Parsed` are set to a value beyond their acceptable
799 /// range.
800 /// - if the value would be outside the range of a [`NaiveDateTime`].
801 /// - if the date does not exist.
802pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult<NaiveDateTime> {
803let date = self.to_naive_date();
804let time = self.to_naive_time();
805if let (Ok(date), Ok(time)) = (date, time) {
806let datetime = date.and_time(time);
807808// verify the timestamp field if any
809 // the following is safe, `timestamp` is very limited in range
810let timestamp = datetime.and_utc().timestamp() - i64::from(offset);
811if let Some(given_timestamp) = self.timestamp {
812// if `datetime` represents a leap second, it might be off by one second.
813if given_timestamp != timestamp814 && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1)
815 {
816return Err(IMPOSSIBLE);
817 }
818 }
819820Ok(datetime)
821 } else if let Some(timestamp) = self.timestamp {
822use super::ParseErroras PE;
823use super::ParseErrorKind::{Impossible, OutOfRange};
824825// if date and time is problematic already, there is no point proceeding.
826 // we at least try to give a correct error though.
827match (date, time) {
828 (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE),
829 (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE),
830 (_, _) => {} // one of them is insufficient
831}
832833// reconstruct date and time fields from timestamp
834let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?;
835let mut datetime = DateTime::from_timestamp_secs(ts).ok_or(OUT_OF_RANGE)?.naive_utc();
836837// fill year, ordinal, hour, minute and second fields from timestamp.
838 // if existing fields are consistent, this will allow the full date/time reconstruction.
839let mut parsed = self.clone();
840if parsed.second == Some(60) {
841// `datetime.second()` cannot be 60, so this is the only case for a leap second.
842match datetime.second() {
843// it's okay, just do not try to overwrite the existing field.
84459 => {}
845// `datetime` is known to be off by one second.
8460 => {
847datetime -= TimeDelta::try_seconds(1).unwrap();
848 }
849// otherwise it is impossible.
850_ => return Err(IMPOSSIBLE),
851 }
852// ...and we have the correct candidates for other fields.
853} else {
854parsed.set_second(i64::from(datetime.second()))?;
855 }
856parsed.set_year(i64::from(datetime.year()))?;
857parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd
858parsed.set_hour(i64::from(datetime.hour()))?;
859parsed.set_minute(i64::from(datetime.minute()))?;
860861// validate other fields (e.g. week) and return
862let date = parsed.to_naive_date()?;
863let time = parsed.to_naive_time()?;
864Ok(date.and_time(time))
865 } else {
866// reproduce the previous error(s)
867date?;
868time?;
869::core::panicking::panic("internal error: entered unreachable code")unreachable!()870 }
871 }
872873/// Returns a parsed fixed time zone offset out of given fields.
874 ///
875 /// # Errors
876 ///
877 /// This method returns:
878 /// - `OUT_OF_RANGE` if the offset is out of range for a `FixedOffset`.
879 /// - `NOT_ENOUGH` if the offset field is not set.
880pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> {
881FixedOffset::east_opt(self.offset.ok_or(NOT_ENOUGH)?).ok_or(OUT_OF_RANGE)
882 }
883884/// Returns a parsed timezone-aware date and time out of given fields.
885 ///
886 /// This method is able to determine the combined date and time from date, time and offset
887 /// fields, and/or from a single timestamp field. It checks all fields are consistent with each
888 /// other.
889 ///
890 /// # Errors
891 ///
892 /// This method returns:
893 /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of
894 /// the other fields.
895 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime
896 /// including offset from UTC.
897 /// - `OUT_OF_RANGE`
898 /// - if any of the fields of `Parsed` are set to a value beyond their acceptable
899 /// range.
900 /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`].
901 /// - if the date does not exist.
902pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
903// If there is no explicit offset, consider a timestamp value as indication of a UTC value.
904let offset = match (self.offset, self.timestamp) {
905 (Some(off), _) => off,
906 (None, Some(_)) => 0, // UNIX timestamp may assume 0 offset
907(None, None) => return Err(NOT_ENOUGH),
908 };
909let datetime = self.to_naive_datetime_with_offset(offset)?;
910let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?;
911912match offset.from_local_datetime(&datetime) {
913MappedLocalTime::None => Err(IMPOSSIBLE),
914MappedLocalTime::Single(t) => Ok(t),
915MappedLocalTime::Ambiguous(..) => Err(NOT_ENOUGH),
916 }
917 }
918919/// Returns a parsed timezone-aware date and time out of given fields,
920 /// with an additional [`TimeZone`] used to interpret and validate the local date.
921 ///
922 /// This method is able to determine the combined date and time from date and time, and/or from
923 /// a single timestamp field. It checks all fields are consistent with each other.
924 ///
925 /// If the parsed fields include an UTC offset, it also has to be consistent with the offset in
926 /// the provided `tz` time zone for that datetime.
927 ///
928 /// # Errors
929 ///
930 /// This method returns:
931 /// - `IMPOSSIBLE`
932 /// - if any of the date fields conflict, if a timestamp conflicts with any of the other
933 /// fields, or if the offset field is set but differs from the offset at that time in the
934 /// `tz` time zone.
935 /// - if the local datetime does not exists in the provided time zone (because it falls in a
936 /// transition due to for example DST).
937 /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime, or if
938 /// the local time in the provided time zone is ambiguous (because it falls in a transition
939 /// due to for example DST) while there is no offset field or timestamp field set.
940 /// - `OUT_OF_RANGE`
941 /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`].
942 /// - if any of the fields of `Parsed` are set to a value beyond their acceptable range.
943 /// - if the date does not exist.
944pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> {
945// if we have `timestamp` specified, guess an offset from that.
946let mut guessed_offset = 0;
947if let Some(timestamp) = self.timestamp {
948// make a naive `DateTime` from given timestamp and (if any) nanosecond.
949 // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
950let nanosecond = self.nanosecond.unwrap_or(0);
951let dt =
952DateTime::from_timestamp(timestamp, nanosecond).ok_or(OUT_OF_RANGE)?.naive_utc();
953guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
954 }
955956// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
957let check_offset = |dt: &DateTime<Tz>| {
958if let Some(offset) = self.offset {
959dt.offset().fix().local_minus_utc() == offset960 } else {
961true
962}
963 };
964965// `guessed_offset` should be correct when `self.timestamp` is given.
966 // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case.
967let datetime = self.to_naive_datetime_with_offset(guessed_offset)?;
968match tz.from_local_datetime(&datetime) {
969MappedLocalTime::None => Err(IMPOSSIBLE),
970MappedLocalTime::Single(t) => {
971if check_offset(&t) {
972Ok(t)
973 } else {
974Err(IMPOSSIBLE)
975 }
976 }
977MappedLocalTime::Ambiguous(min, max) => {
978// try to disambiguate two possible local dates by offset.
979match (check_offset(&min), check_offset(&max)) {
980 (false, false) => Err(IMPOSSIBLE),
981 (false, true) => Ok(max),
982 (true, false) => Ok(min),
983 (true, true) => Err(NOT_ENOUGH),
984 }
985 }
986 }
987 }
988989/// Get the `year` field if set.
990 ///
991 /// See also [`set_year()`](Parsed::set_year).
992#[inline]
993pub fn year(&self) -> Option<i32> {
994self.year
995 }
996997/// Get the `year_div_100` field if set.
998 ///
999 /// See also [`set_year_div_100()`](Parsed::set_year_div_100).
1000#[inline]
1001pub fn year_div_100(&self) -> Option<i32> {
1002self.year_div_100
1003 }
10041005/// Get the `year_mod_100` field if set.
1006 ///
1007 /// See also [`set_year_mod_100()`](Parsed::set_year_mod_100).
1008#[inline]
1009pub fn year_mod_100(&self) -> Option<i32> {
1010self.year_mod_100
1011 }
10121013/// Get the `isoyear` field that is part of an [ISO 8601 week date] if set.
1014 ///
1015 /// See also [`set_isoyear()`](Parsed::set_isoyear).
1016 ///
1017 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1018#[inline]
1019pub fn isoyear(&self) -> Option<i32> {
1020self.isoyear
1021 }
10221023/// Get the `isoyear_div_100` field that is part of an [ISO 8601 week date] if set.
1024 ///
1025 /// See also [`set_isoyear_div_100()`](Parsed::set_isoyear_div_100).
1026 ///
1027 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1028#[inline]
1029pub fn isoyear_div_100(&self) -> Option<i32> {
1030self.isoyear_div_100
1031 }
10321033/// Get the `isoyear_mod_100` field that is part of an [ISO 8601 week date] if set.
1034 ///
1035 /// See also [`set_isoyear_mod_100()`](Parsed::set_isoyear_mod_100).
1036 ///
1037 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1038#[inline]
1039pub fn isoyear_mod_100(&self) -> Option<i32> {
1040self.isoyear_mod_100
1041 }
10421043/// Get the `quarter` field if set.
1044 ///
1045 /// See also [`set_quarter()`](Parsed::set_quarter).
1046#[inline]
1047pub fn quarter(&self) -> Option<u32> {
1048self.quarter
1049 }
10501051/// Get the `month` field if set.
1052 ///
1053 /// See also [`set_month()`](Parsed::set_month).
1054#[inline]
1055pub fn month(&self) -> Option<u32> {
1056self.month
1057 }
10581059/// Get the `week_from_sun` field if set.
1060 ///
1061 /// See also [`set_week_from_sun()`](Parsed::set_week_from_sun).
1062#[inline]
1063pub fn week_from_sun(&self) -> Option<u32> {
1064self.week_from_sun
1065 }
10661067/// Get the `week_from_mon` field if set.
1068 ///
1069 /// See also [`set_week_from_mon()`](Parsed::set_week_from_mon).
1070#[inline]
1071pub fn week_from_mon(&self) -> Option<u32> {
1072self.week_from_mon
1073 }
10741075/// Get the `isoweek` field that is part of an [ISO 8601 week date] if set.
1076 ///
1077 /// See also [`set_isoweek()`](Parsed::set_isoweek).
1078 ///
1079 /// [ISO 8601 week date]: crate::NaiveDate#week-date
1080#[inline]
1081pub fn isoweek(&self) -> Option<u32> {
1082self.isoweek
1083 }
10841085/// Get the `weekday` field if set.
1086 ///
1087 /// See also [`set_weekday()`](Parsed::set_weekday).
1088#[inline]
1089pub fn weekday(&self) -> Option<Weekday> {
1090self.weekday
1091 }
10921093/// Get the `ordinal` (day of the year) field if set.
1094 ///
1095 /// See also [`set_ordinal()`](Parsed::set_ordinal).
1096#[inline]
1097pub fn ordinal(&self) -> Option<u32> {
1098self.ordinal
1099 }
11001101/// Get the `day` of the month field if set.
1102 ///
1103 /// See also [`set_day()`](Parsed::set_day).
1104#[inline]
1105pub fn day(&self) -> Option<u32> {
1106self.day
1107 }
11081109/// Get the `hour_div_12` field (am/pm) if set.
1110 ///
1111 /// 0 indicates AM and 1 indicates PM.
1112 ///
1113 /// See also [`set_ampm()`](Parsed::set_ampm) and [`set_hour()`](Parsed::set_hour).
1114#[inline]
1115pub fn hour_div_12(&self) -> Option<u32> {
1116self.hour_div_12
1117 }
11181119/// Get the `hour_mod_12` field if set.
1120 ///
1121 /// See also [`set_hour12()`](Parsed::set_hour12) and [`set_hour()`](Parsed::set_hour).
1122pub fn hour_mod_12(&self) -> Option<u32> {
1123self.hour_mod_12
1124 }
11251126/// Get the `minute` field if set.
1127 ///
1128 /// See also [`set_minute()`](Parsed::set_minute).
1129#[inline]
1130pub fn minute(&self) -> Option<u32> {
1131self.minute
1132 }
11331134/// Get the `second` field if set.
1135 ///
1136 /// See also [`set_second()`](Parsed::set_second).
1137#[inline]
1138pub fn second(&self) -> Option<u32> {
1139self.second
1140 }
11411142/// Get the `nanosecond` field if set.
1143 ///
1144 /// See also [`set_nanosecond()`](Parsed::set_nanosecond).
1145#[inline]
1146pub fn nanosecond(&self) -> Option<u32> {
1147self.nanosecond
1148 }
11491150/// Get the `timestamp` field if set.
1151 ///
1152 /// See also [`set_timestamp()`](Parsed::set_timestamp).
1153#[inline]
1154pub fn timestamp(&self) -> Option<i64> {
1155self.timestamp
1156 }
11571158/// Get the `offset` field if set.
1159 ///
1160 /// See also [`set_offset()`](Parsed::set_offset).
1161#[inline]
1162pub fn offset(&self) -> Option<i32> {
1163self.offset
1164 }
1165}
11661167/// Create a `NaiveDate` when given a year, week, weekday, and the definition at which day of the
1168/// week a week starts.
1169///
1170/// Returns `IMPOSSIBLE` if `week` is `0` or `53` and the `weekday` falls outside the year.
1171fn resolve_week_date(
1172 year: i32,
1173 week: u32,
1174 weekday: Weekday,
1175 week_start_day: Weekday,
1176) -> ParseResult<NaiveDate> {
1177if week > 53 {
1178return Err(OUT_OF_RANGE);
1179 }
11801181let first_day_of_year = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
1182// Ordinal of the day at which week 1 starts.
1183let first_week_start = 1 + week_start_day.days_since(first_day_of_year.weekday()) as i32;
1184// Number of the `weekday`, which is 0 for the first day of the week.
1185let weekday = weekday.days_since(week_start_day) as i32;
1186let ordinal = first_week_start + (weekas i32 - 1) * 7 + weekday;
1187if ordinal <= 0 {
1188return Err(IMPOSSIBLE);
1189 }
1190first_day_of_year.with_ordinal(ordinalas u32).ok_or(IMPOSSIBLE)
1191}
11921193#[cfg(test)]
1194mod tests {
1195use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
1196use super::Parsed;
1197use crate::Datelike;
1198use crate::Weekday::*;
1199use crate::naive::{NaiveDate, NaiveTime};
1200use crate::offset::{FixedOffset, TimeZone, Utc};
12011202#[test]
1203fn test_parsed_set_fields() {
1204// year*, isoyear*
1205let mut p = Parsed::new();
1206assert_eq!(p.set_year(1987), Ok(()));
1207assert_eq!(p.set_year(1986), Err(IMPOSSIBLE));
1208assert_eq!(p.set_year(1988), Err(IMPOSSIBLE));
1209assert_eq!(p.set_year(1987), Ok(()));
1210assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year`
1211assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE));
1212assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE));
1213assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto
1214assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE));
1215assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE));
12161217let mut p = Parsed::new();
1218assert_eq!(p.set_year(0), Ok(()));
1219assert_eq!(p.set_year_div_100(0), Ok(()));
1220assert_eq!(p.set_year_mod_100(0), Ok(()));
12211222let mut p = Parsed::new();
1223assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE));
1224assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE));
1225assert_eq!(p.set_year(-1), Ok(()));
1226assert_eq!(p.set_year(-2), Err(IMPOSSIBLE));
1227assert_eq!(p.set_year(0), Err(IMPOSSIBLE));
12281229let mut p = Parsed::new();
1230assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
1231assert_eq!(p.set_year_div_100(8), Ok(()));
1232assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
12331234// month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset
1235let mut p = Parsed::new();
1236assert_eq!(p.set_month(7), Ok(()));
1237assert_eq!(p.set_month(1), Err(IMPOSSIBLE));
1238assert_eq!(p.set_month(6), Err(IMPOSSIBLE));
1239assert_eq!(p.set_month(8), Err(IMPOSSIBLE));
1240assert_eq!(p.set_month(12), Err(IMPOSSIBLE));
12411242let mut p = Parsed::new();
1243assert_eq!(p.set_month(8), Ok(()));
1244assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE));
12451246// hour
1247let mut p = Parsed::new();
1248assert_eq!(p.set_hour(12), Ok(()));
1249assert_eq!(p.set_hour(11), Err(IMPOSSIBLE));
1250assert_eq!(p.set_hour(13), Err(IMPOSSIBLE));
1251assert_eq!(p.set_hour(12), Ok(()));
1252assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE));
1253assert_eq!(p.set_ampm(true), Ok(()));
1254assert_eq!(p.set_hour12(12), Ok(()));
1255assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation
1256assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE));
1257assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE));
12581259let mut p = Parsed::new();
1260assert_eq!(p.set_ampm(true), Ok(()));
1261assert_eq!(p.set_hour12(7), Ok(()));
1262assert_eq!(p.set_hour(7), Err(IMPOSSIBLE));
1263assert_eq!(p.set_hour(18), Err(IMPOSSIBLE));
1264assert_eq!(p.set_hour(19), Ok(()));
12651266// timestamp
1267let mut p = Parsed::new();
1268assert_eq!(p.set_timestamp(1_234_567_890), Ok(()));
1269assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE));
1270assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE));
1271 }
12721273#[test]
1274fn test_parsed_set_range() {
1275assert_eq!(Parsed::new().set_year(i32::MIN as i64 - 1), Err(OUT_OF_RANGE));
1276assert!(Parsed::new().set_year(i32::MIN as i64).is_ok());
1277assert!(Parsed::new().set_year(i32::MAX as i64).is_ok());
1278assert_eq!(Parsed::new().set_year(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
12791280assert_eq!(Parsed::new().set_year_div_100(-1), Err(OUT_OF_RANGE));
1281assert!(Parsed::new().set_year_div_100(0).is_ok());
1282assert!(Parsed::new().set_year_div_100(i32::MAX as i64).is_ok());
1283assert_eq!(Parsed::new().set_year_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
12841285assert_eq!(Parsed::new().set_year_mod_100(-1), Err(OUT_OF_RANGE));
1286assert!(Parsed::new().set_year_mod_100(0).is_ok());
1287assert!(Parsed::new().set_year_mod_100(99).is_ok());
1288assert_eq!(Parsed::new().set_year_mod_100(100), Err(OUT_OF_RANGE));
12891290assert_eq!(Parsed::new().set_isoyear(i32::MIN as i64 - 1), Err(OUT_OF_RANGE));
1291assert!(Parsed::new().set_isoyear(i32::MIN as i64).is_ok());
1292assert!(Parsed::new().set_isoyear(i32::MAX as i64).is_ok());
1293assert_eq!(Parsed::new().set_isoyear(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
12941295assert_eq!(Parsed::new().set_isoyear_div_100(-1), Err(OUT_OF_RANGE));
1296assert!(Parsed::new().set_isoyear_div_100(0).is_ok());
1297assert!(Parsed::new().set_isoyear_div_100(99).is_ok());
1298assert_eq!(Parsed::new().set_isoyear_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
12991300assert_eq!(Parsed::new().set_isoyear_mod_100(-1), Err(OUT_OF_RANGE));
1301assert!(Parsed::new().set_isoyear_mod_100(0).is_ok());
1302assert!(Parsed::new().set_isoyear_mod_100(99).is_ok());
1303assert_eq!(Parsed::new().set_isoyear_mod_100(100), Err(OUT_OF_RANGE));
13041305assert_eq!(Parsed::new().set_quarter(0), Err(OUT_OF_RANGE));
1306assert!(Parsed::new().set_quarter(1).is_ok());
1307assert!(Parsed::new().set_quarter(4).is_ok());
1308assert_eq!(Parsed::new().set_quarter(5), Err(OUT_OF_RANGE));
13091310assert_eq!(Parsed::new().set_month(0), Err(OUT_OF_RANGE));
1311assert!(Parsed::new().set_month(1).is_ok());
1312assert!(Parsed::new().set_month(12).is_ok());
1313assert_eq!(Parsed::new().set_month(13), Err(OUT_OF_RANGE));
13141315assert_eq!(Parsed::new().set_week_from_sun(-1), Err(OUT_OF_RANGE));
1316assert!(Parsed::new().set_week_from_sun(0).is_ok());
1317assert!(Parsed::new().set_week_from_sun(53).is_ok());
1318assert_eq!(Parsed::new().set_week_from_sun(54), Err(OUT_OF_RANGE));
13191320assert_eq!(Parsed::new().set_week_from_mon(-1), Err(OUT_OF_RANGE));
1321assert!(Parsed::new().set_week_from_mon(0).is_ok());
1322assert!(Parsed::new().set_week_from_mon(53).is_ok());
1323assert_eq!(Parsed::new().set_week_from_mon(54), Err(OUT_OF_RANGE));
13241325assert_eq!(Parsed::new().set_isoweek(0), Err(OUT_OF_RANGE));
1326assert!(Parsed::new().set_isoweek(1).is_ok());
1327assert!(Parsed::new().set_isoweek(53).is_ok());
1328assert_eq!(Parsed::new().set_isoweek(54), Err(OUT_OF_RANGE));
13291330assert_eq!(Parsed::new().set_ordinal(0), Err(OUT_OF_RANGE));
1331assert!(Parsed::new().set_ordinal(1).is_ok());
1332assert!(Parsed::new().set_ordinal(366).is_ok());
1333assert_eq!(Parsed::new().set_ordinal(367), Err(OUT_OF_RANGE));
13341335assert_eq!(Parsed::new().set_day(0), Err(OUT_OF_RANGE));
1336assert!(Parsed::new().set_day(1).is_ok());
1337assert!(Parsed::new().set_day(31).is_ok());
1338assert_eq!(Parsed::new().set_day(32), Err(OUT_OF_RANGE));
13391340assert_eq!(Parsed::new().set_hour12(0), Err(OUT_OF_RANGE));
1341assert!(Parsed::new().set_hour12(1).is_ok());
1342assert!(Parsed::new().set_hour12(12).is_ok());
1343assert_eq!(Parsed::new().set_hour12(13), Err(OUT_OF_RANGE));
13441345assert_eq!(Parsed::new().set_hour(-1), Err(OUT_OF_RANGE));
1346assert!(Parsed::new().set_hour(0).is_ok());
1347assert!(Parsed::new().set_hour(23).is_ok());
1348assert_eq!(Parsed::new().set_hour(24), Err(OUT_OF_RANGE));
13491350assert_eq!(Parsed::new().set_minute(-1), Err(OUT_OF_RANGE));
1351assert!(Parsed::new().set_minute(0).is_ok());
1352assert!(Parsed::new().set_minute(59).is_ok());
1353assert_eq!(Parsed::new().set_minute(60), Err(OUT_OF_RANGE));
13541355assert_eq!(Parsed::new().set_second(-1), Err(OUT_OF_RANGE));
1356assert!(Parsed::new().set_second(0).is_ok());
1357assert!(Parsed::new().set_second(60).is_ok());
1358assert_eq!(Parsed::new().set_second(61), Err(OUT_OF_RANGE));
13591360assert_eq!(Parsed::new().set_nanosecond(-1), Err(OUT_OF_RANGE));
1361assert!(Parsed::new().set_nanosecond(0).is_ok());
1362assert!(Parsed::new().set_nanosecond(999_999_999).is_ok());
1363assert_eq!(Parsed::new().set_nanosecond(1_000_000_000), Err(OUT_OF_RANGE));
13641365assert!(Parsed::new().set_timestamp(i64::MIN).is_ok());
1366assert!(Parsed::new().set_timestamp(i64::MAX).is_ok());
13671368assert_eq!(Parsed::new().set_offset(i32::MIN as i64 - 1), Err(OUT_OF_RANGE));
1369assert!(Parsed::new().set_offset(i32::MIN as i64).is_ok());
1370assert!(Parsed::new().set_offset(i32::MAX as i64).is_ok());
1371assert_eq!(Parsed::new().set_offset(i32::MAX as i64 + 1), Err(OUT_OF_RANGE));
1372 }
13731374#[test]
1375fn test_parsed_to_naive_date() {
1376macro_rules! parse {
1377 ($($k:ident: $v:expr),*) => (
1378 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date()
1379 )
1380 }
13811382let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap());
13831384// ymd: omission of fields
1385assert_eq!(parse!(), Err(NOT_ENOUGH));
1386assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH));
1387assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH));
1388assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2));
1389assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH));
1390assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH));
1391assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH));
1392assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH));
1393assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2));
1394assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH));
1395assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH));
1396assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2));
1397assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2));
13981399// ymd: out-of-range conditions
1400assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29));
1401assert_eq!(
1402parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29),
1403Err(OUT_OF_RANGE)
1404 );
1405assert_eq!(
1406parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1),
1407Err(OUT_OF_RANGE)
1408 );
1409assert_eq!(
1410parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31),
1411 ymd(1983, 12, 31)
1412 );
1413assert_eq!(
1414parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32),
1415Err(OUT_OF_RANGE)
1416 );
1417assert_eq!(
1418parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0),
1419Err(OUT_OF_RANGE)
1420 );
1421assert_eq!(
1422parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1),
1423Err(OUT_OF_RANGE)
1424 );
1425assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE));
1426assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1));
1427assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(IMPOSSIBLE));
1428let max_year = NaiveDate::MAX.year();
1429assert_eq!(
1430parse!(year_div_100: max_year / 100,
1431 year_mod_100: max_year % 100, month: 1, day: 1),
1432 ymd(max_year, 1, 1)
1433 );
1434assert_eq!(
1435parse!(year_div_100: (max_year + 1) / 100,
1436 year_mod_100: (max_year + 1) % 100, month: 1, day: 1),
1437Err(OUT_OF_RANGE)
1438 );
14391440// ymd: conflicting inputs
1441assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1));
1442assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE));
1443assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1));
1444assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE));
1445assert_eq!(
1446parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1),
1447 ymd(1984, 1, 1)
1448 );
1449assert_eq!(
1450parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1),
1451Err(IMPOSSIBLE)
1452 );
1453assert_eq!(
1454parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1),
1455Err(OUT_OF_RANGE)
1456 );
1457assert_eq!(
1458parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1),
1459Err(OUT_OF_RANGE)
1460 );
1461assert_eq!(
1462parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1),
1463Err(IMPOSSIBLE)
1464 );
1465assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(IMPOSSIBLE));
1466assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(IMPOSSIBLE));
14671468// quarters
1469assert_eq!(parse!(year: 2000, quarter: 1), Err(NOT_ENOUGH));
1470assert_eq!(parse!(year: 2000, quarter: 1, month: 1, day: 1), ymd(2000, 1, 1));
1471assert_eq!(parse!(year: 2000, quarter: 2, month: 4, day: 1), ymd(2000, 4, 1));
1472assert_eq!(parse!(year: 2000, quarter: 3, month: 7, day: 1), ymd(2000, 7, 1));
1473assert_eq!(parse!(year: 2000, quarter: 4, month: 10, day: 1), ymd(2000, 10, 1));
14741475// quarter: conflicting inputs
1476assert_eq!(parse!(year: 2000, quarter: 2, month: 3, day: 31), Err(IMPOSSIBLE));
1477assert_eq!(parse!(year: 2000, quarter: 4, month: 3, day: 31), Err(IMPOSSIBLE));
14781479// weekdates
1480assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH));
1481assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH));
1482assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH));
1483assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(IMPOSSIBLE));
1484assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(IMPOSSIBLE));
1485assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1));
1486assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1));
1487assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2));
1488assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2));
1489assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3));
1490assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3));
1491assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8));
1492assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8));
1493assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9));
1494assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9));
1495assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10));
1496assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30));
1497assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31));
1498assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(IMPOSSIBLE));
1499assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE));
1500assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(IMPOSSIBLE));
1501assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1));
15021503// weekdates: conflicting inputs
1504assert_eq!(
1505parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat),
1506 ymd(2000, 1, 8)
1507 );
1508assert_eq!(
1509parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun),
1510 ymd(2000, 1, 9)
1511 );
1512assert_eq!(
1513parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun),
1514Err(IMPOSSIBLE)
1515 );
1516assert_eq!(
1517parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun),
1518Err(IMPOSSIBLE)
1519 );
15201521// ISO weekdates
1522assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH));
1523assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31));
1524assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1));
1525assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE));
1526assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE));
1527assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3));
1528assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH));
15291530// year and ordinal
1531assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH));
1532assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE));
1533assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1));
1534assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29));
1535assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1));
1536assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31));
1537assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE));
1538assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
1539assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE));
1540assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1));
1541assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28));
1542assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1));
1543assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31));
1544assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE));
1545assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
15461547// more complex cases
1548assert_eq!(
1549parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1,
1550 week_from_sun: 52, week_from_mon: 52, weekday: Wed),
1551 ymd(2014, 12, 31)
1552 );
1553assert_eq!(
1554parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1,
1555 week_from_sun: 52, week_from_mon: 52),
1556 ymd(2014, 12, 31)
1557 );
1558assert_eq!(
1559parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53,
1560 week_from_sun: 52, week_from_mon: 52, weekday: Wed),
1561Err(IMPOSSIBLE)
1562 ); // no ISO week date 2014-W53-3
1563assert_eq!(
1564parse!(year: 2012, isoyear: 2015, isoweek: 1,
1565 week_from_sun: 52, week_from_mon: 52),
1566Err(NOT_ENOUGH)
1567 ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31)
1568assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH));
1569// technically unique (2014-12-31) but Chrono gives up
1570}
15711572#[test]
1573fn test_parsed_to_naive_time() {
1574macro_rules! parse {
1575 ($($k:ident: $v:expr),*) => (
1576 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time()
1577 )
1578 }
15791580let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap());
1581let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap());
15821583// omission of fields
1584assert_eq!(parse!(), Err(NOT_ENOUGH));
1585assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH));
1586assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH));
1587assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0));
1588assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45));
1589assert_eq!(
1590parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45,
1591 nanosecond: 678_901_234),
1592 hmsn(1, 23, 45, 678_901_234)
1593 );
1594assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6));
1595assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH));
1596assert_eq!(
1597parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012),
1598Err(NOT_ENOUGH)
1599 );
16001601// out-of-range conditions
1602assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE));
1603assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE));
1604assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE));
1605assert_eq!(
1606parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61),
1607Err(OUT_OF_RANGE)
1608 );
1609assert_eq!(
1610parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34,
1611 nanosecond: 1_000_000_000),
1612Err(OUT_OF_RANGE)
1613 );
16141615// leap seconds
1616assert_eq!(
1617parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60),
1618 hmsn(1, 23, 59, 1_000_000_000)
1619 );
1620assert_eq!(
1621parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60,
1622 nanosecond: 999_999_999),
1623 hmsn(1, 23, 59, 1_999_999_999)
1624 );
1625 }
16261627#[test]
1628fn test_parsed_to_naive_datetime_with_offset() {
1629macro_rules! parse {
1630 (offset = $offset:expr; $($k:ident: $v:expr),*) => (
1631 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset)
1632 );
1633 ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*))
1634 }
16351636let ymdhms = |y, m, d, h, n, s| {
1637Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap())
1638 };
1639let ymdhmsn = |y, m, d, h, n, s, nano| {
1640Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap())
1641 };
16421643// omission of fields
1644assert_eq!(parse!(), Err(NOT_ENOUGH));
1645assert_eq!(
1646parse!(year: 2015, month: 1, day: 30,
1647 hour_div_12: 1, hour_mod_12: 2, minute: 38),
1648 ymdhms(2015, 1, 30, 14, 38, 0)
1649 );
1650assert_eq!(
1651parse!(year: 1997, month: 1, day: 30,
1652 hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5),
1653 ymdhms(1997, 1, 30, 14, 38, 5)
1654 );
1655assert_eq!(
1656parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5,
1657 minute: 6, second: 7, nanosecond: 890_123_456),
1658 ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456)
1659 );
1660assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0));
1661assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1));
1662assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1));
1663assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40));
1664assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44));
16651666// full fields
1667assert_eq!(
1668parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1669 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1670 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1671 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1672 nanosecond: 12_345_678, timestamp: 1_420_000_000),
1673 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
1674 );
1675assert_eq!(
1676parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1677 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1678 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1679 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1680 nanosecond: 12_345_678, timestamp: 1_419_999_999),
1681Err(IMPOSSIBLE)
1682 );
1683assert_eq!(
1684parse!(offset = 32400;
1685 year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1686 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1687 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1688 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1689 nanosecond: 12_345_678, timestamp: 1_419_967_600),
1690 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
1691 );
16921693// more timestamps
1694let max_days_from_year_1970 =
1695 NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1696let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1)
1697 .unwrap()
1698 .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1699let min_days_from_year_1970 =
1700 NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1701assert_eq!(
1702parse!(timestamp: min_days_from_year_1970.num_seconds()),
1703 ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0)
1704 );
1705assert_eq!(
1706parse!(timestamp: year_0_from_year_1970.num_seconds()),
1707 ymdhms(0, 1, 1, 0, 0, 0)
1708 );
1709assert_eq!(
1710parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
1711 ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59)
1712 );
17131714// leap seconds #1: partial fields
1715assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
1716assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59));
1717assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
1718assert_eq!(
1719parse!(second: 60, timestamp: 1_341_100_799),
1720 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1721 );
1722assert_eq!(
1723parse!(second: 60, timestamp: 1_341_100_800),
1724 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1725 );
1726assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0));
1727assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
1728assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE));
17291730// leap seconds #2: full fields
1731 // we need to have separate tests for them since it uses another control flow.
1732assert_eq!(
1733parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1734 minute: 59, second: 59, timestamp: 1_341_100_798),
1735Err(IMPOSSIBLE)
1736 );
1737assert_eq!(
1738parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1739 minute: 59, second: 59, timestamp: 1_341_100_799),
1740 ymdhms(2012, 6, 30, 23, 59, 59)
1741 );
1742assert_eq!(
1743parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1744 minute: 59, second: 59, timestamp: 1_341_100_800),
1745Err(IMPOSSIBLE)
1746 );
1747assert_eq!(
1748parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1749 minute: 59, second: 60, timestamp: 1_341_100_799),
1750 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1751 );
1752assert_eq!(
1753parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1754 minute: 59, second: 60, timestamp: 1_341_100_800),
1755 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1756 );
1757assert_eq!(
1758parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
1759 minute: 0, second: 0, timestamp: 1_341_100_800),
1760 ymdhms(2012, 7, 1, 0, 0, 0)
1761 );
1762assert_eq!(
1763parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
1764 minute: 0, second: 1, timestamp: 1_341_100_800),
1765Err(IMPOSSIBLE)
1766 );
1767assert_eq!(
1768parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1769 minute: 59, second: 60, timestamp: 1_341_100_801),
1770Err(IMPOSSIBLE)
1771 );
17721773// error codes
1774assert_eq!(
1775parse!(year: 2015, month: 1, day: 20, weekday: Tue,
1776 hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20),
1777Err(OUT_OF_RANGE)
1778 ); // `hour_div_12` is out of range
1779}
17801781#[test]
1782fn test_parsed_to_datetime() {
1783macro_rules! parse {
1784 ($($k:ident: $v:expr),*) => (
1785 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime()
1786 )
1787 }
17881789let ymdhmsn = |y, m, d, h, n, s, nano, off| {
1790Ok(FixedOffset::east_opt(off)
1791 .unwrap()
1792 .from_local_datetime(
1793&NaiveDate::from_ymd_opt(y, m, d)
1794 .unwrap()
1795 .and_hms_nano_opt(h, n, s, nano)
1796 .unwrap(),
1797 )
1798 .unwrap())
1799 };
18001801assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH));
1802assert_eq!(
1803parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1804 minute: 26, second: 40, nanosecond: 12_345_678),
1805Err(NOT_ENOUGH)
1806 );
1807assert_eq!(
1808parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1809 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1810 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0)
1811 );
1812assert_eq!(
1813parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1814 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1815 ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400)
1816 );
1817assert_eq!(
1818parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1,
1819 minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876),
1820 ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876)
1821 );
1822assert_eq!(
1823parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4,
1824 minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400),
1825Err(OUT_OF_RANGE)
1826 ); // `FixedOffset` does not support such huge offset
1827}
18281829#[test]
1830fn test_parsed_to_datetime_with_timezone() {
1831macro_rules! parse {
1832 ($tz:expr; $($k:ident: $v:expr),*) => (
1833 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz)
1834 )
1835 }
18361837// single result from ymdhms
1838assert_eq!(
1839parse!(Utc;
1840 year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1841 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1842Ok(Utc
1843 .from_local_datetime(
1844&NaiveDate::from_ymd_opt(2014, 12, 31)
1845 .unwrap()
1846 .and_hms_nano_opt(4, 26, 40, 12_345_678)
1847 .unwrap()
1848 )
1849 .unwrap())
1850 );
1851assert_eq!(
1852parse!(Utc;
1853 year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1854 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1855Err(IMPOSSIBLE)
1856 );
1857assert_eq!(
1858parse!(FixedOffset::east_opt(32400).unwrap();
1859 year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1860 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1861Err(IMPOSSIBLE)
1862 );
1863assert_eq!(
1864parse!(FixedOffset::east_opt(32400).unwrap();
1865 year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1866 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1867Ok(FixedOffset::east_opt(32400)
1868 .unwrap()
1869 .from_local_datetime(
1870&NaiveDate::from_ymd_opt(2014, 12, 31)
1871 .unwrap()
1872 .and_hms_nano_opt(13, 26, 40, 12_345_678)
1873 .unwrap()
1874 )
1875 .unwrap())
1876 );
18771878// single result from timestamp
1879assert_eq!(
1880parse!(Utc; timestamp: 1_420_000_000, offset: 0),
1881Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap())
1882 );
1883assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE));
1884assert_eq!(
1885parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0),
1886Err(IMPOSSIBLE)
1887 );
1888assert_eq!(
1889parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400),
1890Ok(FixedOffset::east_opt(32400)
1891 .unwrap()
1892 .with_ymd_and_hms(2014, 12, 31, 13, 26, 40)
1893 .unwrap())
1894 );
18951896// TODO test with a variable time zone (for None and Ambiguous cases)
1897}
18981899#[test]
1900fn issue_551() {
1901use crate::Weekday;
1902let mut parsed = Parsed::new();
19031904 parsed.year = Some(2002);
1905 parsed.week_from_mon = Some(22);
1906 parsed.weekday = Some(Weekday::Mon);
1907assert_eq!(NaiveDate::from_ymd_opt(2002, 6, 3).unwrap(), parsed.to_naive_date().unwrap());
19081909 parsed.year = Some(2001);
1910assert_eq!(NaiveDate::from_ymd_opt(2001, 5, 28).unwrap(), parsed.to_naive_date().unwrap());
1911 }
1912}