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