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 fn $setter(&mut self, value: $type) -> Option<()> {
599 *self = self.$builder(value)?;
600 Some(())
601 }
602 )*};
603}
604
605impl Parsed {
610 setters! {
611 year set_year with_year i32;
612 }
613
614 #[inline]
619 pub fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
620 self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
621 if value != 0 {
622 self.year_century_is_negative = value.is_negative();
623 } else {
624 self.year_century_is_negative = is_negative;
625 }
626 Some(())
627 }
628
629 setters! {
630 year_last_two set_year_last_two with_year_last_two u8;
631 iso_year set_iso_year with_iso_year i32;
632 iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
633 }
634
635 #[inline]
640 pub fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
641 self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
642 if value != 0 {
643 self.iso_year_century_is_negative = value.is_negative();
644 } else {
645 self.iso_year_century_is_negative = is_negative;
646 }
647 Some(())
648 }
649
650 setters! {
651 month set_month with_month Month;
652 sunday_week_number set_sunday_week_number with_sunday_week_number u8;
653 monday_week_number set_monday_week_number with_monday_week_number u8;
654 iso_week_number set_iso_week_number with_iso_week_number NonZero<u8>;
655 weekday set_weekday with_weekday Weekday;
656 ordinal set_ordinal with_ordinal NonZero<u16>;
657 day set_day with_day NonZero<u8>;
658 hour_24 set_hour_24 with_hour_24 u8;
659 hour_12 set_hour_12 with_hour_12 NonZero<u8>;
660 hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
661 minute set_minute with_minute u8;
662 second set_second with_second u8;
663 subsecond set_subsecond with_subsecond u32;
664 offset_hour set_offset_hour with_offset_hour i8;
665 offset_minute set_offset_minute_signed with_offset_minute_signed i8;
666 offset_second set_offset_second_signed with_offset_second_signed i8;
667 unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
668 }
669
670 #[doc(hidden)]
672 #[deprecated(
673 since = "0.3.8",
674 note = "use `parsed.set_offset_minute_signed()` instead"
675 )]
676 #[inline]
677 pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
678 if value > i8::MAX.cast_unsigned() {
679 None
680 } else {
681 self.set_offset_minute_signed(value.cast_signed())
682 }
683 }
684
685 #[doc(hidden)]
687 #[deprecated(
688 since = "0.3.8",
689 note = "use `parsed.set_offset_second_signed()` instead"
690 )]
691 #[inline]
692 pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
693 if value > i8::MAX.cast_unsigned() {
694 None
695 } else {
696 self.set_offset_second_signed(value.cast_signed())
697 }
698 }
699}
700
701impl Parsed {
706 #[inline]
708 pub const fn with_year(mut self, value: i32) -> Option<Self> {
709 self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
710 Some(self)
711 }
712
713 #[inline]
718 pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
719 self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
720 if value != 0 {
721 self.year_century_is_negative = value.is_negative();
722 } else {
723 self.year_century_is_negative = is_negative;
724 }
725 Some(self)
726 }
727
728 #[inline]
730 pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
731 self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
732 Some(self)
733 }
734
735 #[inline]
737 pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
738 self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
739 Some(self)
740 }
741
742 #[inline]
747 pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
748 self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
749 if value != 0 {
750 self.iso_year_century_is_negative = value.is_negative();
751 } else {
752 self.iso_year_century_is_negative = is_negative;
753 }
754 Some(self)
755 }
756
757 #[inline]
759 pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
760 self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
761 Some(self)
762 }
763
764 #[inline]
766 pub const fn with_month(mut self, value: Month) -> Option<Self> {
767 self.month = Some(value);
768 Some(self)
769 }
770
771 #[inline]
773 pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
774 self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
775 Some(self)
776 }
777
778 #[inline]
780 pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
781 self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
782 Some(self)
783 }
784
785 #[inline]
787 pub const fn with_iso_week_number(mut self, value: NonZero<u8>) -> Option<Self> {
788 self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
789 Some(self)
790 }
791
792 #[inline]
794 pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
795 self.weekday = Some(value);
796 Some(self)
797 }
798
799 #[inline]
801 pub const fn with_ordinal(mut self, value: NonZero<u16>) -> Option<Self> {
802 self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
803 Some(self)
804 }
805
806 #[inline]
808 pub const fn with_day(mut self, value: NonZero<u8>) -> Option<Self> {
809 self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
810 Some(self)
811 }
812
813 #[inline]
815 pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
816 self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
817 Some(self)
818 }
819
820 #[inline]
822 pub const fn with_hour_12(mut self, value: NonZero<u8>) -> Option<Self> {
823 self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
824 Some(self)
825 }
826
827 #[inline]
829 pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
830 self.hour_12_is_pm = Some(value);
831 Some(self)
832 }
833
834 #[inline]
836 pub const fn with_minute(mut self, value: u8) -> Option<Self> {
837 self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
838 Some(self)
839 }
840
841 #[inline]
843 pub const fn with_second(mut self, value: u8) -> Option<Self> {
844 self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
845 Some(self)
846 }
847
848 #[inline]
850 pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
851 self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
852 Some(self)
853 }
854
855 #[inline]
857 pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
858 self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
859 Some(self)
860 }
861
862 #[doc(hidden)]
864 #[deprecated(
865 since = "0.3.8",
866 note = "use `parsed.with_offset_minute_signed()` instead"
867 )]
868 #[inline]
869 pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
870 if value > i8::MAX as u8 {
871 None
872 } else {
873 self.with_offset_minute_signed(value as i8)
874 }
875 }
876
877 #[inline]
879 pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
880 self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
881 Some(self)
882 }
883
884 #[doc(hidden)]
886 #[deprecated(
887 since = "0.3.8",
888 note = "use `parsed.with_offset_second_signed()` instead"
889 )]
890 #[inline]
891 pub const fn with_offset_second(self, value: u8) -> Option<Self> {
892 if value > i8::MAX as u8 {
893 None
894 } else {
895 self.with_offset_second_signed(value as i8)
896 }
897 }
898
899 #[inline]
901 pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
902 self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
903 Some(self)
904 }
905
906 #[inline]
908 pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
909 self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
910 Some(self)
911 }
912}
913
914impl TryFrom<Parsed> for Date {
915 type Error = error::TryFromParsed;
916
917 #[inline]
918 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
919 macro_rules! match_ {
921 (_ => $catch_all:expr $(,)?) => {
922 $catch_all
923 };
924 (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
925 if let ($(Some($name)),*) = ($(parsed.$name()),*) {
926 $arm
927 } else {
928 match_!($($rest)*)
929 }
930 };
931 }
932
933 #[inline]
936 const fn adjustment(year: i32) -> i16 {
937 match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
939 Weekday::Monday => 7,
940 Weekday::Tuesday => 1,
941 Weekday::Wednesday => 2,
942 Weekday::Thursday => 3,
943 Weekday::Friday => 4,
944 Weekday::Saturday => 5,
945 Weekday::Sunday => 6,
946 }
947 }
948
949 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
952 parsed.year(),
953 parsed.year_century(),
954 parsed.year_century_is_negative(),
955 parsed.year_last_two(),
956 ) {
957 let year = if is_negative {
958 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
959 } else {
960 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
961 };
962 parsed.year = OptionRangedI32::from(RangedI32::new(year));
963 }
964 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
965 parsed.iso_year(),
966 parsed.iso_year_century(),
967 parsed.iso_year_century_is_negative(),
968 parsed.iso_year_last_two(),
969 ) {
970 let iso_year = if is_negative {
971 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
972 } else {
973 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
974 };
975 parsed.iso_year = OptionRangedI32::from(RangedI32::new(iso_year));
976 }
977
978 match_! {
979 (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
980 (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
981 (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
982 iso_year,
983 iso_week_number.get(),
984 weekday,
985 )?),
986 (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
987 year,
988 (sunday_week_number.cast_signed().extend::<i16>() * 7
989 + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
990 - adjustment(year)
991 + 1).cast_unsigned(),
992 )?),
993 (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
994 year,
995 (monday_week_number.cast_signed().extend::<i16>() * 7
996 + weekday.number_days_from_monday().cast_signed().extend::<i16>()
997 - adjustment(year)
998 + 1).cast_unsigned(),
999 )?),
1000 _ => Err(InsufficientInformation),
1001 }
1002 }
1003}
1004
1005impl TryFrom<Parsed> for Time {
1006 type Error = error::TryFromParsed;
1007
1008 #[inline]
1009 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1010 let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
1011 (Some(hour), _, _) => hour,
1012 (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
1013 (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
1014 (_, Some(hour), Some(false)) => hour.get(),
1015 (_, Some(hour), Some(true)) => hour.get() + 12,
1016 _ => return Err(InsufficientInformation),
1017 };
1018
1019 if parsed.hour_24().is_none()
1020 && parsed.hour_12().is_some()
1021 && parsed.hour_12_is_pm().is_some()
1022 && parsed.minute().is_none()
1023 && parsed.second().is_none()
1024 && parsed.subsecond().is_none()
1025 {
1026 return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
1027 }
1028
1029 match (parsed.minute(), parsed.second(), parsed.subsecond()) {
1031 (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
1032 (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
1033 (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
1034 (Some(minute), Some(second), Some(subsecond)) => {
1035 Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
1036 }
1037 _ => Err(InsufficientInformation),
1038 }
1039 }
1040}
1041
1042#[inline]
1043fn utc_offset_try_from_parsed<const REQUIRED: bool>(
1044 parsed: Parsed,
1045) -> Result<UtcOffset, error::TryFromParsed> {
1046 let hour = match (REQUIRED, parsed.offset_hour()) {
1047 (true, None) => return Err(InsufficientInformation),
1049 (false, None) => return Ok(UtcOffset::UTC),
1052 (_, Some(hour)) => hour,
1054 };
1055 let minute = parsed.offset_minute_signed();
1056 let second = minute.and_then(|_| parsed.offset_second_signed());
1058
1059 let minute = minute.unwrap_or(0);
1060 let second = second.unwrap_or(0);
1061
1062 UtcOffset::from_hms(hour, minute, second).map_err(|mut err| {
1063 if err.name == "hours" {
1065 err.name = "offset hour";
1066 } else if err.name == "minutes" {
1067 err.name = "offset minute";
1068 } else if err.name == "seconds" {
1069 err.name = "offset second";
1070 }
1071 err.into()
1072 })
1073}
1074
1075impl TryFrom<Parsed> for UtcOffset {
1076 type Error = error::TryFromParsed;
1077
1078 #[inline]
1079 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1080 utc_offset_try_from_parsed::<true>(parsed)
1081 }
1082}
1083
1084impl TryFrom<Parsed> for PrimitiveDateTime {
1085 type Error = error::TryFromParsed;
1086
1087 #[inline]
1088 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1089 Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
1090 }
1091}
1092
1093impl TryFrom<Parsed> for UtcDateTime {
1094 type Error = error::TryFromParsed;
1095
1096 #[inline]
1097 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1098 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1099 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1100 if let Some(subsecond) = parsed.subsecond() {
1101 value = value.replace_nanosecond(subsecond)?;
1102 }
1103 return Ok(value);
1104 }
1105
1106 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1110 if parsed.set_second(59).is_none() {
1111 bug!("59 is a valid second");
1112 }
1113 if parsed.set_subsecond(999_999_999).is_none() {
1114 bug!("999_999_999 is a valid subsecond");
1115 }
1116 true
1117 } else {
1118 false
1119 };
1120
1121 let dt = OffsetDateTime::new_in_offset(
1122 Date::try_from(parsed)?,
1123 Time::try_from(parsed)?,
1124 utc_offset_try_from_parsed::<false>(parsed)?,
1125 )
1126 .to_utc();
1127
1128 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1129 return Err(error::TryFromParsed::ComponentRange(
1130 error::ComponentRange {
1131 name: "second",
1132 minimum: 0,
1133 maximum: 59,
1134 value: 60,
1135 conditional_message: Some("because leap seconds are not supported"),
1136 },
1137 ));
1138 }
1139 Ok(dt)
1140 }
1141}
1142
1143impl TryFrom<Parsed> for OffsetDateTime {
1144 type Error = error::TryFromParsed;
1145
1146 #[inline]
1147 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1148 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1149 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1150 if let Some(subsecond) = parsed.subsecond() {
1151 value = value.replace_nanosecond(subsecond)?;
1152 }
1153 return Ok(value);
1154 }
1155
1156 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1160 if parsed.set_second(59).is_none() {
1161 bug!("59 is a valid second");
1162 }
1163 if parsed.set_subsecond(999_999_999).is_none() {
1164 bug!("999_999_999 is a valid subsecond");
1165 }
1166 true
1167 } else {
1168 false
1169 };
1170
1171 let dt = Self::new_in_offset(
1172 Date::try_from(parsed)?,
1173 Time::try_from(parsed)?,
1174 UtcOffset::try_from(parsed)?,
1175 );
1176
1177 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1178 return Err(error::TryFromParsed::ComponentRange(
1179 error::ComponentRange {
1180 name: "second",
1181 minimum: 0,
1182 maximum: 59,
1183 value: 60,
1184 conditional_message: Some("because leap seconds are not supported"),
1185 },
1186 ));
1187 }
1188 Ok(dt)
1189 }
1190}