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