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, MappedLocalTime, Offset, TimeZone}; |
10 | use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday}; |
11 | |
12 | /// A type to hold parsed fields of date and time that can check all fields are consistent. |
13 | /// |
14 | /// There are three classes of methods: |
15 | /// |
16 | /// - `set_*` methods to set fields you have available. They do a basic range check, and if the |
17 | /// same field is set more than once it is checked for consistency. |
18 | /// |
19 | /// - `to_*` methods try to make a concrete date and time value out of set fields. |
20 | /// They fully check that all fields are consistent and whether the date/datetime exists. |
21 | /// |
22 | /// - Methods to inspect the parsed fields. |
23 | /// |
24 | /// `Parsed` is used internally by all parsing functions in chrono. It is a public type so that it |
25 | /// can be used to write custom parsers that reuse the resolving algorithm, or to inspect the |
26 | /// results of a string parsed with chrono without converting it to concrete types. |
27 | /// |
28 | /// # Resolving algorithm |
29 | /// |
30 | /// Resolving date/time parts is littered with lots of corner cases, which is why common date/time |
31 | /// parsers do not implement it correctly. |
32 | /// |
33 | /// Chrono provides a complete resolution algorithm that checks all fields for consistency via the |
34 | /// `Parsed` type. |
35 | /// |
36 | /// As an easy example, consider RFC 2822. The [RFC 2822 date and time format] has a day of the week |
37 | /// part, which should be consistent with the other date parts. But a `strptime`-based parse would |
38 | /// happily accept inconsistent input: |
39 | /// |
40 | /// ```python |
41 | /// >>> import time |
42 | /// >>> time.strptime('Wed, 31 Dec 2014 04:26:40 +0000', |
43 | /// '%a, %d %b %Y %H:%M:%S +0000') |
44 | /// time.struct_time(tm_year=2014, tm_mon=12, tm_mday=31, |
45 | /// tm_hour=4, tm_min=26, tm_sec=40, |
46 | /// tm_wday=2, tm_yday=365, tm_isdst=-1) |
47 | /// >>> time.strptime('Thu, 31 Dec 2014 04:26:40 +0000', |
48 | /// '%a, %d %b %Y %H:%M:%S +0000') |
49 | /// time.struct_time(tm_year=2014, tm_mon=12, tm_mday=31, |
50 | /// tm_hour=4, tm_min=26, tm_sec=40, |
51 | /// tm_wday=3, tm_yday=365, tm_isdst=-1) |
52 | /// ``` |
53 | /// |
54 | /// [RFC 2822 date and time format]: https://tools.ietf.org/html/rfc2822#section-3.3 |
55 | /// |
56 | /// # Example |
57 | /// |
58 | /// Let's see how `Parsed` correctly detects the second RFC 2822 string from before is inconsistent. |
59 | /// |
60 | /// ``` |
61 | /// # #[cfg (feature = "alloc" )] { |
62 | /// use chrono::format::{ParseErrorKind, Parsed}; |
63 | /// use chrono::Weekday; |
64 | /// |
65 | /// let mut parsed = Parsed::new(); |
66 | /// parsed.set_weekday(Weekday::Wed)?; |
67 | /// parsed.set_day(31)?; |
68 | /// parsed.set_month(12)?; |
69 | /// parsed.set_year(2014)?; |
70 | /// parsed.set_hour(4)?; |
71 | /// parsed.set_minute(26)?; |
72 | /// parsed.set_second(40)?; |
73 | /// parsed.set_offset(0)?; |
74 | /// let dt = parsed.to_datetime()?; |
75 | /// assert_eq!(dt.to_rfc2822(), "Wed, 31 Dec 2014 04:26:40 +0000" ); |
76 | /// |
77 | /// let mut parsed = Parsed::new(); |
78 | /// parsed.set_weekday(Weekday::Thu)?; // changed to the wrong day |
79 | /// parsed.set_day(31)?; |
80 | /// parsed.set_month(12)?; |
81 | /// parsed.set_year(2014)?; |
82 | /// parsed.set_hour(4)?; |
83 | /// parsed.set_minute(26)?; |
84 | /// parsed.set_second(40)?; |
85 | /// parsed.set_offset(0)?; |
86 | /// let result = parsed.to_datetime(); |
87 | /// |
88 | /// assert!(result.is_err()); |
89 | /// if let Err(error) = result { |
90 | /// assert_eq!(error.kind(), ParseErrorKind::Impossible); |
91 | /// } |
92 | /// # } |
93 | /// # Ok::<(), chrono::ParseError>(()) |
94 | /// ``` |
95 | /// |
96 | /// The same using chrono's build-in parser for RFC 2822 (the [RFC2822 formatting item]) and |
97 | /// [`format::parse()`] showing how to inspect a field on failure. |
98 | /// |
99 | /// [RFC2822 formatting item]: crate::format::Fixed::RFC2822 |
100 | /// [`format::parse()`]: crate::format::parse() |
101 | /// |
102 | /// ``` |
103 | /// # #[cfg (feature = "alloc" )] { |
104 | /// use chrono::format::{parse, Fixed, Item, Parsed}; |
105 | /// use chrono::Weekday; |
106 | /// |
107 | /// let rfc_2822 = [Item::Fixed(Fixed::RFC2822)]; |
108 | /// |
109 | /// let mut parsed = Parsed::new(); |
110 | /// parse(&mut parsed, "Wed, 31 Dec 2014 04:26:40 +0000" , rfc_2822.iter())?; |
111 | /// let dt = parsed.to_datetime()?; |
112 | /// |
113 | /// assert_eq!(dt.to_rfc2822(), "Wed, 31 Dec 2014 04:26:40 +0000" ); |
114 | /// |
115 | /// let mut parsed = Parsed::new(); |
116 | /// parse(&mut parsed, "Thu, 31 Dec 2014 04:26:40 +0000" , rfc_2822.iter())?; |
117 | /// let result = parsed.to_datetime(); |
118 | /// |
119 | /// assert!(result.is_err()); |
120 | /// if result.is_err() { |
121 | /// // What is the weekday? |
122 | /// assert_eq!(parsed.weekday(), Some(Weekday::Thu)); |
123 | /// } |
124 | /// # } |
125 | /// # Ok::<(), chrono::ParseError>(()) |
126 | /// ``` |
127 | #[allow (clippy::manual_non_exhaustive)] |
128 | #[derive (Clone, PartialEq, Eq, Debug, Default, Hash)] |
129 | pub struct Parsed { |
130 | #[doc (hidden)] |
131 | pub year: Option<i32>, |
132 | #[doc (hidden)] |
133 | pub year_div_100: Option<i32>, |
134 | #[doc (hidden)] |
135 | pub year_mod_100: Option<i32>, |
136 | #[doc (hidden)] |
137 | pub isoyear: Option<i32>, |
138 | #[doc (hidden)] |
139 | pub isoyear_div_100: Option<i32>, |
140 | #[doc (hidden)] |
141 | pub isoyear_mod_100: Option<i32>, |
142 | #[doc (hidden)] |
143 | pub month: Option<u32>, |
144 | #[doc (hidden)] |
145 | pub week_from_sun: Option<u32>, |
146 | #[doc (hidden)] |
147 | pub week_from_mon: Option<u32>, |
148 | #[doc (hidden)] |
149 | pub isoweek: Option<u32>, |
150 | #[doc (hidden)] |
151 | pub weekday: Option<Weekday>, |
152 | #[doc (hidden)] |
153 | pub ordinal: Option<u32>, |
154 | #[doc (hidden)] |
155 | pub day: Option<u32>, |
156 | #[doc (hidden)] |
157 | pub hour_div_12: Option<u32>, |
158 | #[doc (hidden)] |
159 | pub hour_mod_12: Option<u32>, |
160 | #[doc (hidden)] |
161 | pub minute: Option<u32>, |
162 | #[doc (hidden)] |
163 | pub second: Option<u32>, |
164 | #[doc (hidden)] |
165 | pub nanosecond: Option<u32>, |
166 | #[doc (hidden)] |
167 | pub timestamp: Option<i64>, |
168 | #[doc (hidden)] |
169 | pub offset: Option<i32>, |
170 | #[doc (hidden)] |
171 | _dummy: (), |
172 | } |
173 | |
174 | /// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"), |
175 | /// and if it is empty, set `old` to `new` as well. |
176 | #[inline ] |
177 | fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> { |
178 | if let Some(ref old: &T) = *old { |
179 | if *old == new { |
180 | Ok(()) |
181 | } else { |
182 | Err(IMPOSSIBLE) |
183 | } |
184 | } else { |
185 | *old = Some(new); |
186 | Ok(()) |
187 | } |
188 | } |
189 | |
190 | impl Parsed { |
191 | /// Returns the initial value of parsed parts. |
192 | #[must_use ] |
193 | pub fn new() -> Parsed { |
194 | Parsed::default() |
195 | } |
196 | |
197 | /// Set the [`year`](Parsed::year) field to the given value. |
198 | /// |
199 | /// The value can be negative, unlike the [`year_div_100`](Parsed::year_div_100) and |
200 | /// [`year_mod_100`](Parsed::year_mod_100) fields. |
201 | /// |
202 | /// # Errors |
203 | /// |
204 | /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. |
205 | /// |
206 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
207 | #[inline ] |
208 | pub fn set_year(&mut self, value: i64) -> ParseResult<()> { |
209 | set_if_consistent(&mut self.year, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) |
210 | } |
211 | |
212 | /// Set the [`year_div_100`](Parsed::year_div_100) field to the given value. |
213 | /// |
214 | /// # Errors |
215 | /// |
216 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`. |
217 | /// |
218 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
219 | #[inline ] |
220 | pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> { |
221 | if !(0..=i32::MAX as i64).contains(&value) { |
222 | return Err(OUT_OF_RANGE); |
223 | } |
224 | set_if_consistent(&mut self.year_div_100, value as i32) |
225 | } |
226 | |
227 | /// Set the [`year_mod_100`](Parsed::year_mod_100) field to the given value. |
228 | /// |
229 | /// When set it implies that the year is not negative. |
230 | /// |
231 | /// If this field is set while the [`year_div_100`](Parsed::year_div_100) field is missing (and |
232 | /// the full [`year`](Parsed::year) field is also not set), it assumes a default value for the |
233 | /// [`year_div_100`](Parsed::year_div_100) field. |
234 | /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise. |
235 | /// |
236 | /// # Errors |
237 | /// |
238 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99. |
239 | /// |
240 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
241 | #[inline ] |
242 | pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> { |
243 | if !(0..100).contains(&value) { |
244 | return Err(OUT_OF_RANGE); |
245 | } |
246 | set_if_consistent(&mut self.year_mod_100, value as i32) |
247 | } |
248 | |
249 | /// Set the [`isoyear`](Parsed::isoyear) field, that is part of an [ISO 8601 week date], to the |
250 | /// given value. |
251 | /// |
252 | /// The value can be negative, unlike the [`isoyear_div_100`](Parsed::isoyear_div_100) and |
253 | /// [`isoyear_mod_100`](Parsed::isoyear_mod_100) fields. |
254 | /// |
255 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
256 | /// |
257 | /// # Errors |
258 | /// |
259 | /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. |
260 | /// |
261 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
262 | #[inline ] |
263 | pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> { |
264 | set_if_consistent(&mut self.isoyear, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) |
265 | } |
266 | |
267 | /// Set the [`isoyear_div_100`](Parsed::isoyear_div_100) field, that is part of an |
268 | /// [ISO 8601 week date], to the given value. |
269 | /// |
270 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
271 | /// |
272 | /// # Errors |
273 | /// |
274 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`. |
275 | /// |
276 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
277 | #[inline ] |
278 | pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> { |
279 | if !(0..=i32::MAX as i64).contains(&value) { |
280 | return Err(OUT_OF_RANGE); |
281 | } |
282 | set_if_consistent(&mut self.isoyear_div_100, value as i32) |
283 | } |
284 | |
285 | /// Set the [`isoyear_mod_100`](Parsed::isoyear_mod_100) field, that is part of an |
286 | /// [ISO 8601 week date], to the given value. |
287 | /// |
288 | /// When set it implies that the year is not negative. |
289 | /// |
290 | /// If this field is set while the [`isoyear_div_100`](Parsed::isoyear_div_100) field is missing |
291 | /// (and the full [`isoyear`](Parsed::isoyear) field is also not set), it assumes a default |
292 | /// value for the [`isoyear_div_100`](Parsed::isoyear_div_100) field. |
293 | /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise. |
294 | /// |
295 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
296 | /// |
297 | /// # Errors |
298 | /// |
299 | /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99. |
300 | /// |
301 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
302 | #[inline ] |
303 | pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> { |
304 | if !(0..100).contains(&value) { |
305 | return Err(OUT_OF_RANGE); |
306 | } |
307 | set_if_consistent(&mut self.isoyear_mod_100, value as i32) |
308 | } |
309 | |
310 | /// Set the [`month`](Parsed::month) field to the given value. |
311 | /// |
312 | /// # Errors |
313 | /// |
314 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12. |
315 | /// |
316 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
317 | #[inline ] |
318 | pub fn set_month(&mut self, value: i64) -> ParseResult<()> { |
319 | if !(1..=12).contains(&value) { |
320 | return Err(OUT_OF_RANGE); |
321 | } |
322 | set_if_consistent(&mut self.month, value as u32) |
323 | } |
324 | |
325 | /// Set the [`week_from_sun`](Parsed::week_from_sun) week number field to the given value. |
326 | /// |
327 | /// Week 1 starts at the first Sunday of January. |
328 | /// |
329 | /// # Errors |
330 | /// |
331 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53. |
332 | /// |
333 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
334 | #[inline ] |
335 | pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> { |
336 | if !(0..=53).contains(&value) { |
337 | return Err(OUT_OF_RANGE); |
338 | } |
339 | set_if_consistent(&mut self.week_from_sun, value as u32) |
340 | } |
341 | |
342 | /// Set the [`week_from_mon`](Parsed::week_from_mon) week number field to the given value. |
343 | /// Set the 'week number starting with Monday' field to the given value. |
344 | /// |
345 | /// Week 1 starts at the first Monday of January. |
346 | /// |
347 | /// # Errors |
348 | /// |
349 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53. |
350 | /// |
351 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
352 | #[inline ] |
353 | pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> { |
354 | if !(0..=53).contains(&value) { |
355 | return Err(OUT_OF_RANGE); |
356 | } |
357 | set_if_consistent(&mut self.week_from_mon, value as u32) |
358 | } |
359 | |
360 | /// Set the [`isoweek`](Parsed::isoweek) field for an [ISO 8601 week date] to the given value. |
361 | /// |
362 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
363 | /// |
364 | /// # Errors |
365 | /// |
366 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-53. |
367 | /// |
368 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
369 | #[inline ] |
370 | pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> { |
371 | if !(1..=53).contains(&value) { |
372 | return Err(OUT_OF_RANGE); |
373 | } |
374 | set_if_consistent(&mut self.isoweek, value as u32) |
375 | } |
376 | |
377 | /// Set the [`weekday`](Parsed::weekday) field to the given value. |
378 | /// |
379 | /// # Errors |
380 | /// |
381 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
382 | #[inline ] |
383 | pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> { |
384 | set_if_consistent(&mut self.weekday, value) |
385 | } |
386 | |
387 | /// Set the [`ordinal`](Parsed::ordinal) (day of the year) field to the given value. |
388 | /// |
389 | /// # Errors |
390 | /// |
391 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-366. |
392 | /// |
393 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
394 | #[inline ] |
395 | pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> { |
396 | if !(1..=366).contains(&value) { |
397 | return Err(OUT_OF_RANGE); |
398 | } |
399 | set_if_consistent(&mut self.ordinal, value as u32) |
400 | } |
401 | |
402 | /// Set the [`day`](Parsed::day) of the month field to the given value. |
403 | /// |
404 | /// # Errors |
405 | /// |
406 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-31. |
407 | /// |
408 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
409 | #[inline ] |
410 | pub fn set_day(&mut self, value: i64) -> ParseResult<()> { |
411 | if !(1..=31).contains(&value) { |
412 | return Err(OUT_OF_RANGE); |
413 | } |
414 | set_if_consistent(&mut self.day, value as u32) |
415 | } |
416 | |
417 | /// Set the [`hour_div_12`](Parsed::hour_div_12) am/pm field to the given value. |
418 | /// |
419 | /// `false` indicates AM and `true` indicates PM. |
420 | /// |
421 | /// # Errors |
422 | /// |
423 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
424 | #[inline ] |
425 | pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> { |
426 | set_if_consistent(&mut self.hour_div_12, value as u32) |
427 | } |
428 | |
429 | /// Set the [`hour_mod_12`](Parsed::hour_mod_12) field, for the hour number in 12-hour clocks, |
430 | /// to the given value. |
431 | /// |
432 | /// Value must be in the canonical range of 1-12. |
433 | /// It will internally be stored as 0-11 (`value % 12`). |
434 | /// |
435 | /// # Errors |
436 | /// |
437 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12. |
438 | /// |
439 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
440 | #[inline ] |
441 | pub fn set_hour12(&mut self, mut value: i64) -> ParseResult<()> { |
442 | if !(1..=12).contains(&value) { |
443 | return Err(OUT_OF_RANGE); |
444 | } |
445 | if value == 12 { |
446 | value = 0 |
447 | } |
448 | set_if_consistent(&mut self.hour_mod_12, value as u32) |
449 | } |
450 | |
451 | /// Set the [`hour_div_12`](Parsed::hour_div_12) and [`hour_mod_12`](Parsed::hour_mod_12) |
452 | /// fields to the given value for a 24-hour clock. |
453 | /// |
454 | /// # Errors |
455 | /// |
456 | /// May return `OUT_OF_RANGE` if `value` is not in the range 0-23. |
457 | /// Currently only checks the value is not out of range for a `u32`. |
458 | /// |
459 | /// Returns `IMPOSSIBLE` one of the fields was already set to a different value. |
460 | #[inline ] |
461 | pub fn set_hour(&mut self, value: i64) -> ParseResult<()> { |
462 | let (hour_div_12, hour_mod_12) = match value { |
463 | hour @ 0..=11 => (0, hour as u32), |
464 | hour @ 12..=23 => (1, hour as u32 - 12), |
465 | _ => return Err(OUT_OF_RANGE), |
466 | }; |
467 | set_if_consistent(&mut self.hour_div_12, hour_div_12)?; |
468 | set_if_consistent(&mut self.hour_mod_12, hour_mod_12) |
469 | } |
470 | |
471 | /// Set the [`minute`](Parsed::minute) field to the given value. |
472 | /// |
473 | /// # Errors |
474 | /// |
475 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-59. |
476 | /// |
477 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
478 | #[inline ] |
479 | pub fn set_minute(&mut self, value: i64) -> ParseResult<()> { |
480 | if !(0..=59).contains(&value) { |
481 | return Err(OUT_OF_RANGE); |
482 | } |
483 | set_if_consistent(&mut self.minute, value as u32) |
484 | } |
485 | |
486 | /// Set the [`second`](Parsed::second) field to the given value. |
487 | /// |
488 | /// The value can be 60 in the case of a leap second. |
489 | /// |
490 | /// # Errors |
491 | /// |
492 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-60. |
493 | /// |
494 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
495 | #[inline ] |
496 | pub fn set_second(&mut self, value: i64) -> ParseResult<()> { |
497 | if !(0..=60).contains(&value) { |
498 | return Err(OUT_OF_RANGE); |
499 | } |
500 | set_if_consistent(&mut self.second, value as u32) |
501 | } |
502 | |
503 | /// Set the [`nanosecond`](Parsed::nanosecond) field to the given value. |
504 | /// |
505 | /// This is the number of nanoseconds since the whole second. |
506 | /// |
507 | /// # Errors |
508 | /// |
509 | /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-999,999,999. |
510 | /// |
511 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
512 | #[inline ] |
513 | pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> { |
514 | if !(0..=999_999_999).contains(&value) { |
515 | return Err(OUT_OF_RANGE); |
516 | } |
517 | set_if_consistent(&mut self.nanosecond, value as u32) |
518 | } |
519 | |
520 | /// Set the [`timestamp`](Parsed::timestamp) field to the given value. |
521 | /// |
522 | /// A Unix timestamp is defined as the number of non-leap seconds since midnight UTC on |
523 | /// January 1, 1970. |
524 | /// |
525 | /// # Errors |
526 | /// |
527 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
528 | #[inline ] |
529 | pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> { |
530 | set_if_consistent(&mut self.timestamp, value) |
531 | } |
532 | |
533 | /// Set the [`offset`](Parsed::offset) field to the given value. |
534 | /// |
535 | /// The offset is in seconds from local time to UTC. |
536 | /// |
537 | /// # Errors |
538 | /// |
539 | /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. |
540 | /// |
541 | /// Returns `IMPOSSIBLE` if this field was already set to a different value. |
542 | #[inline ] |
543 | pub fn set_offset(&mut self, value: i64) -> ParseResult<()> { |
544 | set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) |
545 | } |
546 | |
547 | /// Returns a parsed naive date out of given fields. |
548 | /// |
549 | /// This method is able to determine the date from given subset of fields: |
550 | /// |
551 | /// - Year, month, day. |
552 | /// - Year, day of the year (ordinal). |
553 | /// - Year, week number counted from Sunday or Monday, day of the week. |
554 | /// - ISO week date. |
555 | /// |
556 | /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted, |
557 | /// the two-digit year is used to guess the century number then. |
558 | /// |
559 | /// It checks all given date fields are consistent with each other. |
560 | /// |
561 | /// # Errors |
562 | /// |
563 | /// This method returns: |
564 | /// - `IMPOSSIBLE` if any of the date fields conflict. |
565 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete date. |
566 | /// - `OUT_OF_RANGE` |
567 | /// - if any of the date fields of `Parsed` are set to a value beyond their acceptable range. |
568 | /// - if the value would be outside the range of a [`NaiveDate`]. |
569 | /// - if the date does not exist. |
570 | pub fn to_naive_date(&self) -> ParseResult<NaiveDate> { |
571 | fn resolve_year( |
572 | y: Option<i32>, |
573 | q: Option<i32>, |
574 | r: Option<i32>, |
575 | ) -> ParseResult<Option<i32>> { |
576 | match (y, q, r) { |
577 | // if there is no further information, simply return the given full year. |
578 | // this is a common case, so let's avoid division here. |
579 | (y, None, None) => Ok(y), |
580 | |
581 | // if there is a full year *and* also quotient and/or modulo, |
582 | // check if present quotient and/or modulo is consistent to the full year. |
583 | // since the presence of those fields means a positive full year, |
584 | // we should filter a negative full year first. |
585 | (Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => { |
586 | if y < 0 { |
587 | return Err(IMPOSSIBLE); |
588 | } |
589 | let q_ = y / 100; |
590 | let r_ = y % 100; |
591 | if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ { |
592 | Ok(Some(y)) |
593 | } else { |
594 | Err(IMPOSSIBLE) |
595 | } |
596 | } |
597 | |
598 | // the full year is missing but we have quotient and modulo. |
599 | // reconstruct the full year. make sure that the result is always positive. |
600 | (None, Some(q), Some(r @ 0..=99)) => { |
601 | if q < 0 { |
602 | return Err(IMPOSSIBLE); |
603 | } |
604 | let y = q.checked_mul(100).and_then(|v| v.checked_add(r)); |
605 | Ok(Some(y.ok_or(OUT_OF_RANGE)?)) |
606 | } |
607 | |
608 | // we only have modulo. try to interpret a modulo as a conventional two-digit year. |
609 | // note: we are affected by Rust issue #18060. avoid multiple range patterns. |
610 | (None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })), |
611 | |
612 | // otherwise it is an out-of-bound or insufficient condition. |
613 | (None, Some(_), None) => Err(NOT_ENOUGH), |
614 | (_, _, Some(_)) => Err(OUT_OF_RANGE), |
615 | } |
616 | } |
617 | |
618 | let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?; |
619 | let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?; |
620 | |
621 | // verify the normal year-month-day date. |
622 | let verify_ymd = |date: NaiveDate| { |
623 | let year = date.year(); |
624 | let (year_div_100, year_mod_100) = if year >= 0 { |
625 | (Some(year / 100), Some(year % 100)) |
626 | } else { |
627 | (None, None) // they should be empty to be consistent |
628 | }; |
629 | let month = date.month(); |
630 | let day = date.day(); |
631 | self.year.unwrap_or(year) == year |
632 | && self.year_div_100.or(year_div_100) == year_div_100 |
633 | && self.year_mod_100.or(year_mod_100) == year_mod_100 |
634 | && self.month.unwrap_or(month) == month |
635 | && self.day.unwrap_or(day) == day |
636 | }; |
637 | |
638 | // verify the ISO week date. |
639 | let verify_isoweekdate = |date: NaiveDate| { |
640 | let week = date.iso_week(); |
641 | let isoyear = week.year(); |
642 | let isoweek = week.week(); |
643 | let weekday = date.weekday(); |
644 | let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 { |
645 | (Some(isoyear / 100), Some(isoyear % 100)) |
646 | } else { |
647 | (None, None) // they should be empty to be consistent |
648 | }; |
649 | self.isoyear.unwrap_or(isoyear) == isoyear |
650 | && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100 |
651 | && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100 |
652 | && self.isoweek.unwrap_or(isoweek) == isoweek |
653 | && self.weekday.unwrap_or(weekday) == weekday |
654 | }; |
655 | |
656 | // verify the ordinal and other (non-ISO) week dates. |
657 | let verify_ordinal = |date: NaiveDate| { |
658 | let ordinal = date.ordinal(); |
659 | let week_from_sun = date.weeks_from(Weekday::Sun); |
660 | let week_from_mon = date.weeks_from(Weekday::Mon); |
661 | self.ordinal.unwrap_or(ordinal) == ordinal |
662 | && self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun |
663 | && self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon |
664 | }; |
665 | |
666 | // test several possibilities. |
667 | // tries to construct a full `NaiveDate` as much as possible, then verifies that |
668 | // it is consistent with other given fields. |
669 | let (verified, parsed_date) = match (given_year, given_isoyear, self) { |
670 | (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => { |
671 | // year, month, day |
672 | let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?; |
673 | (verify_isoweekdate(date) && verify_ordinal(date), date) |
674 | } |
675 | |
676 | (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => { |
677 | // year, day of the year |
678 | let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?; |
679 | (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) |
680 | } |
681 | |
682 | (Some(year), _, &Parsed { week_from_sun: Some(week), weekday: Some(weekday), .. }) => { |
683 | // year, week (starting at 1st Sunday), day of the week |
684 | let date = resolve_week_date(year, week, weekday, Weekday::Sun)?; |
685 | (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) |
686 | } |
687 | |
688 | (Some(year), _, &Parsed { week_from_mon: Some(week), weekday: Some(weekday), .. }) => { |
689 | // year, week (starting at 1st Monday), day of the week |
690 | let date = resolve_week_date(year, week, weekday, Weekday::Mon)?; |
691 | (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) |
692 | } |
693 | |
694 | (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => { |
695 | // ISO year, week, day of the week |
696 | let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday); |
697 | let date = date.ok_or(OUT_OF_RANGE)?; |
698 | (verify_ymd(date) && verify_ordinal(date), date) |
699 | } |
700 | |
701 | (_, _, _) => return Err(NOT_ENOUGH), |
702 | }; |
703 | |
704 | if verified { |
705 | Ok(parsed_date) |
706 | } else { |
707 | Err(IMPOSSIBLE) |
708 | } |
709 | } |
710 | |
711 | /// Returns a parsed naive time out of given fields. |
712 | /// |
713 | /// This method is able to determine the time from given subset of fields: |
714 | /// |
715 | /// - Hour, minute. (second and nanosecond assumed to be 0) |
716 | /// - Hour, minute, second. (nanosecond assumed to be 0) |
717 | /// - Hour, minute, second, nanosecond. |
718 | /// |
719 | /// It is able to handle leap seconds when given second is 60. |
720 | /// |
721 | /// # Errors |
722 | /// |
723 | /// This method returns: |
724 | /// - `OUT_OF_RANGE` if any of the time fields of `Parsed` are set to a value beyond |
725 | /// their acceptable range. |
726 | /// - `NOT_ENOUGH` if an hour field is missing, if AM/PM is missing in a 12-hour clock, |
727 | /// if minutes are missing, or if seconds are missing while the nanosecond field is present. |
728 | pub fn to_naive_time(&self) -> ParseResult<NaiveTime> { |
729 | let hour_div_12 = match self.hour_div_12 { |
730 | Some(v @ 0..=1) => v, |
731 | Some(_) => return Err(OUT_OF_RANGE), |
732 | None => return Err(NOT_ENOUGH), |
733 | }; |
734 | let hour_mod_12 = match self.hour_mod_12 { |
735 | Some(v @ 0..=11) => v, |
736 | Some(_) => return Err(OUT_OF_RANGE), |
737 | None => return Err(NOT_ENOUGH), |
738 | }; |
739 | let hour = hour_div_12 * 12 + hour_mod_12; |
740 | |
741 | let minute = match self.minute { |
742 | Some(v @ 0..=59) => v, |
743 | Some(_) => return Err(OUT_OF_RANGE), |
744 | None => return Err(NOT_ENOUGH), |
745 | }; |
746 | |
747 | // we allow omitting seconds or nanoseconds, but they should be in the range. |
748 | let (second, mut nano) = match self.second.unwrap_or(0) { |
749 | v @ 0..=59 => (v, 0), |
750 | 60 => (59, 1_000_000_000), |
751 | _ => return Err(OUT_OF_RANGE), |
752 | }; |
753 | nano += match self.nanosecond { |
754 | Some(v @ 0..=999_999_999) if self.second.is_some() => v, |
755 | Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing |
756 | Some(_) => return Err(OUT_OF_RANGE), |
757 | None => 0, |
758 | }; |
759 | |
760 | NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE) |
761 | } |
762 | |
763 | /// Returns a parsed naive date and time out of given fields, except for the offset field. |
764 | /// |
765 | /// The offset is assumed to have a given value. It is not compared against the offset field set |
766 | /// in the `Parsed` type, so it is allowed to be inconsistent. |
767 | /// |
768 | /// This method is able to determine the combined date and time from date and time fields or |
769 | /// from a single timestamp field. It checks all fields are consistent with each other. |
770 | /// |
771 | /// # Errors |
772 | /// |
773 | /// This method returns: |
774 | /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of |
775 | /// the other fields. |
776 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime. |
777 | /// - `OUT_OF_RANGE` |
778 | /// - if any of the date or time fields of `Parsed` are set to a value beyond their acceptable |
779 | /// range. |
780 | /// - if the value would be outside the range of a [`NaiveDateTime`]. |
781 | /// - if the date does not exist. |
782 | pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult<NaiveDateTime> { |
783 | let date = self.to_naive_date(); |
784 | let time = self.to_naive_time(); |
785 | if let (Ok(date), Ok(time)) = (date, time) { |
786 | let datetime = date.and_time(time); |
787 | |
788 | // verify the timestamp field if any |
789 | // the following is safe, `timestamp` is very limited in range |
790 | let timestamp = datetime.and_utc().timestamp() - i64::from(offset); |
791 | if let Some(given_timestamp) = self.timestamp { |
792 | // if `datetime` represents a leap second, it might be off by one second. |
793 | if given_timestamp != timestamp |
794 | && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1) |
795 | { |
796 | return Err(IMPOSSIBLE); |
797 | } |
798 | } |
799 | |
800 | Ok(datetime) |
801 | } else if let Some(timestamp) = self.timestamp { |
802 | use super::ParseError as PE; |
803 | use super::ParseErrorKind::{Impossible, OutOfRange}; |
804 | |
805 | // if date and time is problematic already, there is no point proceeding. |
806 | // we at least try to give a correct error though. |
807 | match (date, time) { |
808 | (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE), |
809 | (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE), |
810 | (_, _) => {} // one of them is insufficient |
811 | } |
812 | |
813 | // reconstruct date and time fields from timestamp |
814 | let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?; |
815 | let mut datetime = DateTime::from_timestamp(ts, 0).ok_or(OUT_OF_RANGE)?.naive_utc(); |
816 | |
817 | // fill year, ordinal, hour, minute and second fields from timestamp. |
818 | // if existing fields are consistent, this will allow the full date/time reconstruction. |
819 | let mut parsed = self.clone(); |
820 | if parsed.second == Some(60) { |
821 | // `datetime.second()` cannot be 60, so this is the only case for a leap second. |
822 | match datetime.second() { |
823 | // it's okay, just do not try to overwrite the existing field. |
824 | 59 => {} |
825 | // `datetime` is known to be off by one second. |
826 | 0 => { |
827 | datetime -= TimeDelta::try_seconds(1).unwrap(); |
828 | } |
829 | // otherwise it is impossible. |
830 | _ => return Err(IMPOSSIBLE), |
831 | } |
832 | // ...and we have the correct candidates for other fields. |
833 | } else { |
834 | parsed.set_second(i64::from(datetime.second()))?; |
835 | } |
836 | parsed.set_year(i64::from(datetime.year()))?; |
837 | parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd |
838 | parsed.set_hour(i64::from(datetime.hour()))?; |
839 | parsed.set_minute(i64::from(datetime.minute()))?; |
840 | |
841 | // validate other fields (e.g. week) and return |
842 | let date = parsed.to_naive_date()?; |
843 | let time = parsed.to_naive_time()?; |
844 | Ok(date.and_time(time)) |
845 | } else { |
846 | // reproduce the previous error(s) |
847 | date?; |
848 | time?; |
849 | unreachable!() |
850 | } |
851 | } |
852 | |
853 | /// Returns a parsed fixed time zone offset out of given fields. |
854 | /// |
855 | /// # Errors |
856 | /// |
857 | /// This method returns: |
858 | /// - `OUT_OF_RANGE` if the offset is out of range for a `FixedOffset`. |
859 | /// - `NOT_ENOUGH` if the offset field is not set. |
860 | pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> { |
861 | FixedOffset::east_opt(self.offset.ok_or(NOT_ENOUGH)?).ok_or(OUT_OF_RANGE) |
862 | } |
863 | |
864 | /// Returns a parsed timezone-aware date and time out of given fields. |
865 | /// |
866 | /// This method is able to determine the combined date and time from date, time and offset |
867 | /// fields, and/or from a single timestamp field. It checks all fields are consistent with each |
868 | /// other. |
869 | /// |
870 | /// # Errors |
871 | /// |
872 | /// This method returns: |
873 | /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of |
874 | /// the other fields. |
875 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime |
876 | /// including offset from UTC. |
877 | /// - `OUT_OF_RANGE` |
878 | /// - if any of the fields of `Parsed` are set to a value beyond their acceptable |
879 | /// range. |
880 | /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`]. |
881 | /// - if the date does not exist. |
882 | pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> { |
883 | // If there is no explicit offset, consider a timestamp value as indication of a UTC value. |
884 | let offset = match (self.offset, self.timestamp) { |
885 | (Some(off), _) => off, |
886 | (None, Some(_)) => 0, // UNIX timestamp may assume 0 offset |
887 | (None, None) => return Err(NOT_ENOUGH), |
888 | }; |
889 | let datetime = self.to_naive_datetime_with_offset(offset)?; |
890 | let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?; |
891 | |
892 | match offset.from_local_datetime(&datetime) { |
893 | MappedLocalTime::None => Err(IMPOSSIBLE), |
894 | MappedLocalTime::Single(t) => Ok(t), |
895 | MappedLocalTime::Ambiguous(..) => Err(NOT_ENOUGH), |
896 | } |
897 | } |
898 | |
899 | /// Returns a parsed timezone-aware date and time out of given fields, |
900 | /// with an additional [`TimeZone`] used to interpret and validate the local date. |
901 | /// |
902 | /// This method is able to determine the combined date and time from date and time, and/or from |
903 | /// a single timestamp field. It checks all fields are consistent with each other. |
904 | /// |
905 | /// If the parsed fields include an UTC offset, it also has to be consistent with the offset in |
906 | /// the provided `tz` time zone for that datetime. |
907 | /// |
908 | /// # Errors |
909 | /// |
910 | /// This method returns: |
911 | /// - `IMPOSSIBLE` |
912 | /// - if any of the date fields conflict, if a timestamp conflicts with any of the other |
913 | /// fields, or if the offset field is set but differs from the offset at that time in the |
914 | /// `tz` time zone. |
915 | /// - if the local datetime does not exists in the provided time zone (because it falls in a |
916 | /// transition due to for example DST). |
917 | /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime, or if |
918 | /// the local time in the provided time zone is ambiguous (because it falls in a transition |
919 | /// due to for example DST) while there is no offset field or timestamp field set. |
920 | /// - `OUT_OF_RANGE` |
921 | /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`]. |
922 | /// - if any of the fields of `Parsed` are set to a value beyond their acceptable range. |
923 | /// - if the date does not exist. |
924 | pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> { |
925 | // if we have `timestamp` specified, guess an offset from that. |
926 | let mut guessed_offset = 0; |
927 | if let Some(timestamp) = self.timestamp { |
928 | // make a naive `DateTime` from given timestamp and (if any) nanosecond. |
929 | // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine. |
930 | let nanosecond = self.nanosecond.unwrap_or(0); |
931 | let dt = |
932 | DateTime::from_timestamp(timestamp, nanosecond).ok_or(OUT_OF_RANGE)?.naive_utc(); |
933 | guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc(); |
934 | } |
935 | |
936 | // checks if the given `DateTime` has a consistent `Offset` with given `self.offset`. |
937 | let check_offset = |dt: &DateTime<Tz>| { |
938 | if let Some(offset) = self.offset { |
939 | dt.offset().fix().local_minus_utc() == offset |
940 | } else { |
941 | true |
942 | } |
943 | }; |
944 | |
945 | // `guessed_offset` should be correct when `self.timestamp` is given. |
946 | // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case. |
947 | let datetime = self.to_naive_datetime_with_offset(guessed_offset)?; |
948 | match tz.from_local_datetime(&datetime) { |
949 | MappedLocalTime::None => Err(IMPOSSIBLE), |
950 | MappedLocalTime::Single(t) => { |
951 | if check_offset(&t) { |
952 | Ok(t) |
953 | } else { |
954 | Err(IMPOSSIBLE) |
955 | } |
956 | } |
957 | MappedLocalTime::Ambiguous(min, max) => { |
958 | // try to disambiguate two possible local dates by offset. |
959 | match (check_offset(&min), check_offset(&max)) { |
960 | (false, false) => Err(IMPOSSIBLE), |
961 | (false, true) => Ok(max), |
962 | (true, false) => Ok(min), |
963 | (true, true) => Err(NOT_ENOUGH), |
964 | } |
965 | } |
966 | } |
967 | } |
968 | |
969 | /// Get the `year` field if set. |
970 | /// |
971 | /// See also [`set_year()`](Parsed::set_year). |
972 | #[inline ] |
973 | pub fn year(&self) -> Option<i32> { |
974 | self.year |
975 | } |
976 | |
977 | /// Get the `year_div_100` field if set. |
978 | /// |
979 | /// See also [`set_year_div_100()`](Parsed::set_year_div_100). |
980 | #[inline ] |
981 | pub fn year_div_100(&self) -> Option<i32> { |
982 | self.year_div_100 |
983 | } |
984 | |
985 | /// Get the `year_mod_100` field if set. |
986 | /// |
987 | /// See also [`set_year_mod_100()`](Parsed::set_year_mod_100). |
988 | #[inline ] |
989 | pub fn year_mod_100(&self) -> Option<i32> { |
990 | self.year_mod_100 |
991 | } |
992 | |
993 | /// Get the `isoyear` field that is part of an [ISO 8601 week date] if set. |
994 | /// |
995 | /// See also [`set_isoyear()`](Parsed::set_isoyear). |
996 | /// |
997 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
998 | #[inline ] |
999 | pub fn isoyear(&self) -> Option<i32> { |
1000 | self.isoyear |
1001 | } |
1002 | |
1003 | /// Get the `isoyear_div_100` field that is part of an [ISO 8601 week date] if set. |
1004 | /// |
1005 | /// See also [`set_isoyear_div_100()`](Parsed::set_isoyear_div_100). |
1006 | /// |
1007 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
1008 | #[inline ] |
1009 | pub fn isoyear_div_100(&self) -> Option<i32> { |
1010 | self.isoyear_div_100 |
1011 | } |
1012 | |
1013 | /// Get the `isoyear_mod_100` field that is part of an [ISO 8601 week date] if set. |
1014 | /// |
1015 | /// See also [`set_isoyear_mod_100()`](Parsed::set_isoyear_mod_100). |
1016 | /// |
1017 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
1018 | #[inline ] |
1019 | pub fn isoyear_mod_100(&self) -> Option<i32> { |
1020 | self.isoyear_mod_100 |
1021 | } |
1022 | |
1023 | /// Get the `month` field if set. |
1024 | /// |
1025 | /// See also [`set_month()`](Parsed::set_month). |
1026 | #[inline ] |
1027 | pub fn month(&self) -> Option<u32> { |
1028 | self.month |
1029 | } |
1030 | |
1031 | /// Get the `week_from_sun` field if set. |
1032 | /// |
1033 | /// See also [`set_week_from_sun()`](Parsed::set_week_from_sun). |
1034 | #[inline ] |
1035 | pub fn week_from_sun(&self) -> Option<u32> { |
1036 | self.week_from_sun |
1037 | } |
1038 | |
1039 | /// Get the `week_from_mon` field if set. |
1040 | /// |
1041 | /// See also [`set_week_from_mon()`](Parsed::set_week_from_mon). |
1042 | #[inline ] |
1043 | pub fn week_from_mon(&self) -> Option<u32> { |
1044 | self.week_from_mon |
1045 | } |
1046 | |
1047 | /// Get the `isoweek` field that is part of an [ISO 8601 week date] if set. |
1048 | /// |
1049 | /// See also [`set_isoweek()`](Parsed::set_isoweek). |
1050 | /// |
1051 | /// [ISO 8601 week date]: crate::NaiveDate#week-date |
1052 | #[inline ] |
1053 | pub fn isoweek(&self) -> Option<u32> { |
1054 | self.isoweek |
1055 | } |
1056 | |
1057 | /// Get the `weekday` field if set. |
1058 | /// |
1059 | /// See also [`set_weekday()`](Parsed::set_weekday). |
1060 | #[inline ] |
1061 | pub fn weekday(&self) -> Option<Weekday> { |
1062 | self.weekday |
1063 | } |
1064 | |
1065 | /// Get the `ordinal` (day of the year) field if set. |
1066 | /// |
1067 | /// See also [`set_ordinal()`](Parsed::set_ordinal). |
1068 | #[inline ] |
1069 | pub fn ordinal(&self) -> Option<u32> { |
1070 | self.ordinal |
1071 | } |
1072 | |
1073 | /// Get the `day` of the month field if set. |
1074 | /// |
1075 | /// See also [`set_day()`](Parsed::set_day). |
1076 | #[inline ] |
1077 | pub fn day(&self) -> Option<u32> { |
1078 | self.day |
1079 | } |
1080 | |
1081 | /// Get the `hour_div_12` field (am/pm) if set. |
1082 | /// |
1083 | /// 0 indicates AM and 1 indicates PM. |
1084 | /// |
1085 | /// See also [`set_ampm()`](Parsed::set_ampm) and [`set_hour()`](Parsed::set_hour). |
1086 | #[inline ] |
1087 | pub fn hour_div_12(&self) -> Option<u32> { |
1088 | self.hour_div_12 |
1089 | } |
1090 | |
1091 | /// Get the `hour_mod_12` field if set. |
1092 | /// |
1093 | /// See also [`set_hour12()`](Parsed::set_hour12) and [`set_hour()`](Parsed::set_hour). |
1094 | pub fn hour_mod_12(&self) -> Option<u32> { |
1095 | self.hour_mod_12 |
1096 | } |
1097 | |
1098 | /// Get the `minute` field if set. |
1099 | /// |
1100 | /// See also [`set_minute()`](Parsed::set_minute). |
1101 | #[inline ] |
1102 | pub fn minute(&self) -> Option<u32> { |
1103 | self.minute |
1104 | } |
1105 | |
1106 | /// Get the `second` field if set. |
1107 | /// |
1108 | /// See also [`set_second()`](Parsed::set_second). |
1109 | #[inline ] |
1110 | pub fn second(&self) -> Option<u32> { |
1111 | self.second |
1112 | } |
1113 | |
1114 | /// Get the `nanosecond` field if set. |
1115 | /// |
1116 | /// See also [`set_nanosecond()`](Parsed::set_nanosecond). |
1117 | #[inline ] |
1118 | pub fn nanosecond(&self) -> Option<u32> { |
1119 | self.nanosecond |
1120 | } |
1121 | |
1122 | /// Get the `timestamp` field if set. |
1123 | /// |
1124 | /// See also [`set_timestamp()`](Parsed::set_timestamp). |
1125 | #[inline ] |
1126 | pub fn timestamp(&self) -> Option<i64> { |
1127 | self.timestamp |
1128 | } |
1129 | |
1130 | /// Get the `offset` field if set. |
1131 | /// |
1132 | /// See also [`set_offset()`](Parsed::set_offset). |
1133 | #[inline ] |
1134 | pub fn offset(&self) -> Option<i32> { |
1135 | self.offset |
1136 | } |
1137 | } |
1138 | |
1139 | /// Create a `NaiveDate` when given a year, week, weekday, and the definition at which day of the |
1140 | /// week a week starts. |
1141 | /// |
1142 | /// Returns `IMPOSSIBLE` if `week` is `0` or `53` and the `weekday` falls outside the year. |
1143 | fn resolve_week_date( |
1144 | year: i32, |
1145 | week: u32, |
1146 | weekday: Weekday, |
1147 | week_start_day: Weekday, |
1148 | ) -> ParseResult<NaiveDate> { |
1149 | if week > 53 { |
1150 | return Err(OUT_OF_RANGE); |
1151 | } |
1152 | |
1153 | let first_day_of_year: NaiveDate = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?; |
1154 | // Ordinal of the day at which week 1 starts. |
1155 | let first_week_start: i32 = 1 + week_start_day.days_since(first_day_of_year.weekday()) as i32; |
1156 | // Number of the `weekday`, which is 0 for the first day of the week. |
1157 | let weekday: i32 = weekday.days_since(week_start_day) as i32; |
1158 | let ordinal: i32 = first_week_start + (week as i32 - 1) * 7 + weekday; |
1159 | if ordinal <= 0 { |
1160 | return Err(IMPOSSIBLE); |
1161 | } |
1162 | first_day_of_year.with_ordinal(ordinal as u32).ok_or(IMPOSSIBLE) |
1163 | } |
1164 | |
1165 | #[cfg (test)] |
1166 | mod tests { |
1167 | use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; |
1168 | use super::Parsed; |
1169 | use crate::naive::{NaiveDate, NaiveTime}; |
1170 | use crate::offset::{FixedOffset, TimeZone, Utc}; |
1171 | use crate::Datelike; |
1172 | use crate::Weekday::*; |
1173 | |
1174 | #[test ] |
1175 | fn test_parsed_set_fields() { |
1176 | // year*, isoyear* |
1177 | let mut p = Parsed::new(); |
1178 | assert_eq!(p.set_year(1987), Ok(())); |
1179 | assert_eq!(p.set_year(1986), Err(IMPOSSIBLE)); |
1180 | assert_eq!(p.set_year(1988), Err(IMPOSSIBLE)); |
1181 | assert_eq!(p.set_year(1987), Ok(())); |
1182 | assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year` |
1183 | assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE)); |
1184 | assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE)); |
1185 | assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto |
1186 | assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE)); |
1187 | assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE)); |
1188 | |
1189 | let mut p = Parsed::new(); |
1190 | assert_eq!(p.set_year(0), Ok(())); |
1191 | assert_eq!(p.set_year_div_100(0), Ok(())); |
1192 | assert_eq!(p.set_year_mod_100(0), Ok(())); |
1193 | |
1194 | let mut p = Parsed::new(); |
1195 | assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE)); |
1196 | assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE)); |
1197 | assert_eq!(p.set_year(-1), Ok(())); |
1198 | assert_eq!(p.set_year(-2), Err(IMPOSSIBLE)); |
1199 | assert_eq!(p.set_year(0), Err(IMPOSSIBLE)); |
1200 | |
1201 | let mut p = Parsed::new(); |
1202 | assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); |
1203 | assert_eq!(p.set_year_div_100(8), Ok(())); |
1204 | assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); |
1205 | |
1206 | // month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset |
1207 | let mut p = Parsed::new(); |
1208 | assert_eq!(p.set_month(7), Ok(())); |
1209 | assert_eq!(p.set_month(1), Err(IMPOSSIBLE)); |
1210 | assert_eq!(p.set_month(6), Err(IMPOSSIBLE)); |
1211 | assert_eq!(p.set_month(8), Err(IMPOSSIBLE)); |
1212 | assert_eq!(p.set_month(12), Err(IMPOSSIBLE)); |
1213 | |
1214 | let mut p = Parsed::new(); |
1215 | assert_eq!(p.set_month(8), Ok(())); |
1216 | assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE)); |
1217 | |
1218 | // hour |
1219 | let mut p = Parsed::new(); |
1220 | assert_eq!(p.set_hour(12), Ok(())); |
1221 | assert_eq!(p.set_hour(11), Err(IMPOSSIBLE)); |
1222 | assert_eq!(p.set_hour(13), Err(IMPOSSIBLE)); |
1223 | assert_eq!(p.set_hour(12), Ok(())); |
1224 | assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE)); |
1225 | assert_eq!(p.set_ampm(true), Ok(())); |
1226 | assert_eq!(p.set_hour12(12), Ok(())); |
1227 | assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation |
1228 | assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE)); |
1229 | assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE)); |
1230 | |
1231 | let mut p = Parsed::new(); |
1232 | assert_eq!(p.set_ampm(true), Ok(())); |
1233 | assert_eq!(p.set_hour12(7), Ok(())); |
1234 | assert_eq!(p.set_hour(7), Err(IMPOSSIBLE)); |
1235 | assert_eq!(p.set_hour(18), Err(IMPOSSIBLE)); |
1236 | assert_eq!(p.set_hour(19), Ok(())); |
1237 | |
1238 | // timestamp |
1239 | let mut p = Parsed::new(); |
1240 | assert_eq!(p.set_timestamp(1_234_567_890), Ok(())); |
1241 | assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE)); |
1242 | assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE)); |
1243 | } |
1244 | |
1245 | #[test ] |
1246 | fn test_parsed_set_range() { |
1247 | assert_eq!(Parsed::new().set_year(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); |
1248 | assert!(Parsed::new().set_year(i32::MIN as i64).is_ok()); |
1249 | assert!(Parsed::new().set_year(i32::MAX as i64).is_ok()); |
1250 | assert_eq!(Parsed::new().set_year(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
1251 | |
1252 | assert_eq!(Parsed::new().set_year_div_100(-1), Err(OUT_OF_RANGE)); |
1253 | assert!(Parsed::new().set_year_div_100(0).is_ok()); |
1254 | assert!(Parsed::new().set_year_div_100(i32::MAX as i64).is_ok()); |
1255 | assert_eq!(Parsed::new().set_year_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
1256 | |
1257 | assert_eq!(Parsed::new().set_year_mod_100(-1), Err(OUT_OF_RANGE)); |
1258 | assert!(Parsed::new().set_year_mod_100(0).is_ok()); |
1259 | assert!(Parsed::new().set_year_mod_100(99).is_ok()); |
1260 | assert_eq!(Parsed::new().set_year_mod_100(100), Err(OUT_OF_RANGE)); |
1261 | |
1262 | assert_eq!(Parsed::new().set_isoyear(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); |
1263 | assert!(Parsed::new().set_isoyear(i32::MIN as i64).is_ok()); |
1264 | assert!(Parsed::new().set_isoyear(i32::MAX as i64).is_ok()); |
1265 | assert_eq!(Parsed::new().set_isoyear(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
1266 | |
1267 | assert_eq!(Parsed::new().set_isoyear_div_100(-1), Err(OUT_OF_RANGE)); |
1268 | assert!(Parsed::new().set_isoyear_div_100(0).is_ok()); |
1269 | assert!(Parsed::new().set_isoyear_div_100(99).is_ok()); |
1270 | assert_eq!(Parsed::new().set_isoyear_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
1271 | |
1272 | assert_eq!(Parsed::new().set_isoyear_mod_100(-1), Err(OUT_OF_RANGE)); |
1273 | assert!(Parsed::new().set_isoyear_mod_100(0).is_ok()); |
1274 | assert!(Parsed::new().set_isoyear_mod_100(99).is_ok()); |
1275 | assert_eq!(Parsed::new().set_isoyear_mod_100(100), Err(OUT_OF_RANGE)); |
1276 | |
1277 | assert_eq!(Parsed::new().set_month(0), Err(OUT_OF_RANGE)); |
1278 | assert!(Parsed::new().set_month(1).is_ok()); |
1279 | assert!(Parsed::new().set_month(12).is_ok()); |
1280 | assert_eq!(Parsed::new().set_month(13), Err(OUT_OF_RANGE)); |
1281 | |
1282 | assert_eq!(Parsed::new().set_week_from_sun(-1), Err(OUT_OF_RANGE)); |
1283 | assert!(Parsed::new().set_week_from_sun(0).is_ok()); |
1284 | assert!(Parsed::new().set_week_from_sun(53).is_ok()); |
1285 | assert_eq!(Parsed::new().set_week_from_sun(54), Err(OUT_OF_RANGE)); |
1286 | |
1287 | assert_eq!(Parsed::new().set_week_from_mon(-1), Err(OUT_OF_RANGE)); |
1288 | assert!(Parsed::new().set_week_from_mon(0).is_ok()); |
1289 | assert!(Parsed::new().set_week_from_mon(53).is_ok()); |
1290 | assert_eq!(Parsed::new().set_week_from_mon(54), Err(OUT_OF_RANGE)); |
1291 | |
1292 | assert_eq!(Parsed::new().set_isoweek(0), Err(OUT_OF_RANGE)); |
1293 | assert!(Parsed::new().set_isoweek(1).is_ok()); |
1294 | assert!(Parsed::new().set_isoweek(53).is_ok()); |
1295 | assert_eq!(Parsed::new().set_isoweek(54), Err(OUT_OF_RANGE)); |
1296 | |
1297 | assert_eq!(Parsed::new().set_ordinal(0), Err(OUT_OF_RANGE)); |
1298 | assert!(Parsed::new().set_ordinal(1).is_ok()); |
1299 | assert!(Parsed::new().set_ordinal(366).is_ok()); |
1300 | assert_eq!(Parsed::new().set_ordinal(367), Err(OUT_OF_RANGE)); |
1301 | |
1302 | assert_eq!(Parsed::new().set_day(0), Err(OUT_OF_RANGE)); |
1303 | assert!(Parsed::new().set_day(1).is_ok()); |
1304 | assert!(Parsed::new().set_day(31).is_ok()); |
1305 | assert_eq!(Parsed::new().set_day(32), Err(OUT_OF_RANGE)); |
1306 | |
1307 | assert_eq!(Parsed::new().set_hour12(0), Err(OUT_OF_RANGE)); |
1308 | assert!(Parsed::new().set_hour12(1).is_ok()); |
1309 | assert!(Parsed::new().set_hour12(12).is_ok()); |
1310 | assert_eq!(Parsed::new().set_hour12(13), Err(OUT_OF_RANGE)); |
1311 | |
1312 | assert_eq!(Parsed::new().set_hour(-1), Err(OUT_OF_RANGE)); |
1313 | assert!(Parsed::new().set_hour(0).is_ok()); |
1314 | assert!(Parsed::new().set_hour(23).is_ok()); |
1315 | assert_eq!(Parsed::new().set_hour(24), Err(OUT_OF_RANGE)); |
1316 | |
1317 | assert_eq!(Parsed::new().set_minute(-1), Err(OUT_OF_RANGE)); |
1318 | assert!(Parsed::new().set_minute(0).is_ok()); |
1319 | assert!(Parsed::new().set_minute(59).is_ok()); |
1320 | assert_eq!(Parsed::new().set_minute(60), Err(OUT_OF_RANGE)); |
1321 | |
1322 | assert_eq!(Parsed::new().set_second(-1), Err(OUT_OF_RANGE)); |
1323 | assert!(Parsed::new().set_second(0).is_ok()); |
1324 | assert!(Parsed::new().set_second(60).is_ok()); |
1325 | assert_eq!(Parsed::new().set_second(61), Err(OUT_OF_RANGE)); |
1326 | |
1327 | assert_eq!(Parsed::new().set_nanosecond(-1), Err(OUT_OF_RANGE)); |
1328 | assert!(Parsed::new().set_nanosecond(0).is_ok()); |
1329 | assert!(Parsed::new().set_nanosecond(999_999_999).is_ok()); |
1330 | assert_eq!(Parsed::new().set_nanosecond(1_000_000_000), Err(OUT_OF_RANGE)); |
1331 | |
1332 | assert!(Parsed::new().set_timestamp(i64::MIN).is_ok()); |
1333 | assert!(Parsed::new().set_timestamp(i64::MAX).is_ok()); |
1334 | |
1335 | assert_eq!(Parsed::new().set_offset(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); |
1336 | assert!(Parsed::new().set_offset(i32::MIN as i64).is_ok()); |
1337 | assert!(Parsed::new().set_offset(i32::MAX as i64).is_ok()); |
1338 | assert_eq!(Parsed::new().set_offset(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); |
1339 | } |
1340 | |
1341 | #[test ] |
1342 | fn test_parsed_to_naive_date() { |
1343 | macro_rules! parse { |
1344 | ($($k:ident: $v:expr),*) => ( |
1345 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date() |
1346 | ) |
1347 | } |
1348 | |
1349 | let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap()); |
1350 | |
1351 | // ymd: omission of fields |
1352 | assert_eq!(parse!(), Err(NOT_ENOUGH)); |
1353 | assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH)); |
1354 | assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH)); |
1355 | assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2)); |
1356 | assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH)); |
1357 | assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH)); |
1358 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH)); |
1359 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH)); |
1360 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2)); |
1361 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH)); |
1362 | assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH)); |
1363 | assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2)); |
1364 | assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2)); |
1365 | |
1366 | // ymd: out-of-range conditions |
1367 | assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29)); |
1368 | assert_eq!( |
1369 | parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29), |
1370 | Err(OUT_OF_RANGE) |
1371 | ); |
1372 | assert_eq!( |
1373 | parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1), |
1374 | Err(OUT_OF_RANGE) |
1375 | ); |
1376 | assert_eq!( |
1377 | parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31), |
1378 | ymd(1983, 12, 31) |
1379 | ); |
1380 | assert_eq!( |
1381 | parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32), |
1382 | Err(OUT_OF_RANGE) |
1383 | ); |
1384 | assert_eq!( |
1385 | parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0), |
1386 | Err(OUT_OF_RANGE) |
1387 | ); |
1388 | assert_eq!( |
1389 | parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1), |
1390 | Err(OUT_OF_RANGE) |
1391 | ); |
1392 | assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE)); |
1393 | assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1)); |
1394 | assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(IMPOSSIBLE)); |
1395 | let max_year = NaiveDate::MAX.year(); |
1396 | assert_eq!( |
1397 | parse!(year_div_100: max_year / 100, |
1398 | year_mod_100: max_year % 100, month: 1, day: 1), |
1399 | ymd(max_year, 1, 1) |
1400 | ); |
1401 | assert_eq!( |
1402 | parse!(year_div_100: (max_year + 1) / 100, |
1403 | year_mod_100: (max_year + 1) % 100, month: 1, day: 1), |
1404 | Err(OUT_OF_RANGE) |
1405 | ); |
1406 | |
1407 | // ymd: conflicting inputs |
1408 | assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1)); |
1409 | assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE)); |
1410 | assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1)); |
1411 | assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE)); |
1412 | assert_eq!( |
1413 | parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1), |
1414 | ymd(1984, 1, 1) |
1415 | ); |
1416 | assert_eq!( |
1417 | parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1), |
1418 | Err(IMPOSSIBLE) |
1419 | ); |
1420 | assert_eq!( |
1421 | parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1), |
1422 | Err(OUT_OF_RANGE) |
1423 | ); |
1424 | assert_eq!( |
1425 | parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1), |
1426 | Err(OUT_OF_RANGE) |
1427 | ); |
1428 | assert_eq!( |
1429 | parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1), |
1430 | Err(IMPOSSIBLE) |
1431 | ); |
1432 | assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(IMPOSSIBLE)); |
1433 | assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(IMPOSSIBLE)); |
1434 | |
1435 | // weekdates |
1436 | assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH)); |
1437 | assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH)); |
1438 | assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH)); |
1439 | assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(IMPOSSIBLE)); |
1440 | assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(IMPOSSIBLE)); |
1441 | assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1)); |
1442 | assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1)); |
1443 | assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2)); |
1444 | assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2)); |
1445 | assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3)); |
1446 | assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3)); |
1447 | assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8)); |
1448 | assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8)); |
1449 | assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9)); |
1450 | assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9)); |
1451 | assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10)); |
1452 | assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30)); |
1453 | assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31)); |
1454 | assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(IMPOSSIBLE)); |
1455 | assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE)); |
1456 | assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(IMPOSSIBLE)); |
1457 | assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1)); |
1458 | |
1459 | // weekdates: conflicting inputs |
1460 | assert_eq!( |
1461 | parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat), |
1462 | ymd(2000, 1, 8) |
1463 | ); |
1464 | assert_eq!( |
1465 | parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun), |
1466 | ymd(2000, 1, 9) |
1467 | ); |
1468 | assert_eq!( |
1469 | parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun), |
1470 | Err(IMPOSSIBLE) |
1471 | ); |
1472 | assert_eq!( |
1473 | parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun), |
1474 | Err(IMPOSSIBLE) |
1475 | ); |
1476 | |
1477 | // ISO weekdates |
1478 | assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH)); |
1479 | assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31)); |
1480 | assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1)); |
1481 | assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE)); |
1482 | assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE)); |
1483 | assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3)); |
1484 | assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH)); |
1485 | |
1486 | // year and ordinal |
1487 | assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH)); |
1488 | assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE)); |
1489 | assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1)); |
1490 | assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29)); |
1491 | assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1)); |
1492 | assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31)); |
1493 | assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE)); |
1494 | assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); |
1495 | assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE)); |
1496 | assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1)); |
1497 | assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28)); |
1498 | assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1)); |
1499 | assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31)); |
1500 | assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE)); |
1501 | assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); |
1502 | |
1503 | // more complex cases |
1504 | assert_eq!( |
1505 | parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1, |
1506 | week_from_sun: 52, week_from_mon: 52, weekday: Wed), |
1507 | ymd(2014, 12, 31) |
1508 | ); |
1509 | assert_eq!( |
1510 | parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1, |
1511 | week_from_sun: 52, week_from_mon: 52), |
1512 | ymd(2014, 12, 31) |
1513 | ); |
1514 | assert_eq!( |
1515 | parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53, |
1516 | week_from_sun: 52, week_from_mon: 52, weekday: Wed), |
1517 | Err(IMPOSSIBLE) |
1518 | ); // no ISO week date 2014-W53-3 |
1519 | assert_eq!( |
1520 | parse!(year: 2012, isoyear: 2015, isoweek: 1, |
1521 | week_from_sun: 52, week_from_mon: 52), |
1522 | Err(NOT_ENOUGH) |
1523 | ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31) |
1524 | assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH)); |
1525 | // technically unique (2014-12-31) but Chrono gives up |
1526 | } |
1527 | |
1528 | #[test ] |
1529 | fn test_parsed_to_naive_time() { |
1530 | macro_rules! parse { |
1531 | ($($k:ident: $v:expr),*) => ( |
1532 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time() |
1533 | ) |
1534 | } |
1535 | |
1536 | let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap()); |
1537 | let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap()); |
1538 | |
1539 | // omission of fields |
1540 | assert_eq!(parse!(), Err(NOT_ENOUGH)); |
1541 | assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH)); |
1542 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH)); |
1543 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0)); |
1544 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45)); |
1545 | assert_eq!( |
1546 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45, |
1547 | nanosecond: 678_901_234), |
1548 | hmsn(1, 23, 45, 678_901_234) |
1549 | ); |
1550 | assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6)); |
1551 | assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH)); |
1552 | assert_eq!( |
1553 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012), |
1554 | Err(NOT_ENOUGH) |
1555 | ); |
1556 | |
1557 | // out-of-range conditions |
1558 | assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE)); |
1559 | assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE)); |
1560 | assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE)); |
1561 | assert_eq!( |
1562 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61), |
1563 | Err(OUT_OF_RANGE) |
1564 | ); |
1565 | assert_eq!( |
1566 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34, |
1567 | nanosecond: 1_000_000_000), |
1568 | Err(OUT_OF_RANGE) |
1569 | ); |
1570 | |
1571 | // leap seconds |
1572 | assert_eq!( |
1573 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60), |
1574 | hmsn(1, 23, 59, 1_000_000_000) |
1575 | ); |
1576 | assert_eq!( |
1577 | parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60, |
1578 | nanosecond: 999_999_999), |
1579 | hmsn(1, 23, 59, 1_999_999_999) |
1580 | ); |
1581 | } |
1582 | |
1583 | #[test ] |
1584 | fn test_parsed_to_naive_datetime_with_offset() { |
1585 | macro_rules! parse { |
1586 | (offset = $offset:expr; $($k:ident: $v:expr),*) => ( |
1587 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset) |
1588 | ); |
1589 | ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*)) |
1590 | } |
1591 | |
1592 | let ymdhms = |y, m, d, h, n, s| { |
1593 | Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap()) |
1594 | }; |
1595 | let ymdhmsn = |y, m, d, h, n, s, nano| { |
1596 | Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap()) |
1597 | }; |
1598 | |
1599 | // omission of fields |
1600 | assert_eq!(parse!(), Err(NOT_ENOUGH)); |
1601 | assert_eq!( |
1602 | parse!(year: 2015, month: 1, day: 30, |
1603 | hour_div_12: 1, hour_mod_12: 2, minute: 38), |
1604 | ymdhms(2015, 1, 30, 14, 38, 0) |
1605 | ); |
1606 | assert_eq!( |
1607 | parse!(year: 1997, month: 1, day: 30, |
1608 | hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5), |
1609 | ymdhms(1997, 1, 30, 14, 38, 5) |
1610 | ); |
1611 | assert_eq!( |
1612 | parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5, |
1613 | minute: 6, second: 7, nanosecond: 890_123_456), |
1614 | ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456) |
1615 | ); |
1616 | assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0)); |
1617 | assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1)); |
1618 | assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1)); |
1619 | assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40)); |
1620 | assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44)); |
1621 | |
1622 | // full fields |
1623 | assert_eq!( |
1624 | parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, |
1625 | ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, |
1626 | isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, |
1627 | hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, |
1628 | nanosecond: 12_345_678, timestamp: 1_420_000_000), |
1629 | ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) |
1630 | ); |
1631 | assert_eq!( |
1632 | parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, |
1633 | ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, |
1634 | isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, |
1635 | hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, |
1636 | nanosecond: 12_345_678, timestamp: 1_419_999_999), |
1637 | Err(IMPOSSIBLE) |
1638 | ); |
1639 | assert_eq!( |
1640 | parse!(offset = 32400; |
1641 | year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, |
1642 | ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, |
1643 | isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, |
1644 | hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, |
1645 | nanosecond: 12_345_678, timestamp: 1_419_967_600), |
1646 | ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) |
1647 | ); |
1648 | |
1649 | // more timestamps |
1650 | let max_days_from_year_1970 = |
1651 | NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); |
1652 | let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1) |
1653 | .unwrap() |
1654 | .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); |
1655 | let min_days_from_year_1970 = |
1656 | NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); |
1657 | assert_eq!( |
1658 | parse!(timestamp: min_days_from_year_1970.num_seconds()), |
1659 | ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0) |
1660 | ); |
1661 | assert_eq!( |
1662 | parse!(timestamp: year_0_from_year_1970.num_seconds()), |
1663 | ymdhms(0, 1, 1, 0, 0, 0) |
1664 | ); |
1665 | assert_eq!( |
1666 | parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399), |
1667 | ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59) |
1668 | ); |
1669 | |
1670 | // leap seconds #1: partial fields |
1671 | assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE)); |
1672 | assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59)); |
1673 | assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); |
1674 | assert_eq!( |
1675 | parse!(second: 60, timestamp: 1_341_100_799), |
1676 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
1677 | ); |
1678 | assert_eq!( |
1679 | parse!(second: 60, timestamp: 1_341_100_800), |
1680 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
1681 | ); |
1682 | assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0)); |
1683 | assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); |
1684 | assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE)); |
1685 | |
1686 | // leap seconds #2: full fields |
1687 | // we need to have separate tests for them since it uses another control flow. |
1688 | assert_eq!( |
1689 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
1690 | minute: 59, second: 59, timestamp: 1_341_100_798), |
1691 | Err(IMPOSSIBLE) |
1692 | ); |
1693 | assert_eq!( |
1694 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
1695 | minute: 59, second: 59, timestamp: 1_341_100_799), |
1696 | ymdhms(2012, 6, 30, 23, 59, 59) |
1697 | ); |
1698 | assert_eq!( |
1699 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
1700 | minute: 59, second: 59, timestamp: 1_341_100_800), |
1701 | Err(IMPOSSIBLE) |
1702 | ); |
1703 | assert_eq!( |
1704 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
1705 | minute: 59, second: 60, timestamp: 1_341_100_799), |
1706 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
1707 | ); |
1708 | assert_eq!( |
1709 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
1710 | minute: 59, second: 60, timestamp: 1_341_100_800), |
1711 | ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) |
1712 | ); |
1713 | assert_eq!( |
1714 | parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, |
1715 | minute: 0, second: 0, timestamp: 1_341_100_800), |
1716 | ymdhms(2012, 7, 1, 0, 0, 0) |
1717 | ); |
1718 | assert_eq!( |
1719 | parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, |
1720 | minute: 0, second: 1, timestamp: 1_341_100_800), |
1721 | Err(IMPOSSIBLE) |
1722 | ); |
1723 | assert_eq!( |
1724 | parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, |
1725 | minute: 59, second: 60, timestamp: 1_341_100_801), |
1726 | Err(IMPOSSIBLE) |
1727 | ); |
1728 | |
1729 | // error codes |
1730 | assert_eq!( |
1731 | parse!(year: 2015, month: 1, day: 20, weekday: Tue, |
1732 | hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20), |
1733 | Err(OUT_OF_RANGE) |
1734 | ); // `hour_div_12` is out of range |
1735 | } |
1736 | |
1737 | #[test ] |
1738 | fn test_parsed_to_datetime() { |
1739 | macro_rules! parse { |
1740 | ($($k:ident: $v:expr),*) => ( |
1741 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime() |
1742 | ) |
1743 | } |
1744 | |
1745 | let ymdhmsn = |y, m, d, h, n, s, nano, off| { |
1746 | Ok(FixedOffset::east_opt(off) |
1747 | .unwrap() |
1748 | .from_local_datetime( |
1749 | &NaiveDate::from_ymd_opt(y, m, d) |
1750 | .unwrap() |
1751 | .and_hms_nano_opt(h, n, s, nano) |
1752 | .unwrap(), |
1753 | ) |
1754 | .unwrap()) |
1755 | }; |
1756 | |
1757 | assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH)); |
1758 | assert_eq!( |
1759 | parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
1760 | minute: 26, second: 40, nanosecond: 12_345_678), |
1761 | Err(NOT_ENOUGH) |
1762 | ); |
1763 | assert_eq!( |
1764 | parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
1765 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), |
1766 | ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0) |
1767 | ); |
1768 | assert_eq!( |
1769 | parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, |
1770 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), |
1771 | ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400) |
1772 | ); |
1773 | assert_eq!( |
1774 | parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1, |
1775 | minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876), |
1776 | ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876) |
1777 | ); |
1778 | assert_eq!( |
1779 | parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4, |
1780 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400), |
1781 | Err(OUT_OF_RANGE) |
1782 | ); // `FixedOffset` does not support such huge offset |
1783 | } |
1784 | |
1785 | #[test ] |
1786 | fn test_parsed_to_datetime_with_timezone() { |
1787 | macro_rules! parse { |
1788 | ($tz:expr; $($k:ident: $v:expr),*) => ( |
1789 | Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz) |
1790 | ) |
1791 | } |
1792 | |
1793 | // single result from ymdhms |
1794 | assert_eq!( |
1795 | parse!(Utc; |
1796 | year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
1797 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), |
1798 | Ok(Utc |
1799 | .from_local_datetime( |
1800 | &NaiveDate::from_ymd_opt(2014, 12, 31) |
1801 | .unwrap() |
1802 | .and_hms_nano_opt(4, 26, 40, 12_345_678) |
1803 | .unwrap() |
1804 | ) |
1805 | .unwrap()) |
1806 | ); |
1807 | assert_eq!( |
1808 | parse!(Utc; |
1809 | year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, |
1810 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), |
1811 | Err(IMPOSSIBLE) |
1812 | ); |
1813 | assert_eq!( |
1814 | parse!(FixedOffset::east_opt(32400).unwrap(); |
1815 | year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, |
1816 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), |
1817 | Err(IMPOSSIBLE) |
1818 | ); |
1819 | assert_eq!( |
1820 | parse!(FixedOffset::east_opt(32400).unwrap(); |
1821 | year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, |
1822 | minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), |
1823 | Ok(FixedOffset::east_opt(32400) |
1824 | .unwrap() |
1825 | .from_local_datetime( |
1826 | &NaiveDate::from_ymd_opt(2014, 12, 31) |
1827 | .unwrap() |
1828 | .and_hms_nano_opt(13, 26, 40, 12_345_678) |
1829 | .unwrap() |
1830 | ) |
1831 | .unwrap()) |
1832 | ); |
1833 | |
1834 | // single result from timestamp |
1835 | assert_eq!( |
1836 | parse!(Utc; timestamp: 1_420_000_000, offset: 0), |
1837 | Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap()) |
1838 | ); |
1839 | assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE)); |
1840 | assert_eq!( |
1841 | parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0), |
1842 | Err(IMPOSSIBLE) |
1843 | ); |
1844 | assert_eq!( |
1845 | parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400), |
1846 | Ok(FixedOffset::east_opt(32400) |
1847 | .unwrap() |
1848 | .with_ymd_and_hms(2014, 12, 31, 13, 26, 40) |
1849 | .unwrap()) |
1850 | ); |
1851 | |
1852 | // TODO test with a variable time zone (for None and Ambiguous cases) |
1853 | } |
1854 | |
1855 | #[test ] |
1856 | fn issue_551() { |
1857 | use crate::Weekday; |
1858 | let mut parsed = Parsed::new(); |
1859 | |
1860 | parsed.year = Some(2002); |
1861 | parsed.week_from_mon = Some(22); |
1862 | parsed.weekday = Some(Weekday::Mon); |
1863 | assert_eq!(NaiveDate::from_ymd_opt(2002, 6, 3).unwrap(), parsed.to_naive_date().unwrap()); |
1864 | |
1865 | parsed.year = Some(2001); |
1866 | assert_eq!(NaiveDate::from_ymd_opt(2001, 5, 28).unwrap(), parsed.to_naive_date().unwrap()); |
1867 | } |
1868 | } |
1869 | |