1// This is a part of Chrono.
2// See README.md and LICENSE.txt for details.
3
4//! A collection of parsed date and time items.
5//! They can be constructed incrementally while being checked for consistency.
6
7use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
8use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
9use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone};
10use crate::oldtime::Duration as OldDuration;
11use crate::DateTime;
12use crate::Weekday;
13use crate::{Datelike, Timelike};
14
15/// Parsed parts of date and time. There are two classes of methods:
16///
17/// - `set_*` methods try to set given field(s) while checking for the consistency.
18/// It may or may not check for the range constraint immediately (for efficiency reasons).
19///
20/// - `to_*` methods try to make a concrete date and time value out of set fields.
21/// It fully checks any remaining out-of-range conditions and inconsistent/impossible fields.
22#[allow(clippy::manual_non_exhaustive)]
23#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
24pub struct Parsed {
25 /// Year.
26 ///
27 /// This can be negative unlike [`year_div_100`](#structfield.year_div_100)
28 /// and [`year_mod_100`](#structfield.year_mod_100) fields.
29 pub year: Option<i32>,
30
31 /// Year divided by 100. Implies that the year is >= 1 BCE when set.
32 ///
33 /// Due to the common usage, if this field is missing but
34 /// [`year_mod_100`](#structfield.year_mod_100) is present,
35 /// it is inferred to 19 when `year_mod_100 >= 70` and 20 otherwise.
36 pub year_div_100: Option<i32>,
37
38 /// Year modulo 100. Implies that the year is >= 1 BCE when set.
39 pub year_mod_100: Option<i32>,
40
41 /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date).
42 ///
43 /// This can be negative unlike [`isoyear_div_100`](#structfield.isoyear_div_100) and
44 /// [`isoyear_mod_100`](#structfield.isoyear_mod_100) fields.
45 pub isoyear: Option<i32>,
46
47 /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), divided by 100.
48 /// Implies that the year is >= 1 BCE when set.
49 ///
50 /// Due to the common usage, if this field is missing but
51 /// [`isoyear_mod_100`](#structfield.isoyear_mod_100) is present,
52 /// it is inferred to 19 when `isoyear_mod_100 >= 70` and 20 otherwise.
53 pub isoyear_div_100: Option<i32>,
54
55 /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), modulo 100.
56 /// Implies that the year is >= 1 BCE when set.
57 pub isoyear_mod_100: Option<i32>,
58
59 /// Month (1--12).
60 pub month: Option<u32>,
61
62 /// Week number, where the week 1 starts at the first Sunday of January
63 /// (0--53, 1--53 or 1--52 depending on the year).
64 pub week_from_sun: Option<u32>,
65
66 /// Week number, where the week 1 starts at the first Monday of January
67 /// (0--53, 1--53 or 1--52 depending on the year).
68 pub week_from_mon: Option<u32>,
69
70 /// [ISO week number](../naive/struct.NaiveDate.html#week-date)
71 /// (1--52 or 1--53 depending on the year).
72 pub isoweek: Option<u32>,
73
74 /// Day of the week.
75 pub weekday: Option<Weekday>,
76
77 /// Day of the year (1--365 or 1--366 depending on the year).
78 pub ordinal: Option<u32>,
79
80 /// Day of the month (1--28, 1--29, 1--30 or 1--31 depending on the month).
81 pub day: Option<u32>,
82
83 /// Hour number divided by 12 (0--1). 0 indicates AM and 1 indicates PM.
84 pub hour_div_12: Option<u32>,
85
86 /// Hour number modulo 12 (0--11).
87 pub hour_mod_12: Option<u32>,
88
89 /// Minute number (0--59).
90 pub minute: Option<u32>,
91
92 /// Second number (0--60, accounting for leap seconds).
93 pub second: Option<u32>,
94
95 /// The number of nanoseconds since the whole second (0--999,999,999).
96 pub nanosecond: Option<u32>,
97
98 /// The number of non-leap seconds since the midnight UTC on January 1, 1970.
99 ///
100 /// This can be off by one if [`second`](#structfield.second) is 60 (a leap second).
101 pub timestamp: Option<i64>,
102
103 /// Offset from the local time to UTC, in seconds.
104 pub offset: Option<i32>,
105
106 /// A dummy field to make this type not fully destructible (required for API stability).
107 // TODO: Change this to `#[non_exhaustive]` (on the enum) with the next breaking release.
108 _dummy: (),
109}
110
111/// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"),
112/// and if it is empty, set `old` to `new` as well.
113#[inline]
114fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
115 if let Some(ref old: &T) = *old {
116 if *old == new {
117 Ok(())
118 } else {
119 Err(IMPOSSIBLE)
120 }
121 } else {
122 *old = Some(new);
123 Ok(())
124 }
125}
126
127impl Parsed {
128 /// Returns the initial value of parsed parts.
129 #[must_use]
130 pub fn new() -> Parsed {
131 Parsed::default()
132 }
133
134 /// Tries to set the [`year`](#structfield.year) field from given value.
135 #[inline]
136 pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
137 set_if_consistent(&mut self.year, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
138 }
139
140 /// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value.
141 #[inline]
142 pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
143 if value < 0 {
144 return Err(OUT_OF_RANGE);
145 }
146 set_if_consistent(&mut self.year_div_100, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
147 }
148
149 /// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value.
150 #[inline]
151 pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
152 if value < 0 {
153 return Err(OUT_OF_RANGE);
154 }
155 set_if_consistent(&mut self.year_mod_100, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
156 }
157
158 /// Tries to set the [`isoyear`](#structfield.isoyear) field from given value.
159 #[inline]
160 pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
161 set_if_consistent(&mut self.isoyear, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
162 }
163
164 /// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value.
165 #[inline]
166 pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
167 if value < 0 {
168 return Err(OUT_OF_RANGE);
169 }
170 set_if_consistent(
171 &mut self.isoyear_div_100,
172 i32::try_from(value).map_err(|_| OUT_OF_RANGE)?,
173 )
174 }
175
176 /// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value.
177 #[inline]
178 pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
179 if value < 0 {
180 return Err(OUT_OF_RANGE);
181 }
182 set_if_consistent(
183 &mut self.isoyear_mod_100,
184 i32::try_from(value).map_err(|_| OUT_OF_RANGE)?,
185 )
186 }
187
188 /// Tries to set the [`month`](#structfield.month) field from given value.
189 #[inline]
190 pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
191 set_if_consistent(&mut self.month, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
192 }
193
194 /// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value.
195 #[inline]
196 pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
197 set_if_consistent(&mut self.week_from_sun, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
198 }
199
200 /// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value.
201 #[inline]
202 pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
203 set_if_consistent(&mut self.week_from_mon, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
204 }
205
206 /// Tries to set the [`isoweek`](#structfield.isoweek) field from given value.
207 #[inline]
208 pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
209 set_if_consistent(&mut self.isoweek, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
210 }
211
212 /// Tries to set the [`weekday`](#structfield.weekday) field from given value.
213 #[inline]
214 pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
215 set_if_consistent(&mut self.weekday, value)
216 }
217
218 /// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
219 #[inline]
220 pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
221 set_if_consistent(&mut self.ordinal, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
222 }
223
224 /// Tries to set the [`day`](#structfield.day) field from given value.
225 #[inline]
226 pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
227 set_if_consistent(&mut self.day, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
228 }
229
230 /// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value.
231 /// (`false` for AM, `true` for PM)
232 #[inline]
233 pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
234 set_if_consistent(&mut self.hour_div_12, u32::from(value))
235 }
236
237 /// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from
238 /// given hour number in 12-hour clocks.
239 #[inline]
240 pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> {
241 if !(1..=12).contains(&value) {
242 return Err(OUT_OF_RANGE);
243 }
244 set_if_consistent(&mut self.hour_mod_12, value as u32 % 12)
245 }
246
247 /// Tries to set both [`hour_div_12`](#structfield.hour_div_12) and
248 /// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
249 #[inline]
250 pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
251 let v = u32::try_from(value).map_err(|_| OUT_OF_RANGE)?;
252 set_if_consistent(&mut self.hour_div_12, v / 12)?;
253 set_if_consistent(&mut self.hour_mod_12, v % 12)?;
254 Ok(())
255 }
256
257 /// Tries to set the [`minute`](#structfield.minute) field from given value.
258 #[inline]
259 pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
260 set_if_consistent(&mut self.minute, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
261 }
262
263 /// Tries to set the [`second`](#structfield.second) field from given value.
264 #[inline]
265 pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
266 set_if_consistent(&mut self.second, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
267 }
268
269 /// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value.
270 #[inline]
271 pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
272 set_if_consistent(&mut self.nanosecond, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
273 }
274
275 /// Tries to set the [`timestamp`](#structfield.timestamp) field from given value.
276 #[inline]
277 pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
278 set_if_consistent(&mut self.timestamp, value)
279 }
280
281 /// Tries to set the [`offset`](#structfield.offset) field from given value.
282 #[inline]
283 pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
284 set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
285 }
286
287 /// Returns a parsed naive date out of given fields.
288 ///
289 /// This method is able to determine the date from given subset of fields:
290 ///
291 /// - Year, month, day.
292 /// - Year, day of the year (ordinal).
293 /// - Year, week number counted from Sunday or Monday, day of the week.
294 /// - ISO week date.
295 ///
296 /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted,
297 /// the two-digit year is used to guess the century number then.
298 pub fn to_naive_date(&self) -> ParseResult<NaiveDate> {
299 fn resolve_year(
300 y: Option<i32>,
301 q: Option<i32>,
302 r: Option<i32>,
303 ) -> ParseResult<Option<i32>> {
304 match (y, q, r) {
305 // if there is no further information, simply return the given full year.
306 // this is a common case, so let's avoid division here.
307 (y, None, None) => Ok(y),
308
309 // if there is a full year *and* also quotient and/or modulo,
310 // check if present quotient and/or modulo is consistent to the full year.
311 // since the presence of those fields means a positive full year,
312 // we should filter a negative full year first.
313 (Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => {
314 if y < 0 {
315 return Err(OUT_OF_RANGE);
316 }
317 let q_ = y / 100;
318 let r_ = y % 100;
319 if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ {
320 Ok(Some(y))
321 } else {
322 Err(IMPOSSIBLE)
323 }
324 }
325
326 // the full year is missing but we have quotient and modulo.
327 // reconstruct the full year. make sure that the result is always positive.
328 (None, Some(q), Some(r @ 0..=99)) => {
329 if q < 0 {
330 return Err(OUT_OF_RANGE);
331 }
332 let y = q.checked_mul(100).and_then(|v| v.checked_add(r));
333 Ok(Some(y.ok_or(OUT_OF_RANGE)?))
334 }
335
336 // we only have modulo. try to interpret a modulo as a conventional two-digit year.
337 // note: we are affected by Rust issue #18060. avoid multiple range patterns.
338 (None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })),
339
340 // otherwise it is an out-of-bound or insufficient condition.
341 (None, Some(_), None) => Err(NOT_ENOUGH),
342 (_, _, Some(_)) => Err(OUT_OF_RANGE),
343 }
344 }
345
346 let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?;
347 let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?;
348
349 // verify the normal year-month-day date.
350 let verify_ymd = |date: NaiveDate| {
351 let year = date.year();
352 let (year_div_100, year_mod_100) = if year >= 0 {
353 (Some(year / 100), Some(year % 100))
354 } else {
355 (None, None) // they should be empty to be consistent
356 };
357 let month = date.month();
358 let day = date.day();
359 self.year.unwrap_or(year) == year
360 && self.year_div_100.or(year_div_100) == year_div_100
361 && self.year_mod_100.or(year_mod_100) == year_mod_100
362 && self.month.unwrap_or(month) == month
363 && self.day.unwrap_or(day) == day
364 };
365
366 // verify the ISO week date.
367 let verify_isoweekdate = |date: NaiveDate| {
368 let week = date.iso_week();
369 let isoyear = week.year();
370 let isoweek = week.week();
371 let weekday = date.weekday();
372 let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 {
373 (Some(isoyear / 100), Some(isoyear % 100))
374 } else {
375 (None, None) // they should be empty to be consistent
376 };
377 self.isoyear.unwrap_or(isoyear) == isoyear
378 && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100
379 && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100
380 && self.isoweek.unwrap_or(isoweek) == isoweek
381 && self.weekday.unwrap_or(weekday) == weekday
382 };
383
384 // verify the ordinal and other (non-ISO) week dates.
385 let verify_ordinal = |date: NaiveDate| {
386 let ordinal = date.ordinal();
387 let week_from_sun = date.weeks_from(Weekday::Sun);
388 let week_from_mon = date.weeks_from(Weekday::Mon);
389 self.ordinal.unwrap_or(ordinal) == ordinal
390 && self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun
391 && self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon
392 };
393
394 // test several possibilities.
395 // tries to construct a full `NaiveDate` as much as possible, then verifies that
396 // it is consistent with other given fields.
397 let (verified, parsed_date) = match (given_year, given_isoyear, self) {
398 (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => {
399 // year, month, day
400 let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?;
401 (verify_isoweekdate(date) && verify_ordinal(date), date)
402 }
403
404 (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
405 // year, day of the year
406 let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?;
407 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
408 }
409
410 (
411 Some(year),
412 _,
413 &Parsed { week_from_sun: Some(week_from_sun), weekday: Some(weekday), .. },
414 ) => {
415 // year, week (starting at 1st Sunday), day of the week
416 let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
417 let firstweek = match newyear.weekday() {
418 Weekday::Sun => 0,
419 Weekday::Mon => 6,
420 Weekday::Tue => 5,
421 Weekday::Wed => 4,
422 Weekday::Thu => 3,
423 Weekday::Fri => 2,
424 Weekday::Sat => 1,
425 };
426
427 // `firstweek+1`-th day of January is the beginning of the week 1.
428 if week_from_sun > 53 {
429 return Err(OUT_OF_RANGE);
430 } // can it overflow?
431 let ndays = firstweek
432 + (week_from_sun as i32 - 1) * 7
433 + weekday.num_days_from_sunday() as i32;
434 let date = newyear
435 .checked_add_signed(OldDuration::days(i64::from(ndays)))
436 .ok_or(OUT_OF_RANGE)?;
437 if date.year() != year {
438 return Err(OUT_OF_RANGE);
439 } // early exit for correct error
440
441 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
442 }
443
444 (
445 Some(year),
446 _,
447 &Parsed { week_from_mon: Some(week_from_mon), weekday: Some(weekday), .. },
448 ) => {
449 // year, week (starting at 1st Monday), day of the week
450 let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
451 let firstweek = match newyear.weekday() {
452 Weekday::Sun => 1,
453 Weekday::Mon => 0,
454 Weekday::Tue => 6,
455 Weekday::Wed => 5,
456 Weekday::Thu => 4,
457 Weekday::Fri => 3,
458 Weekday::Sat => 2,
459 };
460
461 // `firstweek+1`-th day of January is the beginning of the week 1.
462 if week_from_mon > 53 {
463 return Err(OUT_OF_RANGE);
464 } // can it overflow?
465 let ndays = firstweek
466 + (week_from_mon as i32 - 1) * 7
467 + weekday.num_days_from_monday() as i32;
468 let date = newyear
469 .checked_add_signed(OldDuration::days(i64::from(ndays)))
470 .ok_or(OUT_OF_RANGE)?;
471 if date.year() != year {
472 return Err(OUT_OF_RANGE);
473 } // early exit for correct error
474
475 (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
476 }
477
478 (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => {
479 // ISO year, week, day of the week
480 let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday);
481 let date = date.ok_or(OUT_OF_RANGE)?;
482 (verify_ymd(date) && verify_ordinal(date), date)
483 }
484
485 (_, _, _) => return Err(NOT_ENOUGH),
486 };
487
488 if verified {
489 Ok(parsed_date)
490 } else {
491 Err(IMPOSSIBLE)
492 }
493 }
494
495 /// Returns a parsed naive time out of given fields.
496 ///
497 /// This method is able to determine the time from given subset of fields:
498 ///
499 /// - Hour, minute. (second and nanosecond assumed to be 0)
500 /// - Hour, minute, second. (nanosecond assumed to be 0)
501 /// - Hour, minute, second, nanosecond.
502 ///
503 /// It is able to handle leap seconds when given second is 60.
504 pub fn to_naive_time(&self) -> ParseResult<NaiveTime> {
505 let hour_div_12 = match self.hour_div_12 {
506 Some(v @ 0..=1) => v,
507 Some(_) => return Err(OUT_OF_RANGE),
508 None => return Err(NOT_ENOUGH),
509 };
510 let hour_mod_12 = match self.hour_mod_12 {
511 Some(v @ 0..=11) => v,
512 Some(_) => return Err(OUT_OF_RANGE),
513 None => return Err(NOT_ENOUGH),
514 };
515 let hour = hour_div_12 * 12 + hour_mod_12;
516
517 let minute = match self.minute {
518 Some(v @ 0..=59) => v,
519 Some(_) => return Err(OUT_OF_RANGE),
520 None => return Err(NOT_ENOUGH),
521 };
522
523 // we allow omitting seconds or nanoseconds, but they should be in the range.
524 let (second, mut nano) = match self.second.unwrap_or(0) {
525 v @ 0..=59 => (v, 0),
526 60 => (59, 1_000_000_000),
527 _ => return Err(OUT_OF_RANGE),
528 };
529 nano += match self.nanosecond {
530 Some(v @ 0..=999_999_999) if self.second.is_some() => v,
531 Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing
532 Some(_) => return Err(OUT_OF_RANGE),
533 None => 0,
534 };
535
536 NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE)
537 }
538
539 /// Returns a parsed naive date and time out of given fields,
540 /// except for the [`offset`](#structfield.offset) field (assumed to have a given value).
541 /// This is required for parsing a local time or other known-timezone inputs.
542 ///
543 /// This method is able to determine the combined date and time
544 /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field.
545 /// Either way those fields have to be consistent to each other.
546 pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult<NaiveDateTime> {
547 let date = self.to_naive_date();
548 let time = self.to_naive_time();
549 if let (Ok(date), Ok(time)) = (date, time) {
550 let datetime = date.and_time(time);
551
552 // verify the timestamp field if any
553 // the following is safe, `timestamp` is very limited in range
554 let timestamp = datetime.timestamp() - i64::from(offset);
555 if let Some(given_timestamp) = self.timestamp {
556 // if `datetime` represents a leap second, it might be off by one second.
557 if given_timestamp != timestamp
558 && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1)
559 {
560 return Err(IMPOSSIBLE);
561 }
562 }
563
564 Ok(datetime)
565 } else if let Some(timestamp) = self.timestamp {
566 use super::ParseError as PE;
567 use super::ParseErrorKind::{Impossible, OutOfRange};
568
569 // if date and time is problematic already, there is no point proceeding.
570 // we at least try to give a correct error though.
571 match (date, time) {
572 (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE),
573 (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE),
574 (_, _) => {} // one of them is insufficient
575 }
576
577 // reconstruct date and time fields from timestamp
578 let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?;
579 let datetime = NaiveDateTime::from_timestamp_opt(ts, 0);
580 let mut datetime = datetime.ok_or(OUT_OF_RANGE)?;
581
582 // fill year, ordinal, hour, minute and second fields from timestamp.
583 // if existing fields are consistent, this will allow the full date/time reconstruction.
584 let mut parsed = self.clone();
585 if parsed.second == Some(60) {
586 // `datetime.second()` cannot be 60, so this is the only case for a leap second.
587 match datetime.second() {
588 // it's okay, just do not try to overwrite the existing field.
589 59 => {}
590 // `datetime` is known to be off by one second.
591 0 => {
592 datetime -= OldDuration::seconds(1);
593 }
594 // otherwise it is impossible.
595 _ => return Err(IMPOSSIBLE),
596 }
597 // ...and we have the correct candidates for other fields.
598 } else {
599 parsed.set_second(i64::from(datetime.second()))?;
600 }
601 parsed.set_year(i64::from(datetime.year()))?;
602 parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd
603 parsed.set_hour(i64::from(datetime.hour()))?;
604 parsed.set_minute(i64::from(datetime.minute()))?;
605
606 // validate other fields (e.g. week) and return
607 let date = parsed.to_naive_date()?;
608 let time = parsed.to_naive_time()?;
609 Ok(date.and_time(time))
610 } else {
611 // reproduce the previous error(s)
612 date?;
613 time?;
614 unreachable!()
615 }
616 }
617
618 /// Returns a parsed fixed time zone offset out of given fields.
619 pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> {
620 self.offset.and_then(FixedOffset::east_opt).ok_or(OUT_OF_RANGE)
621 }
622
623 /// Returns a parsed timezone-aware date and time out of given fields.
624 ///
625 /// This method is able to determine the combined date and time
626 /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field,
627 /// plus a time zone offset.
628 /// Either way those fields have to be consistent to each other.
629 pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
630 let offset = self.offset.ok_or(NOT_ENOUGH)?;
631 let datetime = self.to_naive_datetime_with_offset(offset)?;
632 let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?;
633
634 // this is used to prevent an overflow when calling FixedOffset::from_local_datetime
635 datetime
636 .checked_sub_signed(OldDuration::seconds(i64::from(offset.local_minus_utc())))
637 .ok_or(OUT_OF_RANGE)?;
638
639 match offset.from_local_datetime(&datetime) {
640 LocalResult::None => Err(IMPOSSIBLE),
641 LocalResult::Single(t) => Ok(t),
642 LocalResult::Ambiguous(..) => Err(NOT_ENOUGH),
643 }
644 }
645
646 /// Returns a parsed timezone-aware date and time out of given fields,
647 /// with an additional `TimeZone` used to interpret and validate the local date.
648 ///
649 /// This method is able to determine the combined date and time
650 /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field,
651 /// plus a time zone offset.
652 /// Either way those fields have to be consistent to each other.
653 /// If parsed fields include an UTC offset, it also has to be consistent to
654 /// [`offset`](#structfield.offset).
655 pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> {
656 // if we have `timestamp` specified, guess an offset from that.
657 let mut guessed_offset = 0;
658 if let Some(timestamp) = self.timestamp {
659 // make a naive `DateTime` from given timestamp and (if any) nanosecond.
660 // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
661 let nanosecond = self.nanosecond.unwrap_or(0);
662 let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
663 let dt = dt.ok_or(OUT_OF_RANGE)?;
664 guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
665 }
666
667 // checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
668 let check_offset = |dt: &DateTime<Tz>| {
669 if let Some(offset) = self.offset {
670 dt.offset().fix().local_minus_utc() == offset
671 } else {
672 true
673 }
674 };
675
676 // `guessed_offset` should be correct when `self.timestamp` is given.
677 // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case.
678 let datetime = self.to_naive_datetime_with_offset(guessed_offset)?;
679 match tz.from_local_datetime(&datetime) {
680 LocalResult::None => Err(IMPOSSIBLE),
681 LocalResult::Single(t) => {
682 if check_offset(&t) {
683 Ok(t)
684 } else {
685 Err(IMPOSSIBLE)
686 }
687 }
688 LocalResult::Ambiguous(min, max) => {
689 // try to disambiguate two possible local dates by offset.
690 match (check_offset(&min), check_offset(&max)) {
691 (false, false) => Err(IMPOSSIBLE),
692 (false, true) => Ok(max),
693 (true, false) => Ok(min),
694 (true, true) => Err(NOT_ENOUGH),
695 }
696 }
697 }
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
704 use super::Parsed;
705 use crate::naive::{NaiveDate, NaiveTime};
706 use crate::offset::{FixedOffset, TimeZone, Utc};
707 use crate::Datelike;
708 use crate::Weekday::*;
709
710 #[test]
711 fn test_parsed_set_fields() {
712 // year*, isoyear*
713 let mut p = Parsed::new();
714 assert_eq!(p.set_year(1987), Ok(()));
715 assert_eq!(p.set_year(1986), Err(IMPOSSIBLE));
716 assert_eq!(p.set_year(1988), Err(IMPOSSIBLE));
717 assert_eq!(p.set_year(1987), Ok(()));
718 assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year`
719 assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE));
720 assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE));
721 assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto
722 assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE));
723 assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE));
724
725 let mut p = Parsed::new();
726 assert_eq!(p.set_year(0), Ok(()));
727 assert_eq!(p.set_year_div_100(0), Ok(()));
728 assert_eq!(p.set_year_mod_100(0), Ok(()));
729
730 let mut p = Parsed::new();
731 assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE));
732 assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE));
733 assert_eq!(p.set_year(-1), Ok(()));
734 assert_eq!(p.set_year(-2), Err(IMPOSSIBLE));
735 assert_eq!(p.set_year(0), Err(IMPOSSIBLE));
736
737 let mut p = Parsed::new();
738 assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
739 assert_eq!(p.set_year_div_100(8), Ok(()));
740 assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
741
742 // month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset
743 let mut p = Parsed::new();
744 assert_eq!(p.set_month(7), Ok(()));
745 assert_eq!(p.set_month(1), Err(IMPOSSIBLE));
746 assert_eq!(p.set_month(6), Err(IMPOSSIBLE));
747 assert_eq!(p.set_month(8), Err(IMPOSSIBLE));
748 assert_eq!(p.set_month(12), Err(IMPOSSIBLE));
749
750 let mut p = Parsed::new();
751 assert_eq!(p.set_month(8), Ok(()));
752 assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE));
753
754 // hour
755 let mut p = Parsed::new();
756 assert_eq!(p.set_hour(12), Ok(()));
757 assert_eq!(p.set_hour(11), Err(IMPOSSIBLE));
758 assert_eq!(p.set_hour(13), Err(IMPOSSIBLE));
759 assert_eq!(p.set_hour(12), Ok(()));
760 assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE));
761 assert_eq!(p.set_ampm(true), Ok(()));
762 assert_eq!(p.set_hour12(12), Ok(()));
763 assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation
764 assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE));
765 assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE));
766
767 let mut p = Parsed::new();
768 assert_eq!(p.set_ampm(true), Ok(()));
769 assert_eq!(p.set_hour12(7), Ok(()));
770 assert_eq!(p.set_hour(7), Err(IMPOSSIBLE));
771 assert_eq!(p.set_hour(18), Err(IMPOSSIBLE));
772 assert_eq!(p.set_hour(19), Ok(()));
773
774 // timestamp
775 let mut p = Parsed::new();
776 assert_eq!(p.set_timestamp(1_234_567_890), Ok(()));
777 assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE));
778 assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE));
779 }
780
781 #[test]
782 fn test_parsed_to_naive_date() {
783 macro_rules! parse {
784 ($($k:ident: $v:expr),*) => (
785 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date()
786 )
787 }
788
789 let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap());
790
791 // ymd: omission of fields
792 assert_eq!(parse!(), Err(NOT_ENOUGH));
793 assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH));
794 assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH));
795 assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2));
796 assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH));
797 assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH));
798 assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH));
799 assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH));
800 assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2));
801 assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH));
802 assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH));
803 assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2));
804 assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2));
805
806 // ymd: out-of-range conditions
807 assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29));
808 assert_eq!(
809 parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29),
810 Err(OUT_OF_RANGE)
811 );
812 assert_eq!(
813 parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1),
814 Err(OUT_OF_RANGE)
815 );
816 assert_eq!(
817 parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31),
818 ymd(1983, 12, 31)
819 );
820 assert_eq!(
821 parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32),
822 Err(OUT_OF_RANGE)
823 );
824 assert_eq!(
825 parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0),
826 Err(OUT_OF_RANGE)
827 );
828 assert_eq!(
829 parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1),
830 Err(OUT_OF_RANGE)
831 );
832 assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE));
833 assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1));
834 assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(OUT_OF_RANGE));
835 let max_year = NaiveDate::MAX.year();
836 assert_eq!(
837 parse!(year_div_100: max_year / 100,
838 year_mod_100: max_year % 100, month: 1, day: 1),
839 ymd(max_year, 1, 1)
840 );
841 assert_eq!(
842 parse!(year_div_100: (max_year + 1) / 100,
843 year_mod_100: (max_year + 1) % 100, month: 1, day: 1),
844 Err(OUT_OF_RANGE)
845 );
846
847 // ymd: conflicting inputs
848 assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1));
849 assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE));
850 assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1));
851 assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE));
852 assert_eq!(
853 parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1),
854 ymd(1984, 1, 1)
855 );
856 assert_eq!(
857 parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1),
858 Err(IMPOSSIBLE)
859 );
860 assert_eq!(
861 parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1),
862 Err(OUT_OF_RANGE)
863 );
864 assert_eq!(
865 parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1),
866 Err(OUT_OF_RANGE)
867 );
868 assert_eq!(
869 parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1),
870 Err(OUT_OF_RANGE)
871 );
872 assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(OUT_OF_RANGE));
873 assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(OUT_OF_RANGE));
874
875 // weekdates
876 assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH));
877 assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH));
878 assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH));
879 assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(OUT_OF_RANGE));
880 assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(OUT_OF_RANGE));
881 assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1));
882 assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1));
883 assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2));
884 assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2));
885 assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3));
886 assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3));
887 assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8));
888 assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8));
889 assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9));
890 assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9));
891 assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10));
892 assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30));
893 assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31));
894 assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(OUT_OF_RANGE));
895 assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE));
896 assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(OUT_OF_RANGE));
897 assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1));
898
899 // weekdates: conflicting inputs
900 assert_eq!(
901 parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat),
902 ymd(2000, 1, 8)
903 );
904 assert_eq!(
905 parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun),
906 ymd(2000, 1, 9)
907 );
908 assert_eq!(
909 parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun),
910 Err(IMPOSSIBLE)
911 );
912 assert_eq!(
913 parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun),
914 Err(IMPOSSIBLE)
915 );
916
917 // ISO weekdates
918 assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH));
919 assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31));
920 assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1));
921 assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE));
922 assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE));
923 assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3));
924 assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH));
925
926 // year and ordinal
927 assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH));
928 assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE));
929 assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1));
930 assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29));
931 assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1));
932 assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31));
933 assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE));
934 assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
935 assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE));
936 assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1));
937 assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28));
938 assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1));
939 assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31));
940 assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE));
941 assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
942
943 // more complex cases
944 assert_eq!(
945 parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1,
946 week_from_sun: 52, week_from_mon: 52, weekday: Wed),
947 ymd(2014, 12, 31)
948 );
949 assert_eq!(
950 parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1,
951 week_from_sun: 52, week_from_mon: 52),
952 ymd(2014, 12, 31)
953 );
954 assert_eq!(
955 parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53,
956 week_from_sun: 52, week_from_mon: 52, weekday: Wed),
957 Err(IMPOSSIBLE)
958 ); // no ISO week date 2014-W53-3
959 assert_eq!(
960 parse!(year: 2012, isoyear: 2015, isoweek: 1,
961 week_from_sun: 52, week_from_mon: 52),
962 Err(NOT_ENOUGH)
963 ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31)
964 assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH));
965 // technically unique (2014-12-31) but Chrono gives up
966 }
967
968 #[test]
969 fn test_parsed_to_naive_time() {
970 macro_rules! parse {
971 ($($k:ident: $v:expr),*) => (
972 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time()
973 )
974 }
975
976 let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap());
977 let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap());
978
979 // omission of fields
980 assert_eq!(parse!(), Err(NOT_ENOUGH));
981 assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH));
982 assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH));
983 assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0));
984 assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45));
985 assert_eq!(
986 parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45,
987 nanosecond: 678_901_234),
988 hmsn(1, 23, 45, 678_901_234)
989 );
990 assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6));
991 assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH));
992 assert_eq!(
993 parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012),
994 Err(NOT_ENOUGH)
995 );
996
997 // out-of-range conditions
998 assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE));
999 assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE));
1000 assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE));
1001 assert_eq!(
1002 parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61),
1003 Err(OUT_OF_RANGE)
1004 );
1005 assert_eq!(
1006 parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34,
1007 nanosecond: 1_000_000_000),
1008 Err(OUT_OF_RANGE)
1009 );
1010
1011 // leap seconds
1012 assert_eq!(
1013 parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60),
1014 hmsn(1, 23, 59, 1_000_000_000)
1015 );
1016 assert_eq!(
1017 parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60,
1018 nanosecond: 999_999_999),
1019 hmsn(1, 23, 59, 1_999_999_999)
1020 );
1021 }
1022
1023 #[test]
1024 fn test_parsed_to_naive_datetime_with_offset() {
1025 macro_rules! parse {
1026 (offset = $offset:expr; $($k:ident: $v:expr),*) => (
1027 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset)
1028 );
1029 ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*))
1030 }
1031
1032 let ymdhms = |y, m, d, h, n, s| {
1033 Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap())
1034 };
1035 let ymdhmsn = |y, m, d, h, n, s, nano| {
1036 Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap())
1037 };
1038
1039 // omission of fields
1040 assert_eq!(parse!(), Err(NOT_ENOUGH));
1041 assert_eq!(
1042 parse!(year: 2015, month: 1, day: 30,
1043 hour_div_12: 1, hour_mod_12: 2, minute: 38),
1044 ymdhms(2015, 1, 30, 14, 38, 0)
1045 );
1046 assert_eq!(
1047 parse!(year: 1997, month: 1, day: 30,
1048 hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5),
1049 ymdhms(1997, 1, 30, 14, 38, 5)
1050 );
1051 assert_eq!(
1052 parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5,
1053 minute: 6, second: 7, nanosecond: 890_123_456),
1054 ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456)
1055 );
1056 assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0));
1057 assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1));
1058 assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1));
1059 assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40));
1060 assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44));
1061
1062 // full fields
1063 assert_eq!(
1064 parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1065 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1066 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1067 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1068 nanosecond: 12_345_678, timestamp: 1_420_000_000),
1069 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
1070 );
1071 assert_eq!(
1072 parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1073 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1074 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1075 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1076 nanosecond: 12_345_678, timestamp: 1_419_999_999),
1077 Err(IMPOSSIBLE)
1078 );
1079 assert_eq!(
1080 parse!(offset = 32400;
1081 year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
1082 ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
1083 isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
1084 hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
1085 nanosecond: 12_345_678, timestamp: 1_419_967_600),
1086 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
1087 );
1088
1089 // more timestamps
1090 let max_days_from_year_1970 =
1091 NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1092 let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1)
1093 .unwrap()
1094 .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1095 let min_days_from_year_1970 =
1096 NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
1097 assert_eq!(
1098 parse!(timestamp: min_days_from_year_1970.num_seconds()),
1099 ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0)
1100 );
1101 assert_eq!(
1102 parse!(timestamp: year_0_from_year_1970.num_seconds()),
1103 ymdhms(0, 1, 1, 0, 0, 0)
1104 );
1105 assert_eq!(
1106 parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
1107 ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59)
1108 );
1109
1110 // leap seconds #1: partial fields
1111 assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
1112 assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59));
1113 assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
1114 assert_eq!(
1115 parse!(second: 60, timestamp: 1_341_100_799),
1116 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1117 );
1118 assert_eq!(
1119 parse!(second: 60, timestamp: 1_341_100_800),
1120 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1121 );
1122 assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0));
1123 assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
1124 assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE));
1125
1126 // leap seconds #2: full fields
1127 // we need to have separate tests for them since it uses another control flow.
1128 assert_eq!(
1129 parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1130 minute: 59, second: 59, timestamp: 1_341_100_798),
1131 Err(IMPOSSIBLE)
1132 );
1133 assert_eq!(
1134 parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1135 minute: 59, second: 59, timestamp: 1_341_100_799),
1136 ymdhms(2012, 6, 30, 23, 59, 59)
1137 );
1138 assert_eq!(
1139 parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1140 minute: 59, second: 59, timestamp: 1_341_100_800),
1141 Err(IMPOSSIBLE)
1142 );
1143 assert_eq!(
1144 parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1145 minute: 59, second: 60, timestamp: 1_341_100_799),
1146 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1147 );
1148 assert_eq!(
1149 parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1150 minute: 59, second: 60, timestamp: 1_341_100_800),
1151 ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
1152 );
1153 assert_eq!(
1154 parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
1155 minute: 0, second: 0, timestamp: 1_341_100_800),
1156 ymdhms(2012, 7, 1, 0, 0, 0)
1157 );
1158 assert_eq!(
1159 parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
1160 minute: 0, second: 1, timestamp: 1_341_100_800),
1161 Err(IMPOSSIBLE)
1162 );
1163 assert_eq!(
1164 parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
1165 minute: 59, second: 60, timestamp: 1_341_100_801),
1166 Err(IMPOSSIBLE)
1167 );
1168
1169 // error codes
1170 assert_eq!(
1171 parse!(year: 2015, month: 1, day: 20, weekday: Tue,
1172 hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20),
1173 Err(OUT_OF_RANGE)
1174 ); // `hour_div_12` is out of range
1175 }
1176
1177 #[test]
1178 fn test_parsed_to_datetime() {
1179 macro_rules! parse {
1180 ($($k:ident: $v:expr),*) => (
1181 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime()
1182 )
1183 }
1184
1185 let ymdhmsn = |y, m, d, h, n, s, nano, off| {
1186 Ok(FixedOffset::east_opt(off)
1187 .unwrap()
1188 .from_local_datetime(
1189 &NaiveDate::from_ymd_opt(y, m, d)
1190 .unwrap()
1191 .and_hms_nano_opt(h, n, s, nano)
1192 .unwrap(),
1193 )
1194 .unwrap())
1195 };
1196
1197 assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH));
1198 assert_eq!(
1199 parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1200 minute: 26, second: 40, nanosecond: 12_345_678),
1201 Err(NOT_ENOUGH)
1202 );
1203 assert_eq!(
1204 parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1205 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1206 ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0)
1207 );
1208 assert_eq!(
1209 parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1210 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1211 ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400)
1212 );
1213 assert_eq!(
1214 parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1,
1215 minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876),
1216 ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876)
1217 );
1218 assert_eq!(
1219 parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4,
1220 minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400),
1221 Err(OUT_OF_RANGE)
1222 ); // `FixedOffset` does not support such huge offset
1223 }
1224
1225 #[test]
1226 fn test_parsed_to_datetime_with_timezone() {
1227 macro_rules! parse {
1228 ($tz:expr; $($k:ident: $v:expr),*) => (
1229 Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz)
1230 )
1231 }
1232
1233 // single result from ymdhms
1234 assert_eq!(
1235 parse!(Utc;
1236 year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1237 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1238 Ok(Utc
1239 .from_local_datetime(
1240 &NaiveDate::from_ymd_opt(2014, 12, 31)
1241 .unwrap()
1242 .and_hms_nano_opt(4, 26, 40, 12_345_678)
1243 .unwrap()
1244 )
1245 .unwrap())
1246 );
1247 assert_eq!(
1248 parse!(Utc;
1249 year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1250 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1251 Err(IMPOSSIBLE)
1252 );
1253 assert_eq!(
1254 parse!(FixedOffset::east_opt(32400).unwrap();
1255 year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
1256 minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
1257 Err(IMPOSSIBLE)
1258 );
1259 assert_eq!(
1260 parse!(FixedOffset::east_opt(32400).unwrap();
1261 year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
1262 minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
1263 Ok(FixedOffset::east_opt(32400)
1264 .unwrap()
1265 .from_local_datetime(
1266 &NaiveDate::from_ymd_opt(2014, 12, 31)
1267 .unwrap()
1268 .and_hms_nano_opt(13, 26, 40, 12_345_678)
1269 .unwrap()
1270 )
1271 .unwrap())
1272 );
1273
1274 // single result from timestamp
1275 assert_eq!(
1276 parse!(Utc; timestamp: 1_420_000_000, offset: 0),
1277 Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap())
1278 );
1279 assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE));
1280 assert_eq!(
1281 parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0),
1282 Err(IMPOSSIBLE)
1283 );
1284 assert_eq!(
1285 parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400),
1286 Ok(FixedOffset::east_opt(32400)
1287 .unwrap()
1288 .with_ymd_and_hms(2014, 12, 31, 13, 26, 40)
1289 .unwrap())
1290 );
1291
1292 // TODO test with a variable time zone (for None and Ambiguous cases)
1293 }
1294
1295 #[test]
1296 fn issue_551() {
1297 use crate::Weekday;
1298 let mut parsed = Parsed::new();
1299
1300 parsed.year = Some(2002);
1301 parsed.week_from_mon = Some(22);
1302 parsed.weekday = Some(Weekday::Mon);
1303 assert_eq!(NaiveDate::from_ymd_opt(2002, 6, 3).unwrap(), parsed.to_naive_date().unwrap());
1304
1305 parsed.year = Some(2001);
1306 assert_eq!(NaiveDate::from_ymd_opt(2001, 5, 28).unwrap(), parsed.to_naive_date().unwrap());
1307 }
1308}
1309