1use core::num::NonZero;
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 #[inline]
46 fn parse_item<'a>(
47 &self,
48 parsed: &mut Parsed,
49 input: &'a [u8],
50 ) -> Result<&'a [u8], error::ParseFromDescription> {
51 match self {
52 Self::Literal(literal) => Parsed::parse_literal(input, literal),
53 Self::Component(component) => parsed.parse_component(input, *component),
54 Self::Compound(compound) => parsed.parse_items(input, compound),
55 Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
56 Self::First(items) => {
57 let mut first_err = None;
58
59 for item in items.iter() {
60 match parsed.parse_item(input, item) {
61 Ok(remaining_input) => return Ok(remaining_input),
62 Err(err) if first_err.is_none() => first_err = Some(err),
63 Err(_) => {}
64 }
65 }
66
67 match first_err {
68 Some(err) => Err(err),
69 None => Ok(input),
72 }
73 }
74 }
75 }
76}
77
78#[cfg(feature = "alloc")]
79impl sealed::AnyFormatItem for OwnedFormatItem {
80 #[inline]
81 fn parse_item<'a>(
82 &self,
83 parsed: &mut Parsed,
84 input: &'a [u8],
85 ) -> Result<&'a [u8], error::ParseFromDescription> {
86 match self {
87 Self::Literal(literal) => Parsed::parse_literal(input, literal),
88 Self::Component(component) => parsed.parse_component(input, *component),
89 Self::Compound(compound) => parsed.parse_items(input, compound),
90 Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)),
91 Self::First(items) => {
92 let mut first_err = None;
93
94 for item in items.iter() {
95 match parsed.parse_item(input, item) {
96 Ok(remaining_input) => return Ok(remaining_input),
97 Err(err) if first_err.is_none() => first_err = Some(err),
98 Err(_) => {}
99 }
100 }
101
102 match first_err {
103 Some(err) => Err(err),
104 None => Ok(input),
107 }
108 }
109 }
110 }
111}
112
113#[derive(Debug, Clone, Copy)]
120pub struct Parsed {
121 year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
123 year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
125 year_last_two: OptionRangedU8<0, 99>,
127 iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
129 iso_year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
131 iso_year_last_two: OptionRangedU8<0, 99>,
133 month: Option<Month>,
135 sunday_week_number: OptionRangedU8<0, 53>,
137 monday_week_number: OptionRangedU8<0, 53>,
139 iso_week_number: OptionRangedU8<1, 53>,
141 weekday: Option<Weekday>,
143 ordinal: OptionRangedU16<1, 366>,
145 day: OptionRangedU8<1, 31>,
147 hour_24: OptionRangedU8<0, { Hour::per_t::<u8>(Day) - 1 }>,
149 hour_12: OptionRangedU8<1, 12>,
152 hour_12_is_pm: Option<bool>,
154 minute: OptionRangedU8<0, { Minute::per_t::<u8>(Hour) - 1 }>,
156 second: OptionRangedU8<0, { Second::per_t::<u8>(Minute) }>,
159 subsecond: OptionRangedU32<0, { Nanosecond::per_t::<u32>(Second) - 1 }>,
161 offset_hour: OptionRangedI8<-23, 23>,
163 offset_minute:
165 OptionRangedI8<{ -Minute::per_t::<i8>(Hour) + 1 }, { Minute::per_t::<i8>(Hour) - 1 }>,
166 offset_second:
168 OptionRangedI8<{ -Second::per_t::<i8>(Minute) + 1 }, { Second::per_t::<i8>(Minute) - 1 }>,
169 unix_timestamp_nanos: OptionRangedI128<
171 {
172 OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
173 .unix_timestamp_nanos()
174 },
175 {
176 OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC)
177 .unix_timestamp_nanos()
178 },
179 >,
180 offset_is_negative: bool,
183 year_century_is_negative: bool,
186 iso_year_century_is_negative: bool,
189 pub(super) leap_second_allowed: bool,
192}
193
194impl Default for Parsed {
195 #[inline]
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201impl Parsed {
202 #[inline]
204 pub const fn new() -> Self {
205 Self {
206 year: OptionRangedI32::None,
207 year_century: OptionRangedI16::None,
208 year_last_two: OptionRangedU8::None,
209 iso_year: OptionRangedI32::None,
210 iso_year_century: OptionRangedI16::None,
211 iso_year_last_two: OptionRangedU8::None,
212 month: None,
213 sunday_week_number: OptionRangedU8::None,
214 monday_week_number: OptionRangedU8::None,
215 iso_week_number: OptionRangedU8::None,
216 weekday: None,
217 ordinal: OptionRangedU16::None,
218 day: OptionRangedU8::None,
219 hour_24: OptionRangedU8::None,
220 hour_12: OptionRangedU8::None,
221 hour_12_is_pm: None,
222 minute: OptionRangedU8::None,
223 second: OptionRangedU8::None,
224 subsecond: OptionRangedU32::None,
225 offset_hour: OptionRangedI8::None,
226 offset_minute: OptionRangedI8::None,
227 offset_second: OptionRangedI8::None,
228 unix_timestamp_nanos: OptionRangedI128::None,
229 offset_is_negative: false,
230 year_century_is_negative: false,
231 iso_year_century_is_negative: false,
232 leap_second_allowed: false,
233 }
234 }
235
236 #[inline]
242 pub fn parse_item<'a>(
243 &mut self,
244 input: &'a [u8],
245 item: &impl sealed::AnyFormatItem,
246 ) -> Result<&'a [u8], error::ParseFromDescription> {
247 item.parse_item(self, input)
248 }
249
250 #[inline]
256 pub fn parse_items<'a>(
257 &mut self,
258 mut input: &'a [u8],
259 items: &[impl sealed::AnyFormatItem],
260 ) -> Result<&'a [u8], error::ParseFromDescription> {
261 let mut this = *self;
264 for item in items {
265 input = this.parse_item(input, item)?;
266 }
267 *self = this;
268 Ok(input)
269 }
270
271 #[inline]
273 pub fn parse_literal<'a>(
274 input: &'a [u8],
275 literal: &[u8],
276 ) -> Result<&'a [u8], error::ParseFromDescription> {
277 input
278 .strip_prefix(literal)
279 .ok_or(error::ParseFromDescription::InvalidLiteral)
280 }
281
282 pub fn parse_component<'a>(
285 &mut self,
286 input: &'a [u8],
287 component: Component,
288 ) -> Result<&'a [u8], error::ParseFromDescription> {
289 use error::ParseFromDescription::InvalidComponent;
290
291 match component {
292 Component::Day(modifiers) => parse_day(input, modifiers)
293 .and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
294 .ok_or(InvalidComponent("day")),
295 Component::Month(modifiers) => parse_month(input, modifiers)
296 .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
297 .ok_or(InvalidComponent("month")),
298 Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
299 .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
300 .ok_or(InvalidComponent("ordinal")),
301 Component::Weekday(modifiers) => parse_weekday(input, modifiers)
302 .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
303 .ok_or(InvalidComponent("weekday")),
304 Component::WeekNumber(modifiers) => {
305 let ParsedItem(remaining, value) =
306 parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
307 match modifiers.repr {
308 modifier::WeekNumberRepr::Iso => {
309 NonZero::new(value).and_then(|value| self.set_iso_week_number(value))
310 }
311 modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
312 modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value),
313 }
314 .ok_or(InvalidComponent("week number"))?;
315 Ok(remaining)
316 }
317 Component::Year(modifiers) => {
318 let ParsedItem(remaining, (value, is_negative)) =
319 parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
320 match (modifiers.iso_week_based, modifiers.repr) {
321 (false, modifier::YearRepr::Full) => self.set_year(value),
322 (false, modifier::YearRepr::Century) => {
323 self.set_year_century(value.truncate(), is_negative)
324 }
325 (false, modifier::YearRepr::LastTwo) => {
326 self.set_year_last_two(value.cast_unsigned().truncate())
327 }
328 (true, modifier::YearRepr::Full) => self.set_iso_year(value),
329 (true, modifier::YearRepr::Century) => {
330 self.set_iso_year_century(value.truncate(), is_negative)
331 }
332 (true, modifier::YearRepr::LastTwo) => {
333 self.set_iso_year_last_two(value.cast_unsigned().truncate())
334 }
335 }
336 .ok_or(InvalidComponent("year"))?;
337 Ok(remaining)
338 }
339 Component::Hour(modifiers) => {
340 let ParsedItem(remaining, value) =
341 parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?;
342 if modifiers.is_12_hour_clock {
343 NonZero::new(value).and_then(|value| self.set_hour_12(value))
344 } else {
345 self.set_hour_24(value)
346 }
347 .ok_or(InvalidComponent("hour"))?;
348 Ok(remaining)
349 }
350 Component::Minute(modifiers) => parse_minute(input, modifiers)
351 .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
352 .ok_or(InvalidComponent("minute")),
353 Component::Period(modifiers) => parse_period(input, modifiers)
354 .and_then(|parsed| {
355 parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
356 })
357 .ok_or(InvalidComponent("period")),
358 Component::Second(modifiers) => parse_second(input, modifiers)
359 .and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
360 .ok_or(InvalidComponent("second")),
361 Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
362 .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
363 .ok_or(InvalidComponent("subsecond")),
364 Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
365 .and_then(|parsed| {
366 parsed.consume_value(|(value, is_negative)| {
367 self.set_offset_hour(value)?;
368 self.offset_is_negative = is_negative;
369 Some(())
370 })
371 })
372 .ok_or(InvalidComponent("offset hour")),
373 Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
374 .and_then(|parsed| {
375 parsed.consume_value(|value| self.set_offset_minute_signed(value))
376 })
377 .ok_or(InvalidComponent("offset minute")),
378 Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
379 .and_then(|parsed| {
380 parsed.consume_value(|value| self.set_offset_second_signed(value))
381 })
382 .ok_or(InvalidComponent("offset second")),
383 Component::Ignore(modifiers) => parse_ignore(input, modifiers)
384 .map(ParsedItem::<()>::into_inner)
385 .ok_or(InvalidComponent("ignore")),
386 Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers)
387 .and_then(|parsed| {
388 parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
389 })
390 .ok_or(InvalidComponent("unix_timestamp")),
391 Component::End(modifiers) => parse_end(input, modifiers)
392 .map(ParsedItem::<()>::into_inner)
393 .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
394 }
395 }
396}
397
398impl Parsed {
400 #[inline]
402 pub const fn year(&self) -> Option<i32> {
403 self.year.get_primitive()
404 }
405
406 #[inline]
411 pub const fn year_century(&self) -> Option<i16> {
412 self.year_century.get_primitive()
413 }
414
415 #[inline]
420 pub const fn year_century_is_negative(&self) -> Option<bool> {
421 match self.year_century() {
422 Some(_) => Some(self.year_century_is_negative),
423 None => None,
424 }
425 }
426
427 #[inline]
429 pub const fn year_last_two(&self) -> Option<u8> {
430 self.year_last_two.get_primitive()
431 }
432
433 #[inline]
435 pub const fn iso_year(&self) -> Option<i32> {
436 self.iso_year.get_primitive()
437 }
438
439 #[inline]
444 pub const fn iso_year_century(&self) -> Option<i16> {
445 self.iso_year_century.get_primitive()
446 }
447
448 #[inline]
453 pub const fn iso_year_century_is_negative(&self) -> Option<bool> {
454 match self.iso_year_century() {
455 Some(_) => Some(self.iso_year_century_is_negative),
456 None => None,
457 }
458 }
459
460 #[inline]
462 pub const fn iso_year_last_two(&self) -> Option<u8> {
463 self.iso_year_last_two.get_primitive()
464 }
465
466 #[inline]
468 pub const fn month(&self) -> Option<Month> {
469 self.month
470 }
471
472 #[inline]
474 pub const fn sunday_week_number(&self) -> Option<u8> {
475 self.sunday_week_number.get_primitive()
476 }
477
478 #[inline]
480 pub const fn monday_week_number(&self) -> Option<u8> {
481 self.monday_week_number.get_primitive()
482 }
483
484 #[inline]
486 pub const fn iso_week_number(&self) -> Option<NonZero<u8>> {
487 NonZero::new(const_try_opt!(self.iso_week_number.get_primitive()))
488 }
489
490 #[inline]
492 pub const fn weekday(&self) -> Option<Weekday> {
493 self.weekday
494 }
495
496 #[inline]
498 pub const fn ordinal(&self) -> Option<NonZero<u16>> {
499 NonZero::new(const_try_opt!(self.ordinal.get_primitive()))
500 }
501
502 #[inline]
504 pub const fn day(&self) -> Option<NonZero<u8>> {
505 NonZero::new(const_try_opt!(self.day.get_primitive()))
506 }
507
508 #[inline]
510 pub const fn hour_24(&self) -> Option<u8> {
511 self.hour_24.get_primitive()
512 }
513
514 #[inline]
516 pub const fn hour_12(&self) -> Option<NonZero<u8>> {
517 NonZero::new(const_try_opt!(self.hour_12.get_primitive()))
518 }
519
520 #[inline]
522 pub const fn hour_12_is_pm(&self) -> Option<bool> {
523 self.hour_12_is_pm
524 }
525
526 #[inline]
528 pub const fn minute(&self) -> Option<u8> {
529 self.minute.get_primitive()
530 }
531
532 #[inline]
534 pub const fn second(&self) -> Option<u8> {
535 self.second.get_primitive()
536 }
537
538 #[inline]
540 pub const fn subsecond(&self) -> Option<u32> {
541 self.subsecond.get_primitive()
542 }
543
544 #[inline]
546 pub const fn offset_hour(&self) -> Option<i8> {
547 self.offset_hour.get_primitive()
548 }
549
550 #[doc(hidden)]
552 #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
553 #[inline]
554 pub const fn offset_minute(&self) -> Option<u8> {
555 Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
556 }
557
558 #[inline]
560 pub const fn offset_minute_signed(&self) -> Option<i8> {
561 match (self.offset_minute.get_primitive(), self.offset_is_negative) {
562 (Some(offset_minute), true) => Some(-offset_minute),
563 (Some(offset_minute), _) => Some(offset_minute),
564 (None, _) => None,
565 }
566 }
567
568 #[doc(hidden)]
570 #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
571 #[inline]
572 pub const fn offset_second(&self) -> Option<u8> {
573 Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
574 }
575
576 #[inline]
578 pub const fn offset_second_signed(&self) -> Option<i8> {
579 match (self.offset_second.get_primitive(), self.offset_is_negative) {
580 (Some(offset_second), true) => Some(-offset_second),
581 (Some(offset_second), _) => Some(offset_second),
582 (None, _) => None,
583 }
584 }
585
586 #[inline]
588 pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
589 self.unix_timestamp_nanos.get_primitive()
590 }
591}
592
593macro_rules! setters {
595 ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$(
596 #[doc = concat!("Set the `", stringify!($name), "` component.")]
597 #[inline]
598 pub const fn $setter(&mut self, value: $type) -> Option<()> {
599 match self.$builder(value) {
600 Some(value) => {
601 *self = value;
602 Some(())
603 },
604 None => None,
605 }
606 }
607 )*};
608}
609
610impl Parsed {
615 setters! {
616 year set_year with_year i32;
617 }
618
619 #[inline]
624 pub const fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
625 self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
626 if value != 0 {
627 self.year_century_is_negative = value.is_negative();
628 } else {
629 self.year_century_is_negative = is_negative;
630 }
631 Some(())
632 }
633
634 setters! {
635 year_last_two set_year_last_two with_year_last_two u8;
636 iso_year set_iso_year with_iso_year i32;
637 iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
638 }
639
640 #[inline]
645 pub const fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
646 self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
647 if value != 0 {
648 self.iso_year_century_is_negative = value.is_negative();
649 } else {
650 self.iso_year_century_is_negative = is_negative;
651 }
652 Some(())
653 }
654
655 setters! {
656 month set_month with_month Month;
657 sunday_week_number set_sunday_week_number with_sunday_week_number u8;
658 monday_week_number set_monday_week_number with_monday_week_number u8;
659 iso_week_number set_iso_week_number with_iso_week_number NonZero<u8>;
660 weekday set_weekday with_weekday Weekday;
661 ordinal set_ordinal with_ordinal NonZero<u16>;
662 day set_day with_day NonZero<u8>;
663 hour_24 set_hour_24 with_hour_24 u8;
664 hour_12 set_hour_12 with_hour_12 NonZero<u8>;
665 hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
666 minute set_minute with_minute u8;
667 second set_second with_second u8;
668 subsecond set_subsecond with_subsecond u32;
669 offset_hour set_offset_hour with_offset_hour i8;
670 offset_minute set_offset_minute_signed with_offset_minute_signed i8;
671 offset_second set_offset_second_signed with_offset_second_signed i8;
672 unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
673 }
674
675 #[doc(hidden)]
677 #[deprecated(
678 since = "0.3.8",
679 note = "use `parsed.set_offset_minute_signed()` instead"
680 )]
681 #[inline]
682 pub const fn set_offset_minute(&mut self, value: u8) -> Option<()> {
683 if value > i8::MAX as u8 {
684 None
685 } else {
686 self.set_offset_minute_signed(value as i8)
687 }
688 }
689
690 #[doc(hidden)]
692 #[deprecated(
693 since = "0.3.8",
694 note = "use `parsed.set_offset_second_signed()` instead"
695 )]
696 #[inline]
697 pub const fn set_offset_second(&mut self, value: u8) -> Option<()> {
698 if value > i8::MAX as u8 {
699 None
700 } else {
701 self.set_offset_second_signed(value as i8)
702 }
703 }
704}
705
706impl Parsed {
711 #[inline]
713 pub const fn with_year(mut self, value: i32) -> Option<Self> {
714 self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
715 Some(self)
716 }
717
718 #[inline]
723 pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
724 self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
725 if value != 0 {
726 self.year_century_is_negative = value.is_negative();
727 } else {
728 self.year_century_is_negative = is_negative;
729 }
730 Some(self)
731 }
732
733 #[inline]
735 pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
736 self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
737 Some(self)
738 }
739
740 #[inline]
742 pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
743 self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
744 Some(self)
745 }
746
747 #[inline]
752 pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
753 self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
754 if value != 0 {
755 self.iso_year_century_is_negative = value.is_negative();
756 } else {
757 self.iso_year_century_is_negative = is_negative;
758 }
759 Some(self)
760 }
761
762 #[inline]
764 pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
765 self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
766 Some(self)
767 }
768
769 #[inline]
771 pub const fn with_month(mut self, value: Month) -> Option<Self> {
772 self.month = Some(value);
773 Some(self)
774 }
775
776 #[inline]
778 pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
779 self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
780 Some(self)
781 }
782
783 #[inline]
785 pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
786 self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
787 Some(self)
788 }
789
790 #[inline]
792 pub const fn with_iso_week_number(mut self, value: NonZero<u8>) -> Option<Self> {
793 self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
794 Some(self)
795 }
796
797 #[inline]
799 pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
800 self.weekday = Some(value);
801 Some(self)
802 }
803
804 #[inline]
806 pub const fn with_ordinal(mut self, value: NonZero<u16>) -> Option<Self> {
807 self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
808 Some(self)
809 }
810
811 #[inline]
813 pub const fn with_day(mut self, value: NonZero<u8>) -> Option<Self> {
814 self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
815 Some(self)
816 }
817
818 #[inline]
820 pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
821 self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
822 Some(self)
823 }
824
825 #[inline]
827 pub const fn with_hour_12(mut self, value: NonZero<u8>) -> Option<Self> {
828 self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
829 Some(self)
830 }
831
832 #[inline]
834 pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
835 self.hour_12_is_pm = Some(value);
836 Some(self)
837 }
838
839 #[inline]
841 pub const fn with_minute(mut self, value: u8) -> Option<Self> {
842 self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
843 Some(self)
844 }
845
846 #[inline]
848 pub const fn with_second(mut self, value: u8) -> Option<Self> {
849 self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
850 Some(self)
851 }
852
853 #[inline]
855 pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
856 self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
857 Some(self)
858 }
859
860 #[inline]
862 pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
863 self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
864 Some(self)
865 }
866
867 #[doc(hidden)]
869 #[deprecated(
870 since = "0.3.8",
871 note = "use `parsed.with_offset_minute_signed()` instead"
872 )]
873 #[inline]
874 pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
875 if value > i8::MAX as u8 {
876 None
877 } else {
878 self.with_offset_minute_signed(value as i8)
879 }
880 }
881
882 #[inline]
884 pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
885 self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
886 Some(self)
887 }
888
889 #[doc(hidden)]
891 #[deprecated(
892 since = "0.3.8",
893 note = "use `parsed.with_offset_second_signed()` instead"
894 )]
895 #[inline]
896 pub const fn with_offset_second(self, value: u8) -> Option<Self> {
897 if value > i8::MAX as u8 {
898 None
899 } else {
900 self.with_offset_second_signed(value as i8)
901 }
902 }
903
904 #[inline]
906 pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
907 self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
908 Some(self)
909 }
910
911 #[inline]
913 pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
914 self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
915 Some(self)
916 }
917}
918
919impl TryFrom<Parsed> for Date {
920 type Error = error::TryFromParsed;
921
922 #[inline]
923 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
924 macro_rules! match_ {
926 (_ => $catch_all:expr $(,)?) => {
927 $catch_all
928 };
929 (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
930 if let ($(Some($name)),*) = ($(parsed.$name()),*) {
931 $arm
932 } else {
933 match_!($($rest)*)
934 }
935 };
936 }
937
938 #[inline]
941 const fn adjustment(year: i32) -> i16 {
942 match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
944 Weekday::Monday => 7,
945 Weekday::Tuesday => 1,
946 Weekday::Wednesday => 2,
947 Weekday::Thursday => 3,
948 Weekday::Friday => 4,
949 Weekday::Saturday => 5,
950 Weekday::Sunday => 6,
951 }
952 }
953
954 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
957 parsed.year(),
958 parsed.year_century(),
959 parsed.year_century_is_negative(),
960 parsed.year_last_two(),
961 ) {
962 let year = if is_negative {
963 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
964 } else {
965 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
966 };
967 parsed.year = OptionRangedI32::from(RangedI32::new(year));
968 }
969 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
970 parsed.iso_year(),
971 parsed.iso_year_century(),
972 parsed.iso_year_century_is_negative(),
973 parsed.iso_year_last_two(),
974 ) {
975 let iso_year = if is_negative {
976 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
977 } else {
978 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
979 };
980 parsed.iso_year = OptionRangedI32::from(RangedI32::new(iso_year));
981 }
982
983 match_! {
984 (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
985 (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
986 (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
987 iso_year,
988 iso_week_number.get(),
989 weekday,
990 )?),
991 (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
992 year,
993 (sunday_week_number.cast_signed().extend::<i16>() * 7
994 + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
995 - adjustment(year)
996 + 1).cast_unsigned(),
997 )?),
998 (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
999 year,
1000 (monday_week_number.cast_signed().extend::<i16>() * 7
1001 + weekday.number_days_from_monday().cast_signed().extend::<i16>()
1002 - adjustment(year)
1003 + 1).cast_unsigned(),
1004 )?),
1005 _ => Err(InsufficientInformation),
1006 }
1007 }
1008}
1009
1010impl TryFrom<Parsed> for Time {
1011 type Error = error::TryFromParsed;
1012
1013 #[inline]
1014 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1015 let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
1016 (Some(hour), _, _) => hour,
1017 (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
1018 (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
1019 (_, Some(hour), Some(false)) => hour.get(),
1020 (_, Some(hour), Some(true)) => hour.get() + 12,
1021 _ => return Err(InsufficientInformation),
1022 };
1023
1024 if parsed.hour_24().is_none()
1025 && parsed.hour_12().is_some()
1026 && parsed.hour_12_is_pm().is_some()
1027 && parsed.minute().is_none()
1028 && parsed.second().is_none()
1029 && parsed.subsecond().is_none()
1030 {
1031 return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
1032 }
1033
1034 match (parsed.minute(), parsed.second(), parsed.subsecond()) {
1036 (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
1037 (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
1038 (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
1039 (Some(minute), Some(second), Some(subsecond)) => {
1040 Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
1041 }
1042 _ => Err(InsufficientInformation),
1043 }
1044 }
1045}
1046
1047#[inline]
1048fn utc_offset_try_from_parsed<const REQUIRED: bool>(
1049 parsed: Parsed,
1050) -> Result<UtcOffset, error::TryFromParsed> {
1051 let hour = match (REQUIRED, parsed.offset_hour()) {
1052 (true, None) => return Err(InsufficientInformation),
1054 (false, None) => return Ok(UtcOffset::UTC),
1057 (_, Some(hour)) => hour,
1059 };
1060 let minute = parsed.offset_minute_signed();
1061 let second = minute.and_then(|_| parsed.offset_second_signed());
1063
1064 let minute = minute.unwrap_or(0);
1065 let second = second.unwrap_or(0);
1066
1067 UtcOffset::from_hms(hour, minute, second).map_err(|mut err| {
1068 if err.name == "hours" {
1070 err.name = "offset hour";
1071 } else if err.name == "minutes" {
1072 err.name = "offset minute";
1073 } else if err.name == "seconds" {
1074 err.name = "offset second";
1075 }
1076 err.into()
1077 })
1078}
1079
1080impl TryFrom<Parsed> for UtcOffset {
1081 type Error = error::TryFromParsed;
1082
1083 #[inline]
1084 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1085 utc_offset_try_from_parsed::<true>(parsed)
1086 }
1087}
1088
1089impl TryFrom<Parsed> for PrimitiveDateTime {
1090 type Error = error::TryFromParsed;
1091
1092 #[inline]
1093 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1094 Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
1095 }
1096}
1097
1098impl TryFrom<Parsed> for UtcDateTime {
1099 type Error = error::TryFromParsed;
1100
1101 #[inline]
1102 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1103 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1104 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1105 if let Some(subsecond) = parsed.subsecond() {
1106 value = value.replace_nanosecond(subsecond)?;
1107 }
1108 return Ok(value);
1109 }
1110
1111 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1115 if parsed.set_second(59).is_none() {
1116 bug!("59 is a valid second");
1117 }
1118 if parsed.set_subsecond(999_999_999).is_none() {
1119 bug!("999_999_999 is a valid subsecond");
1120 }
1121 true
1122 } else {
1123 false
1124 };
1125
1126 let dt = OffsetDateTime::new_in_offset(
1127 Date::try_from(parsed)?,
1128 Time::try_from(parsed)?,
1129 utc_offset_try_from_parsed::<false>(parsed)?,
1130 )
1131 .to_utc();
1132
1133 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1134 return Err(error::TryFromParsed::ComponentRange(
1135 error::ComponentRange {
1136 name: "second",
1137 minimum: 0,
1138 maximum: 59,
1139 value: 60,
1140 conditional_message: Some("because leap seconds are not supported"),
1141 },
1142 ));
1143 }
1144 Ok(dt)
1145 }
1146}
1147
1148impl TryFrom<Parsed> for OffsetDateTime {
1149 type Error = error::TryFromParsed;
1150
1151 #[inline]
1152 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1153 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1154 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1155 if let Some(subsecond) = parsed.subsecond() {
1156 value = value.replace_nanosecond(subsecond)?;
1157 }
1158 return Ok(value);
1159 }
1160
1161 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1165 if parsed.set_second(59).is_none() {
1166 bug!("59 is a valid second");
1167 }
1168 if parsed.set_subsecond(999_999_999).is_none() {
1169 bug!("999_999_999 is a valid subsecond");
1170 }
1171 true
1172 } else {
1173 false
1174 };
1175
1176 let dt = Self::new_in_offset(
1177 Date::try_from(parsed)?,
1178 Time::try_from(parsed)?,
1179 UtcOffset::try_from(parsed)?,
1180 );
1181
1182 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1183 return Err(error::TryFromParsed::ComponentRange(
1184 error::ComponentRange {
1185 name: "second",
1186 minimum: 0,
1187 maximum: 59,
1188 value: 60,
1189 conditional_message: Some("because leap seconds are not supported"),
1190 },
1191 ));
1192 }
1193 Ok(dt)
1194 }
1195}