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