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