1use core::num::{NonZeroU16, NonZeroU8};
4
5use deranged::{
6 OptionRangedI128, OptionRangedI16, OptionRangedI32, OptionRangedI8, OptionRangedU16,
7 OptionRangedU32, OptionRangedU8, RangedI128, RangedI16, RangedI32, RangedI8, RangedU16,
8 RangedU32, RangedU8,
9};
10use num_conv::prelude::*;
11
12use crate::convert::{Day, Hour, Minute, Nanosecond, Second};
13use crate::date::{MAX_YEAR, MIN_YEAR};
14use crate::error::TryFromParsed::InsufficientInformation;
15#[cfg(feature = "alloc")]
16use crate::format_description::OwnedFormatItem;
17use crate::format_description::{modifier, BorrowedFormatItem, Component};
18use crate::internal_macros::{bug, const_try_opt};
19use crate::parsing::component::{
20 parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
21 parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second,
22 parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, Period,
23};
24use crate::parsing::ParsedItem;
25use crate::{
26 error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
27};
28
29mod sealed {
31 use super::*;
32
33 pub trait AnyFormatItem {
35 fn parse_item<'a>(
37 &self,
38 parsed: &mut Parsed,
39 input: &'a [u8],
40 ) -> Result<&'a [u8], error::ParseFromDescription>;
41 }
42}
43
44impl sealed::AnyFormatItem for BorrowedFormatItem<'_> {
45 fn parse_item<'a>(
46 &self,
47 parsed: &mut Parsed,
48 input: &'a [u8],
49 ) -> Result<&'a [u8], error::ParseFromDescription> {
50 match self {
51 Self::Literal(literal) => Parsed::parse_literal(input, literal),
52 Self::Component(component) => parsed.parse_component(input, *component),
53 Self::Compound(compound) => parsed.parse_items(input, compound),
54 Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
55 Self::First(items) => {
56 let mut first_err = None;
57
58 for item in items.iter() {
59 match parsed.parse_item(input, item) {
60 Ok(remaining_input) => return Ok(remaining_input),
61 Err(err) if first_err.is_none() => first_err = Some(err),
62 Err(_) => {}
63 }
64 }
65
66 match first_err {
67 Some(err) => Err(err),
68 None => Ok(input),
71 }
72 }
73 }
74 }
75}
76
77#[cfg(feature = "alloc")]
78impl sealed::AnyFormatItem for OwnedFormatItem {
79 fn parse_item<'a>(
80 &self,
81 parsed: &mut Parsed,
82 input: &'a [u8],
83 ) -> Result<&'a [u8], error::ParseFromDescription> {
84 match self {
85 Self::Literal(literal) => Parsed::parse_literal(input, literal),
86 Self::Component(component) => parsed.parse_component(input, *component),
87 Self::Compound(compound) => parsed.parse_items(input, compound),
88 Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)),
89 Self::First(items) => {
90 let mut first_err = None;
91
92 for item in items.iter() {
93 match parsed.parse_item(input, item) {
94 Ok(remaining_input) => return Ok(remaining_input),
95 Err(err) if first_err.is_none() => first_err = Some(err),
96 Err(_) => {}
97 }
98 }
99
100 match first_err {
101 Some(err) => Err(err),
102 None => Ok(input),
105 }
106 }
107 }
108 }
109}
110
111#[derive(Debug, Clone, Copy)]
118pub struct Parsed {
119 year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
121 year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
123 year_last_two: OptionRangedU8<0, 99>,
125 iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
127 iso_year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
129 iso_year_last_two: OptionRangedU8<0, 99>,
131 month: Option<Month>,
133 sunday_week_number: OptionRangedU8<0, 53>,
135 monday_week_number: OptionRangedU8<0, 53>,
137 iso_week_number: OptionRangedU8<1, 53>,
139 weekday: Option<Weekday>,
141 ordinal: OptionRangedU16<1, 366>,
143 day: OptionRangedU8<1, 31>,
145 hour_24: OptionRangedU8<0, { Hour::per(Day) - 1 }>,
147 hour_12: OptionRangedU8<1, 12>,
150 hour_12_is_pm: Option<bool>,
152 minute: OptionRangedU8<0, { Minute::per(Hour) - 1 }>,
154 second: OptionRangedU8<0, { Second::per(Minute) }>,
157 subsecond: OptionRangedU32<0, { Nanosecond::per(Second) - 1 }>,
159 offset_hour: OptionRangedI8<-23, 23>,
161 offset_minute:
163 OptionRangedI8<{ -((Minute::per(Hour) - 1) as i8) }, { (Minute::per(Hour) - 1) as _ }>,
164 offset_second:
166 OptionRangedI8<{ -((Second::per(Minute) - 1) as i8) }, { (Second::per(Minute) - 1) as _ }>,
167 unix_timestamp_nanos: OptionRangedI128<
169 {
170 OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
171 .unix_timestamp_nanos()
172 },
173 {
174 OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC)
175 .unix_timestamp_nanos()
176 },
177 >,
178 offset_is_negative: bool,
181 year_century_is_negative: bool,
184 iso_year_century_is_negative: bool,
187 pub(super) leap_second_allowed: bool,
190}
191
192impl Default for Parsed {
193 fn default() -> Self {
194 Self::new()
195 }
196}
197
198impl Parsed {
199 pub const fn new() -> Self {
201 Self {
202 year: OptionRangedI32::None,
203 year_century: OptionRangedI16::None,
204 year_last_two: OptionRangedU8::None,
205 iso_year: OptionRangedI32::None,
206 iso_year_century: OptionRangedI16::None,
207 iso_year_last_two: OptionRangedU8::None,
208 month: None,
209 sunday_week_number: OptionRangedU8::None,
210 monday_week_number: OptionRangedU8::None,
211 iso_week_number: OptionRangedU8::None,
212 weekday: None,
213 ordinal: OptionRangedU16::None,
214 day: OptionRangedU8::None,
215 hour_24: OptionRangedU8::None,
216 hour_12: OptionRangedU8::None,
217 hour_12_is_pm: None,
218 minute: OptionRangedU8::None,
219 second: OptionRangedU8::None,
220 subsecond: OptionRangedU32::None,
221 offset_hour: OptionRangedI8::None,
222 offset_minute: OptionRangedI8::None,
223 offset_second: OptionRangedI8::None,
224 unix_timestamp_nanos: OptionRangedI128::None,
225 offset_is_negative: false,
226 year_century_is_negative: false,
227 iso_year_century_is_negative: false,
228 leap_second_allowed: false,
229 }
230 }
231
232 pub fn parse_item<'a>(
238 &mut self,
239 input: &'a [u8],
240 item: &impl sealed::AnyFormatItem,
241 ) -> Result<&'a [u8], error::ParseFromDescription> {
242 item.parse_item(self, input)
243 }
244
245 pub fn parse_items<'a>(
251 &mut self,
252 mut input: &'a [u8],
253 items: &[impl sealed::AnyFormatItem],
254 ) -> Result<&'a [u8], error::ParseFromDescription> {
255 let mut this = *self;
258 for item in items {
259 input = this.parse_item(input, item)?;
260 }
261 *self = this;
262 Ok(input)
263 }
264
265 pub fn parse_literal<'a>(
267 input: &'a [u8],
268 literal: &[u8],
269 ) -> Result<&'a [u8], error::ParseFromDescription> {
270 input
271 .strip_prefix(literal)
272 .ok_or(error::ParseFromDescription::InvalidLiteral)
273 }
274
275 pub fn parse_component<'a>(
278 &mut self,
279 input: &'a [u8],
280 component: Component,
281 ) -> Result<&'a [u8], error::ParseFromDescription> {
282 use error::ParseFromDescription::InvalidComponent;
283
284 match component {
285 Component::Day(modifiers) => parse_day(input, modifiers)
286 .and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
287 .ok_or(InvalidComponent("day")),
288 Component::Month(modifiers) => parse_month(input, modifiers)
289 .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
290 .ok_or(InvalidComponent("month")),
291 Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
292 .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
293 .ok_or(InvalidComponent("ordinal")),
294 Component::Weekday(modifiers) => parse_weekday(input, modifiers)
295 .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
296 .ok_or(InvalidComponent("weekday")),
297 Component::WeekNumber(modifiers) => {
298 let ParsedItem(remaining, value) =
299 parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
300 match modifiers.repr {
301 modifier::WeekNumberRepr::Iso => {
302 NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value))
303 }
304 modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
305 modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value),
306 }
307 .ok_or(InvalidComponent("week number"))?;
308 Ok(remaining)
309 }
310 Component::Year(modifiers) => {
311 let ParsedItem(remaining, (value, is_negative)) =
312 parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
313 match (modifiers.iso_week_based, modifiers.repr) {
314 (false, modifier::YearRepr::Full) => self.set_year(value),
315 (false, modifier::YearRepr::Century) => {
316 self.set_year_century(value.truncate(), is_negative)
317 }
318 (false, modifier::YearRepr::LastTwo) => {
319 self.set_year_last_two(value.cast_unsigned().truncate())
320 }
321 (true, modifier::YearRepr::Full) => self.set_iso_year(value),
322 (true, modifier::YearRepr::Century) => {
323 self.set_iso_year_century(value.truncate(), is_negative)
324 }
325 (true, modifier::YearRepr::LastTwo) => {
326 self.set_iso_year_last_two(value.cast_unsigned().truncate())
327 }
328 }
329 .ok_or(InvalidComponent("year"))?;
330 Ok(remaining)
331 }
332 Component::Hour(modifiers) => {
333 let ParsedItem(remaining, value) =
334 parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?;
335 if modifiers.is_12_hour_clock {
336 NonZeroU8::new(value).and_then(|value| self.set_hour_12(value))
337 } else {
338 self.set_hour_24(value)
339 }
340 .ok_or(InvalidComponent("hour"))?;
341 Ok(remaining)
342 }
343 Component::Minute(modifiers) => parse_minute(input, modifiers)
344 .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
345 .ok_or(InvalidComponent("minute")),
346 Component::Period(modifiers) => parse_period(input, modifiers)
347 .and_then(|parsed| {
348 parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
349 })
350 .ok_or(InvalidComponent("period")),
351 Component::Second(modifiers) => parse_second(input, modifiers)
352 .and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
353 .ok_or(InvalidComponent("second")),
354 Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
355 .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
356 .ok_or(InvalidComponent("subsecond")),
357 Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
358 .and_then(|parsed| {
359 parsed.consume_value(|(value, is_negative)| {
360 self.set_offset_hour(value)?;
361 self.offset_is_negative = is_negative;
362 Some(())
363 })
364 })
365 .ok_or(InvalidComponent("offset hour")),
366 Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
367 .and_then(|parsed| {
368 parsed.consume_value(|value| self.set_offset_minute_signed(value))
369 })
370 .ok_or(InvalidComponent("offset minute")),
371 Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
372 .and_then(|parsed| {
373 parsed.consume_value(|value| self.set_offset_second_signed(value))
374 })
375 .ok_or(InvalidComponent("offset second")),
376 Component::Ignore(modifiers) => parse_ignore(input, modifiers)
377 .map(ParsedItem::<()>::into_inner)
378 .ok_or(InvalidComponent("ignore")),
379 Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers)
380 .and_then(|parsed| {
381 parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
382 })
383 .ok_or(InvalidComponent("unix_timestamp")),
384 Component::End(modifiers) => parse_end(input, modifiers)
385 .map(ParsedItem::<()>::into_inner)
386 .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
387 }
388 }
389}
390
391impl Parsed {
393 pub const fn year(&self) -> Option<i32> {
395 self.year.get_primitive()
396 }
397
398 pub const fn year_century(&self) -> Option<i16> {
403 self.year_century.get_primitive()
404 }
405
406 pub const fn year_century_is_negative(&self) -> Option<bool> {
411 match self.year_century() {
412 Some(_) => Some(self.year_century_is_negative),
413 None => None,
414 }
415 }
416
417 pub const fn year_last_two(&self) -> Option<u8> {
419 self.year_last_two.get_primitive()
420 }
421
422 pub const fn iso_year(&self) -> Option<i32> {
424 self.iso_year.get_primitive()
425 }
426
427 pub const fn iso_year_century(&self) -> Option<i16> {
432 self.iso_year_century.get_primitive()
433 }
434
435 pub const fn iso_year_century_is_negative(&self) -> Option<bool> {
440 match self.iso_year_century() {
441 Some(_) => Some(self.iso_year_century_is_negative),
442 None => None,
443 }
444 }
445
446 pub const fn iso_year_last_two(&self) -> Option<u8> {
448 self.iso_year_last_two.get_primitive()
449 }
450
451 pub const fn month(&self) -> Option<Month> {
453 self.month
454 }
455
456 pub const fn sunday_week_number(&self) -> Option<u8> {
458 self.sunday_week_number.get_primitive()
459 }
460
461 pub const fn monday_week_number(&self) -> Option<u8> {
463 self.monday_week_number.get_primitive()
464 }
465
466 pub const fn iso_week_number(&self) -> Option<NonZeroU8> {
468 NonZeroU8::new(const_try_opt!(self.iso_week_number.get_primitive()))
469 }
470
471 pub const fn weekday(&self) -> Option<Weekday> {
473 self.weekday
474 }
475
476 pub const fn ordinal(&self) -> Option<NonZeroU16> {
478 NonZeroU16::new(const_try_opt!(self.ordinal.get_primitive()))
479 }
480
481 pub const fn day(&self) -> Option<NonZeroU8> {
483 NonZeroU8::new(const_try_opt!(self.day.get_primitive()))
484 }
485
486 pub const fn hour_24(&self) -> Option<u8> {
488 self.hour_24.get_primitive()
489 }
490
491 pub const fn hour_12(&self) -> Option<NonZeroU8> {
493 NonZeroU8::new(const_try_opt!(self.hour_12.get_primitive()))
494 }
495
496 pub const fn hour_12_is_pm(&self) -> Option<bool> {
498 self.hour_12_is_pm
499 }
500
501 pub const fn minute(&self) -> Option<u8> {
503 self.minute.get_primitive()
504 }
505
506 pub const fn second(&self) -> Option<u8> {
508 self.second.get_primitive()
509 }
510
511 pub const fn subsecond(&self) -> Option<u32> {
513 self.subsecond.get_primitive()
514 }
515
516 pub const fn offset_hour(&self) -> Option<i8> {
518 self.offset_hour.get_primitive()
519 }
520
521 #[doc(hidden)]
523 #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
524 pub const fn offset_minute(&self) -> Option<u8> {
525 Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
526 }
527
528 pub const fn offset_minute_signed(&self) -> Option<i8> {
530 match (self.offset_minute.get_primitive(), self.offset_is_negative) {
531 (Some(offset_minute), true) => Some(-offset_minute),
532 (Some(offset_minute), _) => Some(offset_minute),
533 (None, _) => None,
534 }
535 }
536
537 #[doc(hidden)]
539 #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
540 pub const fn offset_second(&self) -> Option<u8> {
541 Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
542 }
543
544 pub const fn offset_second_signed(&self) -> Option<i8> {
546 match (self.offset_second.get_primitive(), self.offset_is_negative) {
547 (Some(offset_second), true) => Some(-offset_second),
548 (Some(offset_second), _) => Some(offset_second),
549 (None, _) => None,
550 }
551 }
552
553 pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
555 self.unix_timestamp_nanos.get_primitive()
556 }
557}
558
559macro_rules! setters {
561 ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$(
562 #[doc = concat!("Set the `", stringify!($name), "` component.")]
563 pub fn $setter(&mut self, value: $type) -> Option<()> {
564 *self = self.$builder(value)?;
565 Some(())
566 }
567 )*};
568}
569
570impl Parsed {
575 setters! {
576 year set_year with_year i32;
577 }
578
579 pub fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
584 self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
585 if value != 0 {
586 self.year_century_is_negative = value.is_negative();
587 } else {
588 self.year_century_is_negative = is_negative;
589 }
590 Some(())
591 }
592
593 setters! {
594 year_last_two set_year_last_two with_year_last_two u8;
595 iso_year set_iso_year with_iso_year i32;
596 iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
597 }
598
599 pub fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
604 self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
605 if value != 0 {
606 self.iso_year_century_is_negative = value.is_negative();
607 } else {
608 self.iso_year_century_is_negative = is_negative;
609 }
610 Some(())
611 }
612
613 setters! {
614 month set_month with_month Month;
615 sunday_week_number set_sunday_week_number with_sunday_week_number u8;
616 monday_week_number set_monday_week_number with_monday_week_number u8;
617 iso_week_number set_iso_week_number with_iso_week_number NonZeroU8;
618 weekday set_weekday with_weekday Weekday;
619 ordinal set_ordinal with_ordinal NonZeroU16;
620 day set_day with_day NonZeroU8;
621 hour_24 set_hour_24 with_hour_24 u8;
622 hour_12 set_hour_12 with_hour_12 NonZeroU8;
623 hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
624 minute set_minute with_minute u8;
625 second set_second with_second u8;
626 subsecond set_subsecond with_subsecond u32;
627 offset_hour set_offset_hour with_offset_hour i8;
628 offset_minute set_offset_minute_signed with_offset_minute_signed i8;
629 offset_second set_offset_second_signed with_offset_second_signed i8;
630 unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
631 }
632
633 #[doc(hidden)]
635 #[deprecated(
636 since = "0.3.8",
637 note = "use `parsed.set_offset_minute_signed()` instead"
638 )]
639 pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
640 if value > i8::MAX.cast_unsigned() {
641 None
642 } else {
643 self.set_offset_minute_signed(value.cast_signed())
644 }
645 }
646
647 #[doc(hidden)]
649 #[deprecated(
650 since = "0.3.8",
651 note = "use `parsed.set_offset_second_signed()` instead"
652 )]
653 pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
654 if value > i8::MAX.cast_unsigned() {
655 None
656 } else {
657 self.set_offset_second_signed(value.cast_signed())
658 }
659 }
660}
661
662impl Parsed {
667 pub const fn with_year(mut self, value: i32) -> Option<Self> {
669 self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
670 Some(self)
671 }
672
673 pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
678 self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
679 if value != 0 {
680 self.year_century_is_negative = value.is_negative();
681 } else {
682 self.year_century_is_negative = is_negative;
683 }
684 Some(self)
685 }
686
687 pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
689 self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
690 Some(self)
691 }
692
693 pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
695 self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
696 Some(self)
697 }
698
699 pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
704 self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
705 if value != 0 {
706 self.iso_year_century_is_negative = value.is_negative();
707 } else {
708 self.iso_year_century_is_negative = is_negative;
709 }
710 Some(self)
711 }
712
713 pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
715 self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
716 Some(self)
717 }
718
719 pub const fn with_month(mut self, value: Month) -> Option<Self> {
721 self.month = Some(value);
722 Some(self)
723 }
724
725 pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
727 self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
728 Some(self)
729 }
730
731 pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
733 self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
734 Some(self)
735 }
736
737 pub const fn with_iso_week_number(mut self, value: NonZeroU8) -> Option<Self> {
739 self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
740 Some(self)
741 }
742
743 pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
745 self.weekday = Some(value);
746 Some(self)
747 }
748
749 pub const fn with_ordinal(mut self, value: NonZeroU16) -> Option<Self> {
751 self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
752 Some(self)
753 }
754
755 pub const fn with_day(mut self, value: NonZeroU8) -> Option<Self> {
757 self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
758 Some(self)
759 }
760
761 pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
763 self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
764 Some(self)
765 }
766
767 pub const fn with_hour_12(mut self, value: NonZeroU8) -> Option<Self> {
769 self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
770 Some(self)
771 }
772
773 pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
775 self.hour_12_is_pm = Some(value);
776 Some(self)
777 }
778
779 pub const fn with_minute(mut self, value: u8) -> Option<Self> {
781 self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
782 Some(self)
783 }
784
785 pub const fn with_second(mut self, value: u8) -> Option<Self> {
787 self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
788 Some(self)
789 }
790
791 pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
793 self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
794 Some(self)
795 }
796
797 pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
799 self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
800 Some(self)
801 }
802
803 #[doc(hidden)]
805 #[deprecated(
806 since = "0.3.8",
807 note = "use `parsed.with_offset_minute_signed()` instead"
808 )]
809 pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
810 if value > i8::MAX as u8 {
811 None
812 } else {
813 self.with_offset_minute_signed(value as _)
814 }
815 }
816
817 pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
819 self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
820 Some(self)
821 }
822
823 #[doc(hidden)]
825 #[deprecated(
826 since = "0.3.8",
827 note = "use `parsed.with_offset_second_signed()` instead"
828 )]
829 pub const fn with_offset_second(self, value: u8) -> Option<Self> {
830 if value > i8::MAX as u8 {
831 None
832 } else {
833 self.with_offset_second_signed(value as _)
834 }
835 }
836
837 pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
839 self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
840 Some(self)
841 }
842
843 pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
845 self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
846 Some(self)
847 }
848}
849
850impl TryFrom<Parsed> for Date {
851 type Error = error::TryFromParsed;
852
853 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
854 macro_rules! match_ {
856 (_ => $catch_all:expr $(,)?) => {
857 $catch_all
858 };
859 (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
860 if let ($(Some($name)),*) = ($(parsed.$name()),*) {
861 $arm
862 } else {
863 match_!($($rest)*)
864 }
865 };
866 }
867
868 const fn adjustment(year: i32) -> i16 {
871 match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
873 Weekday::Monday => 7,
874 Weekday::Tuesday => 1,
875 Weekday::Wednesday => 2,
876 Weekday::Thursday => 3,
877 Weekday::Friday => 4,
878 Weekday::Saturday => 5,
879 Weekday::Sunday => 6,
880 }
881 }
882
883 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
886 parsed.year(),
887 parsed.year_century(),
888 parsed.year_century_is_negative(),
889 parsed.year_last_two(),
890 ) {
891 let year = if is_negative {
892 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
893 } else {
894 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
895 };
896 parsed.year = OptionRangedI32::from(RangedI32::new(year));
897 }
898 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
899 parsed.iso_year(),
900 parsed.iso_year_century(),
901 parsed.iso_year_century_is_negative(),
902 parsed.iso_year_last_two(),
903 ) {
904 let iso_year = if is_negative {
905 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
906 } else {
907 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
908 };
909 parsed.iso_year = OptionRangedI32::from(RangedI32::new(iso_year));
910 }
911
912 match_! {
913 (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
914 (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
915 (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
916 iso_year,
917 iso_week_number.get(),
918 weekday,
919 )?),
920 (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
921 year,
922 (sunday_week_number.cast_signed().extend::<i16>() * 7
923 + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
924 - adjustment(year)
925 + 1).cast_unsigned(),
926 )?),
927 (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
928 year,
929 (monday_week_number.cast_signed().extend::<i16>() * 7
930 + weekday.number_days_from_monday().cast_signed().extend::<i16>()
931 - adjustment(year)
932 + 1).cast_unsigned(),
933 )?),
934 _ => Err(InsufficientInformation),
935 }
936 }
937}
938
939impl TryFrom<Parsed> for Time {
940 type Error = error::TryFromParsed;
941
942 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
943 let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
944 (Some(hour), _, _) => hour,
945 (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
946 (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
947 (_, Some(hour), Some(false)) => hour.get(),
948 (_, Some(hour), Some(true)) => hour.get() + 12,
949 _ => return Err(InsufficientInformation),
950 };
951
952 if parsed.hour_24().is_none()
953 && parsed.hour_12().is_some()
954 && parsed.hour_12_is_pm().is_some()
955 && parsed.minute().is_none()
956 && parsed.second().is_none()
957 && parsed.subsecond().is_none()
958 {
959 return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
960 }
961
962 match (parsed.minute(), parsed.second(), parsed.subsecond()) {
964 (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
965 (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
966 (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
967 (Some(minute), Some(second), Some(subsecond)) => {
968 Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
969 }
970 _ => Err(InsufficientInformation),
971 }
972 }
973}
974
975fn utc_offset_try_from_parsed<const REQUIRED: bool>(
976 parsed: Parsed,
977) -> Result<UtcOffset, error::TryFromParsed> {
978 let hour = match (REQUIRED, parsed.offset_hour()) {
979 (true, None) => return Err(InsufficientInformation),
981 (false, None) => return Ok(UtcOffset::UTC),
984 (_, Some(hour)) => hour,
986 };
987 let minute = parsed.offset_minute_signed();
988 let second = minute.and_then(|_| parsed.offset_second_signed());
990
991 let minute = minute.unwrap_or(0);
992 let second = second.unwrap_or(0);
993
994 UtcOffset::from_hms(hour, minute, second).map_err(|mut err| {
995 if err.name == "hours" {
997 err.name = "offset hour";
998 } else if err.name == "minutes" {
999 err.name = "offset minute";
1000 } else if err.name == "seconds" {
1001 err.name = "offset second";
1002 }
1003 err.into()
1004 })
1005}
1006
1007impl TryFrom<Parsed> for UtcOffset {
1008 type Error = error::TryFromParsed;
1009
1010 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1011 utc_offset_try_from_parsed::<true>(parsed)
1012 }
1013}
1014
1015impl TryFrom<Parsed> for PrimitiveDateTime {
1016 type Error = error::TryFromParsed;
1017
1018 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1019 Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
1020 }
1021}
1022
1023impl TryFrom<Parsed> for UtcDateTime {
1024 type Error = error::TryFromParsed;
1025
1026 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1027 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1028 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1029 if let Some(subsecond) = parsed.subsecond() {
1030 value = value.replace_nanosecond(subsecond)?;
1031 }
1032 return Ok(value);
1033 }
1034
1035 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1039 if parsed.set_second(59).is_none() {
1040 bug!("59 is a valid second");
1041 }
1042 if parsed.set_subsecond(999_999_999).is_none() {
1043 bug!("999_999_999 is a valid subsecond");
1044 }
1045 true
1046 } else {
1047 false
1048 };
1049
1050 let dt = OffsetDateTime::new_in_offset(
1051 Date::try_from(parsed)?,
1052 Time::try_from(parsed)?,
1053 utc_offset_try_from_parsed::<false>(parsed)?,
1054 )
1055 .to_utc();
1056
1057 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1058 return Err(error::TryFromParsed::ComponentRange(
1059 error::ComponentRange {
1060 name: "second",
1061 minimum: 0,
1062 maximum: 59,
1063 value: 60,
1064 conditional_message: Some("because leap seconds are not supported"),
1065 },
1066 ));
1067 }
1068 Ok(dt)
1069 }
1070}
1071
1072impl TryFrom<Parsed> for OffsetDateTime {
1073 type Error = error::TryFromParsed;
1074
1075 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1076 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1077 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1078 if let Some(subsecond) = parsed.subsecond() {
1079 value = value.replace_nanosecond(subsecond)?;
1080 }
1081 return Ok(value);
1082 }
1083
1084 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1088 if parsed.set_second(59).is_none() {
1089 bug!("59 is a valid second");
1090 }
1091 if parsed.set_subsecond(999_999_999).is_none() {
1092 bug!("999_999_999 is a valid subsecond");
1093 }
1094 true
1095 } else {
1096 false
1097 };
1098
1099 let dt = Self::new_in_offset(
1100 Date::try_from(parsed)?,
1101 Time::try_from(parsed)?,
1102 UtcOffset::try_from(parsed)?,
1103 );
1104
1105 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1106 return Err(error::TryFromParsed::ComponentRange(
1107 error::ComponentRange {
1108 name: "second",
1109 minimum: 0,
1110 maximum: 59,
1111 value: 60,
1112 conditional_message: Some("because leap seconds are not supported"),
1113 },
1114 ));
1115 }
1116 Ok(dt)
1117 }
1118}