1use std::{
17 num::{NonZeroU64, ParseIntError},
18 ops::{Add, AddAssign, Sub, SubAssign},
19 str::FromStr,
20};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum ErrorSegment {
24 Year,
25 Month,
26 Day,
27 Hour,
28 Minute,
29 Second,
30 TimeZone,
31}
32
33impl std::fmt::Display for ErrorSegment {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 Self::Year => write!(f, "year"),
37 Self::Month => write!(f, "month"),
38 Self::Day => write!(f, "day"),
39 Self::Hour => write!(f, "hour"),
40 Self::Minute => write!(f, "minute"),
41 Self::Second => write!(f, "second"),
42 Self::TimeZone => write!(f, "timezone"),
43 }
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum ErrorKind {
49 NotEnoughDigits,
50 ParseIntError(ParseIntError),
51 TooLarge,
52 TooSmall,
53 OutOfRange,
54 InvalidFormat,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct DateTimeError {
59 segment: ErrorSegment,
60 kind: ErrorKind,
61}
62
63impl std::fmt::Display for DateTimeError {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 use ErrorKind::*;
66
67 match self.kind {
68 NotEnoughDigits => write!(f, "Not enough digits in the {}.", self.segment),
69 ParseIntError(ref err) => {
70 write!(f, "Failed to parse {} because '{err}'.", self.segment)
71 }
72 TooLarge => write!(f, "The {} is too large.", self.segment),
73 TooSmall => write!(f, "The {} is too small.", self.segment),
74 InvalidFormat => write!(f, "The format of {} is invalid.", self.segment),
75 OutOfRange => write!(
76 f,
77 "The value of {} is outside the range of the domain.",
78 self.segment
79 ),
80 }
81 }
82}
83
84impl std::error::Error for DateTimeError {}
85
86macro_rules! datetime_error {
87 ( $segment:ident, $kind:expr ) => {{
88 use ErrorKind::*;
89 use ErrorSegment::*;
90 DateTimeError {
91 segment: $segment,
92 kind: $kind,
93 }
94 }};
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
98pub struct TimeZone(i16);
99
100impl TimeZone {
101 const MIN: Self = Self(-14 * 60);
102 const MAX: Self = Self(14 * 60);
103 const UTC: Self = Self(0);
104}
105
106impl std::fmt::Display for TimeZone {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 if self.0 == 0 {
109 write!(f, "Z")
110 } else {
111 let h = self.0 / 60;
112 let m = self.0 % 60;
113 write!(f, "{:+02}:{:02}", h, m.abs())
114 }
115 }
116}
117
118impl FromStr for TimeZone {
119 type Err = DateTimeError;
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 if s == "Z" {
123 return Ok(Self(0));
124 }
125
126 let (hour, minute) = s
127 .split_once(':')
128 .ok_or(datetime_error!(TimeZone, InvalidFormat))?;
129 if !hour.starts_with(['+', '-'])
130 || hour.len() != 3
131 || minute.len() != 2
132 || minute.starts_with(['+', '-'])
133 {
134 return Err(datetime_error!(TimeZone, InvalidFormat))?;
135 }
136 let hour = hour
137 .parse::<i16>()
138 .map_err(|err| datetime_error!(TimeZone, ParseIntError(err)))?;
139 let minute = minute
140 .parse::<i16>()
141 .map_err(|err| datetime_error!(TimeZone, ParseIntError(err)))?;
142
143 if minute >= 60 {
144 return Err(datetime_error!(TimeZone, TooLarge));
145 }
146
147 if hour < -14 || (hour == -14 && minute != 0) {
148 Err(datetime_error!(TimeZone, TooSmall))
149 } else if hour > 14 || (hour == 14 && minute != 0) {
150 Err(datetime_error!(TimeZone, TooLarge))
151 } else {
152 Ok(Self(hour * 60 + minute * hour.signum()))
153 }
154 }
155}
156
157const NUM_OF_DAYS_IN_A_MONTH: [u8; 13] = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
160struct NaiveYear(i128);
161
162impl NaiveYear {
163 fn checked_add(&self, rhs: i128) -> Option<Self> {
164 let mut ret = self.0.checked_add(rhs)?;
165 if ret == 0 {
166 ret = 1;
167 }
168 Some(Self(ret))
169 }
170
171 fn checked_sub(&self, rhs: i128) -> Option<Self> {
172 let mut ret = self.0.checked_sub(rhs)?;
173 if ret == 0 {
174 ret = -1;
175 }
176 Some(Self(ret))
177 }
178}
179
180impl std::fmt::Display for NaiveYear {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 write!(f, "{:04}", self.0)
183 }
184}
185
186impl FromStr for NaiveYear {
187 type Err = DateTimeError;
188
189 fn from_str(s: &str) -> Result<Self, Self::Err> {
190 if s.starts_with('+') {
191 return Err(datetime_error!(Year, InvalidFormat));
192 }
193 if s.len() < 4 + s.starts_with('-') as usize {
194 return Err(datetime_error!(Year, NotEnoughDigits));
195 }
196
197 let ret = s
198 .parse()
199 .map(Self)
200 .map_err(|err| datetime_error!(Year, ParseIntError(err)))?;
201 if ret.0 == 0 {
202 return Err(datetime_error!(Year, OutOfRange));
206 }
207 Ok(ret)
208 }
209}
210
211macro_rules! impl_add_for_naive_year {
212 ( $( $t:ty ),* ) => {
213 $(
214 impl Add<$t> for NaiveYear {
215 type Output = NaiveYear;
216
217 fn add(self, rhs: $t) -> Self::Output {
218 let mut ret = self.0 + rhs as i128;
219 if ret == 0 {
220 ret += 1;
221 }
222 Self(ret)
223 }
224 }
225 )*
226 };
227}
228impl_add_for_naive_year!(i8, u8, i16, u16, i32, u32, i64, u64, i128);
229macro_rules! impl_sub_for_naive_year {
230 ( $( $t:ty ),* ) => {
231 $(
232 impl Sub<$t> for NaiveYear {
233 type Output = NaiveYear;
234
235 fn sub(self, rhs: $t) -> Self::Output {
236 let mut ret = self.0 - rhs as i128;
237 if ret == 0 {
238 ret -= 1;
239 }
240 Self(ret)
241 }
242 }
243 )*
244 };
245}
246impl_sub_for_naive_year!(i8, u8, i16, u16, i32, u32, i64, u64, i128);
247
248impl Default for NaiveYear {
249 fn default() -> Self {
250 Self(1)
251 }
252}
253
254#[derive(Debug, Clone, Copy)]
255pub struct GYear {
256 year: NaiveYear,
257 tz: Option<TimeZone>,
258}
259
260impl PartialOrd for GYear {
261 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
262 match (self.tz, other.tz) {
263 (Some(stz), Some(otz)) => match self.year.cmp(&other.year) {
264 std::cmp::Ordering::Equal => Some(stz.cmp(&otz)),
265 cmp => Some(cmp),
266 },
267 (None, None) => self.year.partial_cmp(&other.year),
268 _ => None,
269 }
270 }
271}
272
273impl std::fmt::Display for GYear {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 write!(f, "{}", self.year)?;
276 if let Some(tz) = self.tz.as_ref() {
277 write!(f, "{}", tz)?;
278 }
279 Ok(())
280 }
281}
282
283impl FromStr for GYear {
284 type Err = DateTimeError;
285
286 fn from_str(s: &str) -> Result<Self, Self::Err> {
287 let base = s.starts_with('-') as usize;
288 if let Some(sep) = s[base..].bytes().position(|b| !b.is_ascii_digit()) {
289 let (year, tz) = s.split_at(base + sep);
290 Ok(Self {
291 year: year.parse()?,
292 tz: Some(tz.parse()?),
293 })
294 } else {
295 Ok(Self {
296 year: s.parse()?,
297 tz: None,
298 })
299 }
300 }
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
304struct NaiveMonth(u8);
305
306impl std::fmt::Display for NaiveMonth {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 write!(f, "{:02}", self.0)
309 }
310}
311
312impl FromStr for NaiveMonth {
313 type Err = DateTimeError;
314
315 fn from_str(s: &str) -> Result<Self, Self::Err> {
316 let ret = s
317 .parse()
318 .map(Self)
319 .map_err(|err| datetime_error!(Month, ParseIntError(err)))?;
320 if s.starts_with('+') {
321 Err(datetime_error!(Month, InvalidFormat))
322 } else if s.len() < 2 {
323 Err(datetime_error!(Month, NotEnoughDigits))
324 } else if !(1..=12).contains(&ret.0) {
325 Err(datetime_error!(Month, OutOfRange))
326 } else {
327 Ok(ret)
328 }
329 }
330}
331
332impl Default for NaiveMonth {
333 fn default() -> Self {
334 Self(1)
335 }
336}
337
338#[derive(Debug, Clone, Copy)]
339pub struct GMonth {
340 month: NaiveMonth,
341 tz: Option<TimeZone>,
342}
343
344impl PartialOrd for GMonth {
345 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
346 match (self.tz, other.tz) {
347 (Some(stz), Some(otz)) => match self.month.cmp(&other.month) {
348 std::cmp::Ordering::Equal => Some(stz.cmp(&otz)),
349 cmp => Some(cmp),
350 },
351 (None, None) => self.month.partial_cmp(&other.month),
352 _ => None,
353 }
354 }
355}
356
357impl std::fmt::Display for GMonth {
358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359 write!(f, "{}", self.month)?;
360 if let Some(tz) = self.tz.as_ref() {
361 write!(f, "{}", tz)?;
362 }
363 Ok(())
364 }
365}
366
367impl FromStr for GMonth {
368 type Err = DateTimeError;
369
370 fn from_str(s: &str) -> Result<Self, Self::Err> {
371 let s = s
372 .strip_prefix("--")
373 .ok_or(datetime_error!(Year, InvalidFormat))?;
374 if let Some(sep) = s.bytes().position(|b| !b.is_ascii_digit()) {
375 let (month, tz) = s.split_at(sep);
376 Ok(Self {
377 month: month.parse()?,
378 tz: Some(tz.parse()?),
379 })
380 } else {
381 Ok(Self {
382 month: s.parse()?,
383 tz: None,
384 })
385 }
386 }
387}
388
389#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
390struct NaiveDay(u8);
391
392impl std::fmt::Display for NaiveDay {
393 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394 write!(f, "{:02}", self.0)
395 }
396}
397
398impl FromStr for NaiveDay {
399 type Err = DateTimeError;
400
401 fn from_str(s: &str) -> Result<Self, Self::Err> {
402 let ret = s
403 .parse()
404 .map(Self)
405 .map_err(|err| datetime_error!(Day, ParseIntError(err)))?;
406 if s.starts_with('+') {
407 Err(datetime_error!(Day, InvalidFormat))
408 } else if s.len() < 2 {
409 Err(datetime_error!(Day, NotEnoughDigits))
410 } else if !(1..=31).contains(&ret.0) {
411 Err(datetime_error!(Day, OutOfRange))
412 } else {
413 Ok(ret)
414 }
415 }
416}
417
418impl Default for NaiveDay {
419 fn default() -> Self {
420 Self(1)
421 }
422}
423
424#[derive(Debug, Clone, Copy)]
425pub struct GDay {
426 day: NaiveDay,
427 tz: Option<TimeZone>,
428}
429
430impl PartialOrd for GDay {
431 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
432 let year = NaiveYear(2015);
433 let month = NaiveMonth(5);
435 let time = NaiveTime {
436 hour: 0,
437 minute: 0,
438 nanosecond: 0,
439 };
440
441 DateTime {
442 datetime: NaiveDateTime {
443 date: NaiveDate {
444 year,
445 month,
446 day: self.day,
447 },
448 time,
449 },
450 tz: self.tz,
451 }
452 .partial_cmp(&DateTime {
453 datetime: NaiveDateTime {
454 date: NaiveDate {
455 year,
456 month,
457 day: other.day,
458 },
459 time,
460 },
461 tz: other.tz,
462 })
463 }
464}
465
466impl std::fmt::Display for GDay {
467 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468 write!(f, "{}", self.day)?;
469 if let Some(tz) = self.tz.as_ref() {
470 write!(f, "{}", tz)?;
471 }
472 Ok(())
473 }
474}
475
476impl FromStr for GDay {
477 type Err = DateTimeError;
478
479 fn from_str(s: &str) -> Result<Self, Self::Err> {
480 let s = s
481 .strip_prefix("---")
482 .ok_or(datetime_error!(Year, InvalidFormat))?;
483 if let Some(sep) = s.bytes().position(|b| !b.is_ascii_digit()) {
484 let (day, tz) = s.split_at(sep);
485 Ok(Self {
486 day: day.parse()?,
487 tz: Some(tz.parse()?),
488 })
489 } else {
490 Ok(Self {
491 day: s.parse()?,
492 tz: None,
493 })
494 }
495 }
496}
497
498#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
499struct NaiveYearMonth {
500 year: NaiveYear,
501 month: NaiveMonth,
502}
503
504impl std::fmt::Display for NaiveYearMonth {
505 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
506 write!(f, "{}-{}", self.year, self.month)
507 }
508}
509
510impl FromStr for NaiveYearMonth {
511 type Err = DateTimeError;
512
513 fn from_str(s: &str) -> Result<Self, Self::Err> {
514 let (year, month) = s
515 .rsplit_once('-')
516 .ok_or(datetime_error!(Year, InvalidFormat))?;
517 Ok(Self {
518 year: year.parse()?,
519 month: month.parse()?,
520 })
521 }
522}
523
524#[derive(Debug, Clone, Copy)]
525pub struct GYearMonth {
526 ym: NaiveYearMonth,
527 tz: Option<TimeZone>,
528}
529
530impl PartialOrd for GYearMonth {
531 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
532 match (self.tz, other.tz) {
533 (Some(stz), Some(otz)) => match self.ym.cmp(&other.ym) {
534 std::cmp::Ordering::Equal => Some(stz.cmp(&otz)),
535 cmp => Some(cmp),
536 },
537 (None, None) => self.ym.partial_cmp(&other.ym),
538 _ => None,
539 }
540 }
541}
542
543impl std::fmt::Display for GYearMonth {
544 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
545 write!(f, "{}", self.ym)?;
546 if let Some(tz) = self.tz.as_ref() {
547 write!(f, "{}", tz)?;
548 }
549 Ok(())
550 }
551}
552
553impl FromStr for GYearMonth {
554 type Err = DateTimeError;
555
556 fn from_str(s: &str) -> Result<Self, Self::Err> {
557 if s.len() < 7 {
558 Err(datetime_error!(Year, InvalidFormat))
560 } else if s.ends_with('Z') {
561 let (ym, tz) = s.split_at(s.len() - 1);
562 Ok(Self {
563 ym: ym.parse()?,
564 tz: Some(tz.parse()?),
565 })
566 } else if let (ym, tz) = s.split_at(s.len() - 6)
567 && let Ok(tz) = tz.parse()
568 && let Ok(ym) = ym.parse()
569 {
570 Ok(Self { ym, tz: Some(tz) })
571 } else {
572 Ok(Self {
573 ym: s.parse()?,
574 tz: None,
575 })
576 }
577 }
578}
579
580#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
581struct NaiveMonthDay {
582 month: NaiveMonth,
583 day: NaiveDay,
584}
585
586impl std::fmt::Display for NaiveMonthDay {
587 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
588 write!(f, "{}-{}", self.month, self.day)
589 }
590}
591
592impl FromStr for NaiveMonthDay {
593 type Err = DateTimeError;
594
595 fn from_str(s: &str) -> Result<Self, Self::Err> {
596 let (month, day) = s
597 .split_once('-')
598 .ok_or(datetime_error!(Month, InvalidFormat))?;
599 let ret = Self {
600 month: month.parse()?,
601 day: day.parse()?,
602 };
603 if NUM_OF_DAYS_IN_A_MONTH[ret.month.0 as usize] < ret.day.0 {
604 return Err(datetime_error!(Day, TooLarge));
605 }
606 Ok(ret)
607 }
608}
609
610#[derive(Debug, Clone, Copy)]
611pub struct GMonthDay {
612 md: NaiveMonthDay,
613 tz: Option<TimeZone>,
614}
615
616impl PartialOrd for GMonthDay {
617 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
618 let year = NaiveYear(2000);
620 let time = NaiveTime {
621 hour: 0,
622 minute: 0,
623 nanosecond: 0,
624 };
625
626 DateTime {
627 datetime: NaiveDateTime {
628 date: NaiveDate {
629 year,
630 month: self.md.month,
631 day: self.md.day,
632 },
633 time,
634 },
635 tz: self.tz,
636 }
637 .partial_cmp(&DateTime {
638 datetime: NaiveDateTime {
639 date: NaiveDate {
640 year,
641 month: other.md.month,
642 day: other.md.day,
643 },
644 time,
645 },
646 tz: other.tz,
647 })
648 }
649}
650
651impl std::fmt::Display for GMonthDay {
652 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
653 write!(f, "{}", self.md)?;
654 if let Some(tz) = self.tz.as_ref() {
655 write!(f, "{}", tz)?;
656 }
657 Ok(())
658 }
659}
660
661impl FromStr for GMonthDay {
662 type Err = DateTimeError;
663
664 fn from_str(s: &str) -> Result<Self, Self::Err> {
665 let s = s
666 .strip_prefix("--")
667 .ok_or(datetime_error!(Year, InvalidFormat))?;
668 if s.len() < 5 {
669 Err(datetime_error!(Month, InvalidFormat))
671 } else if let Some(md) = s.strip_suffix('Z') {
672 Ok(Self {
673 md: md.parse()?,
674 tz: Some("Z".parse()?),
675 })
676 } else if s.len() >= 6
677 && let (md, tz) = s.split_at(s.len() - 6)
678 && let Ok(tz) = tz.parse()
679 && let Ok(md) = md.parse()
680 {
681 Ok(Self { md, tz: Some(tz) })
682 } else {
683 Ok(Self {
684 md: s.parse()?,
685 tz: None,
686 })
687 }
688 }
689}
690
691#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
692struct NaiveDate {
693 year: NaiveYear,
694 month: NaiveMonth,
695 day: NaiveDay,
696}
697
698impl std::fmt::Display for NaiveDate {
699 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
700 write!(f, "{}-{}-{}", self.year, self.month, self.day)
701 }
702}
703
704impl FromStr for NaiveDate {
705 type Err = DateTimeError;
706
707 fn from_str(s: &str) -> Result<Self, Self::Err> {
708 let len = s.len();
709 if len < 10 {
710 return Err(datetime_error!(Year, InvalidFormat));
712 }
713 let (year, md) = s.split_at(len - 5);
714 let md = md.parse::<NaiveMonthDay>()?;
715 let year = year
716 .strip_suffix('-')
717 .ok_or(datetime_error!(Year, InvalidFormat))?;
718 let year = year.parse::<NaiveYear>()?;
719 if !is_leap(year) && md.month.0 == 2 && md.day.0 == 29 {
720 return Err(datetime_error!(Day, TooLarge));
721 }
722 Ok(Self {
723 year,
724 month: md.month,
725 day: md.day,
726 })
727 }
728}
729
730#[derive(Debug, Clone, Copy)]
731pub struct Date {
732 ymd: NaiveDate,
733 tz: Option<TimeZone>,
734}
735
736impl PartialOrd for Date {
737 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
738 let time = NaiveTime {
739 hour: 0,
740 minute: 0,
741 nanosecond: 0,
742 };
743 DateTime {
744 datetime: NaiveDateTime {
745 date: self.ymd,
746 time,
747 },
748 tz: self.tz,
749 }
750 .partial_cmp(&DateTime {
751 datetime: NaiveDateTime {
752 date: other.ymd,
753 time,
754 },
755 tz: other.tz,
756 })
757 }
758}
759
760impl std::fmt::Display for Date {
761 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
762 write!(f, "{}", self.ymd)?;
763 if let Some(tz) = self.tz.as_ref() {
764 write!(f, "{}", tz)?;
765 }
766 Ok(())
767 }
768}
769
770impl FromStr for Date {
771 type Err = DateTimeError;
772
773 fn from_str(s: &str) -> Result<Self, Self::Err> {
774 if s.len() < 10 {
775 Err(datetime_error!(Month, InvalidFormat))
777 } else if let Some(ymd) = s.strip_suffix('Z') {
778 Ok(Self {
779 ymd: ymd.parse()?,
780 tz: Some("Z".parse()?),
781 })
782 } else if let (ymd, tz) = s.split_at(s.len() - 6)
783 && let Ok(tz) = tz.parse()
784 && let Ok(ymd) = ymd.parse()
785 {
786 Ok(Self { ymd, tz: Some(tz) })
787 } else {
788 Ok(Self {
789 ymd: s.parse()?,
790 tz: None,
791 })
792 }
793 }
794}
795
796const INSERTED_LEAPSECONDS: &[(u16, u8, u8)] = include!("../resources/inserted-leapseconds.rs");
801const REMOVED_LEAPSECONDS: &[(u16, u8, u8)] = include!("../resources/removed-leapseconds.rs");
802
803#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
804struct NaiveTime {
805 hour: u8,
806 minute: u8,
807 nanosecond: u64,
809}
810
811impl std::fmt::Display for NaiveTime {
812 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
813 let second = self.nanosecond / NANOSECONDS_PER_SECOND;
814 let mut nano = self.nanosecond % NANOSECONDS_PER_SECOND;
815 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, second)?;
816 if nano != 0 {
817 while nano % 10 == 0 {
818 nano /= 10;
819 }
820 write!(f, ".{}", nano)?;
821 }
822 Ok(())
823 }
824}
825
826impl FromStr for NaiveTime {
827 type Err = DateTimeError;
828
829 fn from_str(s: &str) -> Result<Self, Self::Err> {
830 if s.len() < 8 {
831 return Err(datetime_error!(Hour, InvalidFormat));
833 }
834
835 macro_rules! parse_number {
836 ( $s:expr, $seg:ident, $max:expr ) => {{
837 let num = $s
838 .parse()
839 .map_err(|err| datetime_error!($seg, ParseIntError(err)))?;
840 if $s.starts_with('+') {
841 return Err(datetime_error!($seg, InvalidFormat));
842 }
843 if num > $max {
844 return Err(datetime_error!($seg, TooLarge));
845 }
846 num
847 }};
848 }
849
850 let (sh, ms) = s.split_at(2);
851 let hour = parse_number!(sh, Hour, 24);
852 let (sm, second) = ms
853 .strip_prefix(':')
854 .ok_or(datetime_error!(Hour, InvalidFormat))?
855 .split_at(2);
856 let minute = parse_number!(sm, Minute, 59);
857 let (ss, nano) = second
858 .strip_prefix(':')
859 .ok_or(datetime_error!(Minute, InvalidFormat))?
860 .split_at(2);
861 let second: u64 = parse_number!(ss, Second, 60);
862 let ret = if nano.is_empty() {
863 Self {
864 hour,
865 minute,
866 nanosecond: second * NANOSECONDS_PER_SECOND,
867 }
868 } else if nano.len() == 1 {
869 return Err(datetime_error!(Second, InvalidFormat));
872 } else {
873 let sn = nano
874 .strip_prefix('.')
875 .ok_or(datetime_error!(Second, InvalidFormat))?;
876 let mut nano: u64 = parse_number!(sn, Second, 999_999_999);
879 let base = 9 - sn.len();
880 nano *= 10u64.pow(base as u32);
881 Self {
882 hour,
883 minute,
884 nanosecond: second * NANOSECONDS_PER_SECOND + nano,
885 }
886 };
887
888 if ret.hour == 24 && (ret.minute != 0 || ret.nanosecond != 0) {
889 return Err(datetime_error!(Hour, OutOfRange));
890 }
891 if ret.nanosecond == 60 * NANOSECONDS_PER_SECOND && (ret.hour != 23 || ret.minute != 59) {
892 return Err(datetime_error!(Second, OutOfRange));
893 }
894
895 Ok(ret)
896 }
897}
898
899#[derive(Debug, Clone, Copy)]
900pub struct Time {
901 time: NaiveTime,
902 tz: Option<TimeZone>,
903}
904
905impl PartialOrd for Time {
906 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
907 let date = NaiveDate {
908 year: NaiveYear(2015),
909 month: NaiveMonth(5),
910 day: NaiveDay(15),
911 };
912
913 DateTime {
914 datetime: NaiveDateTime {
915 date,
916 time: self.time,
917 },
918 tz: self.tz,
919 }
920 .partial_cmp(&DateTime {
921 datetime: NaiveDateTime {
922 date,
923 time: other.time,
924 },
925 tz: other.tz,
926 })
927 }
928}
929
930impl std::fmt::Display for Time {
931 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
932 write!(f, "{}", self.time)?;
933 if let Some(tz) = self.tz.as_ref() {
934 write!(f, "{}", tz)?;
935 }
936 Ok(())
937 }
938}
939
940impl FromStr for Time {
941 type Err = DateTimeError;
942
943 fn from_str(s: &str) -> Result<Self, Self::Err> {
944 if let Some(time) = s.strip_suffix('Z') {
945 Ok(Self {
946 time: time.parse()?,
947 tz: Some("Z".parse()?),
948 })
949 } else if s.len() >= 14 && matches!(s.as_bytes()[s.len() - 6], b'+' | b'-') {
950 let (time, tz) = s.split_at(s.len() - 6);
952 Ok(Self {
953 time: time.parse()?,
954 tz: Some(tz.parse()?),
955 })
956 } else {
957 Ok(Self {
958 time: s.parse()?,
959 tz: None,
960 })
961 }
962 }
963}
964
965#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
966struct NaiveDateTime {
967 date: NaiveDate,
968 time: NaiveTime,
969}
970
971impl std::fmt::Display for NaiveDateTime {
972 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
973 write!(f, "{}T{}", self.date, self.time)
974 }
975}
976
977impl FromStr for NaiveDateTime {
978 type Err = DateTimeError;
979
980 fn from_str(s: &str) -> Result<Self, Self::Err> {
981 if s.len() < 19 {
982 return Err(datetime_error!(Year, InvalidFormat));
984 }
985
986 let (date, time) = s
987 .split_once('T')
988 .ok_or(datetime_error!(Year, InvalidFormat))?;
989 let ret = Self {
990 date: date.parse()?,
991 time: time.parse()?,
992 };
993
994 if ret.time.nanosecond == 60 * NANOSECONDS_PER_SECOND {
995 if let Ok(year) = u16::try_from(ret.date.year.0)
997 && INSERTED_LEAPSECONDS
998 .binary_search(&(year, ret.date.month.0, ret.date.day.0))
999 .is_err()
1000 {
1001 return Err(datetime_error!(Second, OutOfRange));
1002 }
1003 } else if ret.time.nanosecond == 59 * NANOSECONDS_PER_SECOND {
1004 if let Ok(year) = u16::try_from(ret.date.year.0)
1006 && REMOVED_LEAPSECONDS
1007 .binary_search(&(year, ret.date.month.0, ret.date.day.0))
1008 .is_ok()
1009 {
1010 return Err(datetime_error!(Second, OutOfRange));
1011 }
1012 }
1013
1014 Ok(ret)
1015 }
1016}
1017
1018#[derive(Debug, Clone, Copy)]
1019pub struct DateTime {
1020 datetime: NaiveDateTime,
1021 tz: Option<TimeZone>,
1022}
1023
1024impl DateTime {
1025 pub const unsafe fn new_unchecked(
1031 year: i128,
1032 month: u8,
1033 day: u8,
1034 hour: u8,
1035 minute: u8,
1036 nanosecond: u64,
1037 tz: Option<TimeZone>,
1038 ) -> Self {
1039 DateTime {
1040 datetime: NaiveDateTime {
1041 date: NaiveDate {
1042 year: NaiveYear(year),
1043 month: NaiveMonth(month),
1044 day: NaiveDay(day),
1045 },
1046 time: NaiveTime {
1047 hour,
1048 minute,
1049 nanosecond,
1050 },
1051 },
1052 tz,
1053 }
1054 }
1055
1056 pub fn checked_add(&self, rhs: Duration) -> Option<DateTime> {
1059 if rhs.neg {
1060 return self.checked_sub(Duration { neg: false, ..rhs });
1061 }
1062
1063 let mut ret = DateTime {
1064 datetime: NaiveDateTime::default(),
1065 tz: None,
1066 };
1067
1068 let temp = rhs
1073 .month
1074 .map(|m| m.get())
1075 .unwrap_or(0)
1076 .checked_add(self.datetime.date.month.0 as u64)?;
1077 ret.datetime.date.month.0 = ((temp - 1) % 12 + 1) as u8;
1078 let carry = (temp - 1) / 12;
1079
1080 ret.datetime.date.year = self
1083 .datetime
1084 .date
1085 .year
1086 .checked_add(rhs.year.map(|y| y.get()).unwrap_or(0) as i128)?
1087 .checked_add(carry as i128)?;
1088
1089 ret.tz = self.tz;
1092
1093 let temp = rhs
1098 .nanosecond
1099 .map(|n| n.get())
1100 .unwrap_or(0)
1101 .checked_add(self.datetime.time.nanosecond)?;
1102 ret.datetime.time.nanosecond = temp % (60 * NANOSECONDS_PER_SECOND);
1103 let carry = temp / (60 * NANOSECONDS_PER_SECOND);
1104
1105 let temp = rhs
1110 .minute
1111 .map(|m| m.get())
1112 .unwrap_or(0)
1113 .checked_add(self.datetime.time.minute as u64)?
1114 .checked_add(carry)?;
1115 ret.datetime.time.minute = (temp % 60) as u8;
1116 let carry = temp / 60;
1117
1118 let temp = rhs
1123 .hour
1124 .map(|h| h.get())
1125 .unwrap_or(0)
1126 .checked_add(self.datetime.time.hour as u64)?
1127 .checked_add(carry)?;
1128 ret.datetime.time.hour = (temp % 24) as u8;
1129 let carry = temp / 24;
1130
1131 let temp_days = self.datetime.date.day.0.clamp(
1139 1,
1140 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?,
1141 );
1142 let mut day = rhs
1144 .day
1145 .map(|d| d.get())
1146 .unwrap_or(0)
1147 .checked_add(temp_days as u64)?
1148 .checked_add(carry)?;
1149 const NUM_OF_DAYS_OVER_400YEARS: u64 = 365 * 400 + 100 - 4 + 1;
1166 ret.datetime.date.year = ret
1167 .datetime
1168 .date
1169 .year
1170 .checked_add((day / NUM_OF_DAYS_OVER_400YEARS) as i128)?;
1171 day %= NUM_OF_DAYS_OVER_400YEARS;
1172 const NUM_OF_DAYS_OVER_100YEARS: u64 = 365 * 100 + 25 - 1;
1174 ret.datetime.date.year = ret
1175 .datetime
1176 .date
1177 .year
1178 .checked_add((day / NUM_OF_DAYS_OVER_100YEARS) as i128)?;
1179 day %= NUM_OF_DAYS_OVER_100YEARS;
1180 const NUM_OF_DAYS_OVER_4YEARS: u64 = 365 * 4 + 1;
1182 ret.datetime.date.year = ret
1183 .datetime
1184 .date
1185 .year
1186 .checked_add((day / NUM_OF_DAYS_OVER_4YEARS) as i128)?;
1187 day %= NUM_OF_DAYS_OVER_4YEARS;
1188 loop {
1192 let temp = if day < 1 {
1193 let month = ret.datetime.date.month.0 as i8 - 1;
1194 day = day
1195 .checked_add(maximum_day_in_month_for(ret.datetime.date.year, month)? as u64)?;
1196 month
1197 } else if let max_days =
1198 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?
1199 as u64
1200 && day > max_days
1201 {
1202 day -= max_days;
1203 ret.datetime.date.month.0 as i8 + 1
1204 } else {
1205 break;
1206 };
1207
1208 ret.datetime.date.month.0 = (temp - 1).rem_euclid(12) as u8 + 1;
1209 ret.datetime.date.year = ret
1210 .datetime
1211 .date
1212 .year
1213 .checked_add((temp - 1).div_euclid(12) as i128)?;
1214 }
1215 ret.datetime.date.day.0 = day as u8;
1216
1217 Some(ret)
1218 }
1219
1220 pub fn checked_sub(&self, rhs: Duration) -> Option<DateTime> {
1223 if rhs.neg {
1224 return self.checked_add(Duration { neg: false, ..rhs });
1225 }
1226
1227 let mut ret = DateTime {
1228 datetime: NaiveDateTime::default(),
1229 tz: None,
1230 };
1231
1232 let temp = (self.datetime.date.month.0 as i128)
1237 .checked_sub(rhs.month.map(|m| m.get() as i128).unwrap_or(0))?;
1238 ret.datetime.date.month.0 = temp.checked_sub(1)?.rem_euclid(12) as u8 + 1;
1239 let carry = (temp - 1).div_euclid(12);
1240
1241 ret.datetime.date.year = self
1244 .datetime
1245 .date
1246 .year
1247 .checked_sub(rhs.year.map(|y| y.get()).unwrap_or(0) as i128)?
1248 .checked_add(carry)?;
1249
1250 ret.tz = self.tz;
1253
1254 let temp = (self.datetime.time.nanosecond as i128)
1259 .checked_sub(rhs.nanosecond.map(|n| n.get()).unwrap_or(0) as i128)?;
1260 ret.datetime.time.nanosecond =
1261 temp.rem_euclid((60 * NANOSECONDS_PER_SECOND) as i128) as u64;
1262 let carry = temp.div_euclid((60 * NANOSECONDS_PER_SECOND) as i128);
1263
1264 let temp = (self.datetime.time.minute as i128)
1269 .checked_sub(rhs.minute.map(|m| m.get()).unwrap_or(0) as i128)?
1270 .checked_add(carry)?;
1271 ret.datetime.time.minute = temp.rem_euclid(60) as u8;
1272 let carry = temp.div_euclid(60);
1273
1274 let temp = (self.datetime.time.hour as i128)
1279 .checked_sub(rhs.hour.map(|h| h.get()).unwrap_or(0) as i128)?
1280 .checked_add(carry)?;
1281 ret.datetime.time.hour = temp.rem_euclid(24) as u8;
1282 let carry = temp.div_euclid(24);
1283
1284 let temp_days = self.datetime.date.day.0.clamp(
1292 1,
1293 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?,
1294 );
1295 let mut day = (temp_days as i128)
1297 .checked_sub(rhs.day.map(|d| d.get()).unwrap_or(0) as i128)?
1298 .checked_add(carry)?;
1299 const NUM_OF_DAYS_OVER_400YEARS: i128 = 365 * 400 + 100 - 4 + 1;
1316 ret.datetime.date.year = ret
1317 .datetime
1318 .date
1319 .year
1320 .checked_add(day / NUM_OF_DAYS_OVER_400YEARS)?;
1321 day %= NUM_OF_DAYS_OVER_400YEARS;
1322 const NUM_OF_DAYS_OVER_100YEARS: i128 = 365 * 100 + 25 - 1;
1324 ret.datetime.date.year = ret
1325 .datetime
1326 .date
1327 .year
1328 .checked_add(day / NUM_OF_DAYS_OVER_100YEARS)?;
1329 day %= NUM_OF_DAYS_OVER_100YEARS;
1330 const NUM_OF_DAYS_OVER_4YEARS: i128 = 365 * 4 + 1;
1332 ret.datetime.date.year = ret
1333 .datetime
1334 .date
1335 .year
1336 .checked_add(day / NUM_OF_DAYS_OVER_4YEARS)?;
1337 day %= NUM_OF_DAYS_OVER_4YEARS;
1338 loop {
1342 let temp = if day < 1 {
1343 let month = ret.datetime.date.month.0 as i8 - 1;
1344 day = day
1345 .checked_add(maximum_day_in_month_for(ret.datetime.date.year, month)? as i128)?;
1346 month
1347 } else if let max_days =
1348 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?
1349 as i128
1350 && day > max_days
1351 {
1352 day -= max_days;
1353 ret.datetime.date.month.0 as i8 + 1
1354 } else {
1355 break;
1356 };
1357
1358 ret.datetime.date.month.0 = (temp - 1).rem_euclid(12) as u8 + 1;
1359 ret.datetime.date.year = ret
1360 .datetime
1361 .date
1362 .year
1363 .checked_add((temp - 1).div_euclid(12) as i128)?;
1364 }
1365 ret.datetime.date.day.0 = day as u8;
1366
1367 Some(ret)
1368 }
1369
1370 pub fn to_utc(&self) -> DateTime {
1372 let Some(tz) = self.tz else {
1373 return *self;
1374 };
1375
1376 if tz.0 == 0 {
1378 return *self;
1379 }
1380
1381 let h = tz.0 / 60;
1382 let m = tz.0 % 60;
1383 let duration = Duration {
1384 neg: tz.0 > 0,
1385 hour: NonZeroU64::new(h.unsigned_abs() as u64),
1386 minute: NonZeroU64::new(m.unsigned_abs() as u64),
1387 ..Default::default()
1388 };
1389
1390 DateTime {
1391 tz: Some(TimeZone(0)),
1392 ..*self + duration
1393 }
1394 }
1395}
1396
1397impl PartialOrd for DateTime {
1398 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1401 let lhs = self.to_utc();
1402 let rhs = other.to_utc();
1403
1404 if lhs.tz.is_some() == rhs.tz.is_some() {
1405 Some(lhs.datetime.cmp(&rhs.datetime))
1406 } else if lhs.tz.is_some() {
1407 let min = DateTime {
1408 datetime: rhs.datetime,
1409 tz: Some(TimeZone::MAX),
1410 };
1411 let max = DateTime {
1412 datetime: rhs.datetime,
1413 tz: Some(TimeZone::MIN),
1414 };
1415 if lhs < min {
1416 Some(std::cmp::Ordering::Less)
1417 } else if max < lhs {
1418 Some(std::cmp::Ordering::Greater)
1419 } else {
1420 None
1421 }
1422 } else {
1423 let min = DateTime {
1424 datetime: lhs.datetime,
1425 tz: Some(TimeZone::MAX),
1426 };
1427 let max = DateTime {
1428 datetime: lhs.datetime,
1429 tz: Some(TimeZone::MIN),
1430 };
1431 if max < rhs {
1432 Some(std::cmp::Ordering::Less)
1433 } else if rhs < min {
1434 Some(std::cmp::Ordering::Greater)
1435 } else {
1436 None
1437 }
1438 }
1439 }
1440}
1441
1442impl std::fmt::Display for DateTime {
1443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1444 write!(f, "{}", self.datetime)?;
1445 if let Some(tz) = self.tz.as_ref() {
1446 write!(f, "{}", tz)?;
1447 }
1448 Ok(())
1449 }
1450}
1451
1452impl FromStr for DateTime {
1453 type Err = DateTimeError;
1454
1455 fn from_str(s: &str) -> Result<Self, Self::Err> {
1456 if let Some(datetime) = s.strip_suffix('Z') {
1457 Ok(Self {
1458 datetime: datetime.parse()?,
1459 tz: Some("Z".parse()?),
1460 })
1461 } else if s.len() >= 25 && matches!(s.as_bytes()[s.len() - 6], b'+' | b'-') {
1462 let (datetime, tz) = s.split_at(s.len() - 6);
1464 Ok(Self {
1465 datetime: datetime.parse()?,
1466 tz: Some(tz.parse()?),
1467 })
1468 } else {
1469 Ok(Self {
1470 datetime: s.parse()?,
1471 tz: None,
1472 })
1473 }
1474 }
1475}
1476
1477impl Add<Duration> for DateTime {
1478 type Output = DateTime;
1479
1480 fn add(self, rhs: Duration) -> Self::Output {
1481 self.checked_add(rhs).expect("attempt to add with overflow")
1482 }
1483}
1484
1485impl AddAssign<Duration> for DateTime {
1486 fn add_assign(&mut self, rhs: Duration) {
1487 *self = *self + rhs;
1488 }
1489}
1490
1491impl Sub<Duration> for DateTime {
1492 type Output = DateTime;
1493
1494 fn sub(self, rhs: Duration) -> Self::Output {
1495 self.checked_sub(rhs)
1496 .expect("attempt to subtract with overflow")
1497 }
1498}
1499
1500impl SubAssign<Duration> for DateTime {
1501 fn sub_assign(&mut self, rhs: Duration) {
1502 *self = *self - rhs;
1503 }
1504}
1505
1506const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;
1507
1508#[derive(Debug, Clone, Copy, Default)]
1509pub struct Duration {
1510 neg: bool,
1511 year: Option<NonZeroU64>,
1512 month: Option<NonZeroU64>,
1513 day: Option<NonZeroU64>,
1514 hour: Option<NonZeroU64>,
1515 minute: Option<NonZeroU64>,
1516 nanosecond: Option<NonZeroU64>,
1517}
1518
1519impl PartialOrd for Duration {
1520 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1523 const BASELINE: [DateTime; 4] = unsafe {
1532 [
1536 DateTime::new_unchecked(1696, 9, 1, 0, 0, 0, Some(TimeZone::UTC)),
1537 DateTime::new_unchecked(1697, 2, 1, 0, 0, 0, Some(TimeZone::UTC)),
1538 DateTime::new_unchecked(1903, 3, 1, 0, 0, 0, Some(TimeZone::UTC)),
1539 DateTime::new_unchecked(1903, 7, 1, 0, 0, 0, Some(TimeZone::UTC)),
1540 ]
1541 };
1542
1543 let ret0 = (BASELINE[0] + *self).partial_cmp(&(BASELINE[0] + *other))?;
1544 let ret1 = (BASELINE[1] + *self).partial_cmp(&(BASELINE[1] + *other))?;
1545 let ret2 = (BASELINE[2] + *self).partial_cmp(&(BASELINE[2] + *other))?;
1546 let ret3 = (BASELINE[3] + *self).partial_cmp(&(BASELINE[3] + *other))?;
1547 (ret0 == ret1 && ret0 == ret2 && ret0 == ret3).then_some(ret0)
1548 }
1549}
1550
1551impl std::fmt::Display for Duration {
1552 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1553 if self.neg {
1554 write!(f, "-")?;
1555 }
1556 write!(f, "P")?;
1557 if self.year.is_none()
1558 && self.month.is_none()
1559 && self.day.is_none()
1560 && self.hour.is_none()
1561 && self.minute.is_none()
1562 && self.nanosecond.is_none()
1563 {
1564 write!(f, "0Y")?;
1565 return Ok(());
1566 }
1567 if let Some(year) = self.year {
1568 write!(f, "{}Y", year)?;
1569 }
1570 if let Some(month) = self.month {
1571 write!(f, "{}M", month)?;
1572 }
1573 if let Some(day) = self.day {
1574 write!(f, "{}D", day)?;
1575 }
1576
1577 match (self.hour, self.minute, self.nanosecond) {
1578 (None, None, None) => {}
1579 (hour, minute, nanosecond) => {
1580 write!(f, "T")?;
1581 if let Some(hour) = hour {
1582 write!(f, "{}H", hour)?;
1583 }
1584 if let Some(minute) = minute {
1585 write!(f, "{}M", minute)?;
1586 }
1587 if let Some(nanosecond) = nanosecond {
1588 let sec = nanosecond.get() / NANOSECONDS_PER_SECOND;
1589 let mut nano = nanosecond.get() % NANOSECONDS_PER_SECOND;
1590 if nano == 0 {
1591 write!(f, "{}S", sec)?;
1592 } else {
1593 while nano % 10 == 0 {
1594 nano /= 10;
1595 }
1596 write!(f, "{}.{}S", sec, nano)?;
1597 }
1598 }
1599 }
1600 }
1601
1602 Ok(())
1603 }
1604}
1605
1606impl FromStr for Duration {
1607 type Err = DateTimeError;
1608
1609 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
1610 let mut neg = false;
1611 if let Some(rem) = s.strip_prefix('-') {
1612 neg = true;
1613 s = rem;
1614 }
1615
1616 s = s
1617 .strip_prefix('P')
1618 .ok_or(datetime_error!(Year, InvalidFormat))?;
1619 if s.is_empty() {
1620 return Err(datetime_error!(Year, InvalidFormat))?;
1621 }
1622
1623 let mut year = None;
1624 let mut month = None;
1625 let mut day = None;
1626 let mut hour = None;
1627 let mut minute = None;
1628 let mut nanosecond = None;
1629
1630 macro_rules! parse_number {
1631 ( $s:expr, $ind:literal, $seg:ident, $res:expr ) => {
1632 if let Some((seg, rem)) = $s.split_once($ind) {
1633 let seg = seg
1634 .parse::<u64>()
1635 .map_err(|err| datetime_error!($seg, ParseIntError(err)))?;
1636 $res = NonZeroU64::new(seg);
1637 $s = rem;
1638 }
1639 };
1640 }
1641 parse_number!(s, 'Y', Year, year);
1642 parse_number!(s, 'M', Month, month);
1643 parse_number!(s, 'D', Day, day);
1644 if let Some(rem) = s.strip_prefix('T') {
1645 s = rem;
1646 if s.is_empty() {
1647 return Err(datetime_error!(Hour, InvalidFormat));
1648 }
1649 parse_number!(s, 'H', Hour, hour);
1650 parse_number!(s, 'M', Minute, minute);
1651 if let Some((sec, rem)) = s.split_once('S') {
1652 s = rem;
1653
1654 if let Some((sec, frac)) = sec.split_once('.') {
1655 let sec = sec
1656 .parse::<u64>()
1657 .map_err(|err| datetime_error!(Second, ParseIntError(err)))?
1658 .checked_mul(NANOSECONDS_PER_SECOND)
1659 .ok_or(datetime_error!(Second, TooLarge))?;
1660 let base = 10u64.pow(9 - frac.len() as u32);
1661 let frac = frac
1662 .parse::<u64>()
1663 .map_err(|err| datetime_error!(Second, ParseIntError(err)))?
1664 .checked_mul(base)
1665 .ok_or(datetime_error!(Second, TooLarge))?;
1666 let nano = sec
1667 .checked_add(frac)
1668 .ok_or(datetime_error!(Second, TooLarge))?;
1669 nanosecond = NonZeroU64::new(nano);
1670 } else {
1671 let sec = sec
1672 .parse::<u64>()
1673 .map_err(|err| datetime_error!(Second, ParseIntError(err)))?;
1674 let nano = sec
1675 .checked_mul(NANOSECONDS_PER_SECOND)
1676 .ok_or(datetime_error!(Second, TooLarge))?;
1677 nanosecond = NonZeroU64::new(nano);
1678 }
1679 }
1680 }
1681
1682 if !s.is_empty() {
1683 return Err(datetime_error!(Second, InvalidFormat));
1684 }
1685
1686 Ok(Self {
1687 neg,
1688 year,
1689 month,
1690 day,
1691 hour,
1692 minute,
1693 nanosecond,
1694 })
1695 }
1696}
1697
1698fn is_leap(year: NaiveYear) -> bool {
1699 year.0 % 400 == 0 || (year.0 % 100 != 0 && year.0 % 4 == 0)
1700}
1701
1702macro_rules! impl_partial_eq_for_datetime_objects {
1703 ( $( $t:ty ),* ) => {
1704 $(
1705 impl PartialEq for $t {
1706 fn eq(&self, other: &Self) -> bool {
1707 self.partial_cmp(other).is_some_and(|ret| ret.is_eq())
1708 }
1709 }
1710 )*
1711 };
1712}
1713impl_partial_eq_for_datetime_objects!(
1714 Duration, DateTime, Time, Date, GYearMonth, GYear, GMonthDay, GDay, GMonth
1715);
1716
1717fn maximum_day_in_month_for(year: NaiveYear, month: i8) -> Option<u8> {
1722 let m = (month - 1).rem_euclid(12) + 1;
1723 let mut y = year.0.checked_add((month - 1).div_euclid(12) as i128)?;
1724 if y == 0 {
1725 y = 1;
1726 }
1727 if m == 2 {
1728 Some(28 + is_leap(NaiveYear(y)) as u8)
1729 } else {
1730 Some(NUM_OF_DAYS_IN_A_MONTH[m as usize])
1731 }
1732}
1733
1734#[cfg(test)]
1735mod tests {
1736 use super::*;
1737
1738 #[test]
1739 fn gyear_parse_test() {
1740 assert!("1999".parse::<GYear>().is_ok());
1741 assert!("-1999".parse::<GYear>().is_ok());
1742 assert!("1999Z".parse::<GYear>().is_ok());
1743 assert!("-1999Z".parse::<GYear>().is_ok());
1744 assert!("1999+09:00".parse::<GYear>().is_ok());
1745 assert!("1999-09:00".parse::<GYear>().is_ok());
1746 assert!("-1999+09:00".parse::<GYear>().is_ok());
1747 assert!("-1999-09:00".parse::<GYear>().is_ok());
1748 assert!("-1999+14:00".parse::<GYear>().is_ok());
1750 assert!("-1999-14:00".parse::<GYear>().is_ok());
1752 assert!("1999+09:59".parse::<GYear>().is_ok());
1754
1755 assert!("231999".parse::<GYear>().is_ok());
1756 assert!("-231999".parse::<GYear>().is_ok());
1757 assert!("231999+09:00".parse::<GYear>().is_ok());
1758 assert!("231999-09:00".parse::<GYear>().is_ok());
1759 assert!("-231999+09:00".parse::<GYear>().is_ok());
1760 assert!("-231999-09:00".parse::<GYear>().is_ok());
1761
1762 assert!("0000".parse::<GYear>().is_err());
1764 assert!("0000+09:00".parse::<GYear>().is_err());
1765 assert!(
1767 "1000000000000000000000000000000000000000"
1768 .parse::<GYear>()
1769 .is_err()
1770 );
1771 assert!(
1773 "-1000000000000000000000000000000000000000"
1774 .parse::<GYear>()
1775 .is_err()
1776 );
1777 assert!("1999+12:60".parse::<GYear>().is_err());
1779 assert!("1999+12:000".parse::<GYear>().is_err());
1780 assert!("1999+012:00".parse::<GYear>().is_err());
1781 assert!("+1999".parse::<GYear>().is_err());
1783 assert!("+1999+09:00".parse::<GYear>().is_err());
1784 assert!("1999+15:00".parse::<GYear>().is_err());
1786 assert!("1999-15:00".parse::<GYear>().is_err());
1788 assert!("1999+14:01".parse::<GYear>().is_err());
1790 assert!("1999-14:01".parse::<GYear>().is_err());
1792 }
1793
1794 #[test]
1795 fn gmonth_parse_test() {
1796 assert!("--12".parse::<GMonth>().is_ok());
1797 assert!("--12Z".parse::<GMonth>().is_ok());
1798 assert!("--12+09:00".parse::<GMonth>().is_ok());
1799 assert!("--12-09:00".parse::<GMonth>().is_ok());
1800
1801 assert!("--00".parse::<GMonth>().is_err());
1803 assert!("--00+09:00".parse::<GMonth>().is_err());
1804 assert!("--13".parse::<GMonth>().is_err());
1806 assert!("--12+12:60".parse::<GMonth>().is_err());
1808 assert!("--12+12:000".parse::<GMonth>().is_err());
1809 assert!("--12+012:00".parse::<GMonth>().is_err());
1810 assert!("--+12+09:00".parse::<GMonth>().is_err());
1812 assert!("---12+09:00".parse::<GMonth>().is_err());
1814 }
1815
1816 #[test]
1817 fn gday_parse_test() {
1818 assert!("---08".parse::<GDay>().is_ok());
1819 assert!("---08Z".parse::<GDay>().is_ok());
1820 assert!("---08+09:00".parse::<GDay>().is_ok());
1821 assert!("---08-09:00".parse::<GDay>().is_ok());
1822
1823 assert!("---00".parse::<GDay>().is_err());
1825 assert!("---00+09:00".parse::<GDay>().is_err());
1826 assert!("---32".parse::<GDay>().is_err());
1828 assert!("---12+12:60".parse::<GDay>().is_err());
1830 assert!("---12+12:000".parse::<GDay>().is_err());
1831 assert!("---12+012:00".parse::<GDay>().is_err());
1832 assert!("---+12+09:00".parse::<GDay>().is_err());
1834 assert!("----12+09:00".parse::<GDay>().is_err());
1836 }
1837
1838 #[test]
1839 fn gyearmonth_parse_test() {
1840 assert!("2015-05".parse::<GYearMonth>().is_ok());
1841 assert!("2015-05Z".parse::<GYearMonth>().is_ok());
1842 assert!("2015-05+09:00".parse::<GYearMonth>().is_ok());
1843 assert!("2015-05-09:00".parse::<GYearMonth>().is_ok());
1844 assert!("-0660-02+09:00".parse::<GYearMonth>().is_ok());
1845 assert!("-0660-02-09:00".parse::<GYearMonth>().is_ok());
1846
1847 assert!("2015-05+12:60".parse::<GYearMonth>().is_err());
1849 assert!("2015-05+12:000".parse::<GYearMonth>().is_err());
1850 assert!("2025-05+012:00".parse::<GYearMonth>().is_err());
1851 assert!("+2015-05+09:00".parse::<GYearMonth>().is_err());
1853 }
1854
1855 #[test]
1856 fn gmonthday_parse_test() {
1857 assert!("--05-15".parse::<GMonthDay>().is_ok());
1858 assert!("--05-15Z".parse::<GMonthDay>().is_ok());
1859 assert!("--05-15+09:00".parse::<GMonthDay>().is_ok());
1860 assert!("--05-15-09:00".parse::<GMonthDay>().is_ok());
1861 assert!("--02-11+09:00".parse::<GMonthDay>().is_ok());
1862 assert!("--02-11-09:00".parse::<GMonthDay>().is_ok());
1863 assert!("--01-31".parse::<GMonthDay>().is_ok());
1865 assert!("--02-29".parse::<GMonthDay>().is_ok());
1866 assert!("--03-31".parse::<GMonthDay>().is_ok());
1867 assert!("--04-30".parse::<GMonthDay>().is_ok());
1868 assert!("--05-31".parse::<GMonthDay>().is_ok());
1869 assert!("--06-30".parse::<GMonthDay>().is_ok());
1870 assert!("--07-31".parse::<GMonthDay>().is_ok());
1871 assert!("--08-31".parse::<GMonthDay>().is_ok());
1872 assert!("--09-30".parse::<GMonthDay>().is_ok());
1873 assert!("--10-31".parse::<GMonthDay>().is_ok());
1874 assert!("--11-30".parse::<GMonthDay>().is_ok());
1875 assert!("--12-31".parse::<GMonthDay>().is_ok());
1876
1877 assert!("--05-15+12:60".parse::<GMonthDay>().is_err());
1879 assert!("--05-15+12:000".parse::<GMonthDay>().is_err());
1880 assert!("--05-15+012:00".parse::<GMonthDay>().is_err());
1881 assert!("--+05-15+09:00".parse::<GMonthDay>().is_err());
1883 assert!("--01-32".parse::<GMonthDay>().is_err());
1885 assert!("--02-30".parse::<GMonthDay>().is_err());
1886 assert!("--03-32".parse::<GMonthDay>().is_err());
1887 assert!("--04-31".parse::<GMonthDay>().is_err());
1888 assert!("--05-32".parse::<GMonthDay>().is_err());
1889 assert!("--06-31".parse::<GMonthDay>().is_err());
1890 assert!("--07-32".parse::<GMonthDay>().is_err());
1891 assert!("--08-32".parse::<GMonthDay>().is_err());
1892 assert!("--09-31".parse::<GMonthDay>().is_err());
1893 assert!("--10-32".parse::<GMonthDay>().is_err());
1894 assert!("--11-31".parse::<GMonthDay>().is_err());
1895 assert!("--12-32".parse::<GMonthDay>().is_err());
1896 }
1897
1898 #[test]
1899 fn date_parse_test() {
1900 assert!("2015-05-15".parse::<Date>().is_ok());
1901 assert!("2015-05-15Z".parse::<Date>().is_ok());
1902 assert!("2015-05-15+09:00".parse::<Date>().is_ok());
1903 assert!("2015-05-15-09:00".parse::<Date>().is_ok());
1904 assert!("-0660-02-11+09:00".parse::<Date>().is_ok());
1905 assert!("-0660-02-11-09:00".parse::<Date>().is_ok());
1906 assert!("2024-02-29".parse::<Date>().is_ok());
1908 assert!("2000-02-29".parse::<Date>().is_ok());
1909
1910 assert!("2015-05-15+12:60".parse::<Date>().is_err());
1912 assert!("2015-05-15+12:000".parse::<Date>().is_err());
1913 assert!("2015-05-15+012:00".parse::<Date>().is_err());
1914 assert!("+2015-05-15+09:00".parse::<Date>().is_err());
1916 assert!("2015-02-29".parse::<Date>().is_err());
1918 assert!("1900-02-29".parse::<Date>().is_err());
1919 }
1920
1921 #[test]
1922 fn time_parse_test() {
1923 assert!("00:00:00".parse::<Time>().is_ok());
1924 assert!("12:00:00".parse::<Time>().is_ok());
1925 assert!("24:00:00".parse::<Time>().is_ok());
1927 assert!("23:59:60".parse::<Time>().is_ok());
1929 assert!("12:30:00Z".parse::<Time>().is_ok());
1930 assert!("12:00:30+09:00".parse::<Time>().is_ok());
1931 assert!("12:15:15-09:00".parse::<Time>().is_ok());
1932
1933 assert!("25:00:00".parse::<Time>().is_err());
1934 assert!("12:60:00".parse::<Time>().is_err());
1935 assert!("09:00:60".parse::<Time>().is_err());
1936 assert!("9:00:00".parse::<Time>().is_err());
1937 assert!("+09:00:00".parse::<Time>().is_err());
1938 assert!("-09:00:00".parse::<Time>().is_err());
1939 assert!("+9:00:00".parse::<Time>().is_err());
1940 assert!("-9:00:00".parse::<Time>().is_err());
1941 assert!("09:+0:00".parse::<Time>().is_err());
1942 assert!("09:-0:00".parse::<Time>().is_err());
1943 assert!("09:00:+0".parse::<Time>().is_err());
1944 assert!("09:00:-0".parse::<Time>().is_err());
1945 assert!("09:00:00++1:00".parse::<Time>().is_err());
1946 assert!("09:00:00+-1:00".parse::<Time>().is_err());
1947 assert!("09:00:00-+1:00".parse::<Time>().is_err());
1948 assert!("09:00:00--1:00".parse::<Time>().is_err());
1949 assert!("09:00:00+01:+0".parse::<Time>().is_err());
1950 assert!("09:00:00+01:-0".parse::<Time>().is_err());
1951 assert!("+09:00:10".parse::<Time>().is_err());
1953 assert!("-09:00:10".parse::<Time>().is_err());
1955 assert!("23:00:60".parse::<Time>().is_err());
1957 assert!("24:00:01".parse::<Time>().is_err());
1959 }
1960
1961 #[test]
1962 fn datetime_parse_test() {
1963 assert!("2000-01-20T12:00:00-13:00".parse::<DateTime>().is_ok());
1964 assert!("2000-01-20T12:00:00Z".parse::<DateTime>().is_ok());
1965 assert!("2000-01-12T12:13:14Z".parse::<DateTime>().is_ok());
1966 assert!("-0660-02-11T00:00:00+09:00".parse::<DateTime>().is_ok());
1967
1968 assert!("+2000-01-20T12:00:00-13:00".parse::<DateTime>().is_err());
1969 assert!("2000-01-20t12:00:00-13:00".parse::<DateTime>().is_err());
1970 assert!("2000-+1-20T12:00:00-13:00".parse::<DateTime>().is_err());
1971 assert!("+000-01-20T12:00:00-13:00".parse::<DateTime>().is_err());
1972 assert!("0000-01-20T12:00:00-13:00".parse::<DateTime>().is_err());
1973 assert!("2000-01-2012:00:00-13:00".parse::<DateTime>().is_err());
1974 assert!("2000001-20T12:00:00-13:00".parse::<DateTime>().is_err());
1975 assert!("2000-01-20T-12:00".parse::<DateTime>().is_err());
1976 }
1977
1978 #[test]
1979 fn duration_parse_test() {
1980 assert!("P1347Y".parse::<Duration>().is_ok());
1981 assert!("P1347M".parse::<Duration>().is_ok());
1982 assert!("P1Y2MT2H".parse::<Duration>().is_ok());
1983 assert!("P0Y1347M".parse::<Duration>().is_ok());
1984 assert!("P0Y1347M0D".parse::<Duration>().is_ok());
1985 assert!("-P1347M".parse::<Duration>().is_ok());
1986
1987 assert!("P-1347M".parse::<Duration>().is_err());
1988 assert!("P1Y2MT".parse::<Duration>().is_err());
1989 }
1990
1991 #[test]
1992 fn datetime_addition_test() {
1993 let datetime = "2000-01-12T12:13:14Z".parse::<DateTime>().unwrap();
1994 let duration = "P1Y3M5DT7H10M3.3S".parse::<Duration>().unwrap();
1995 let ret = datetime + duration;
1996 assert_eq!(ret.to_string(), "2001-04-17T19:23:17.3Z");
1997
1998 let datetime = "2000-01-01T00:00:00Z".parse::<DateTime>().unwrap();
1999 let duration = "-P3M".parse::<Duration>().unwrap();
2000 let ret = datetime + duration;
2001 assert_eq!(ret.to_string(), "1999-10-01T00:00:00Z");
2002
2003 let datetime = "2000-01-12T00:00:00Z".parse::<DateTime>().unwrap();
2004 let duration = "PT33H".parse::<Duration>().unwrap();
2005 let ret = datetime + duration;
2006 assert_eq!(ret.to_string(), "2000-01-13T09:00:00Z");
2007
2008 let datetime = "2000-03-04T23:00:00+03:00".parse::<DateTime>().unwrap();
2009 let utc = datetime.to_utc();
2010 assert_eq!(utc.to_string(), "2000-03-04T20:00:00Z");
2011 }
2012
2013 #[test]
2014 fn datetime_comparison_test() {
2015 assert!(
2017 "2000-01-15T00:00:00".parse::<DateTime>().unwrap()
2018 < "2000-02-15T00:00:00".parse::<DateTime>().unwrap()
2019 );
2020 assert!(
2021 "2000-01-15T12:00:00".parse::<DateTime>().unwrap()
2022 < "2000-01-16T12:00:00Z".parse::<DateTime>().unwrap()
2023 );
2024
2025 assert!(
2027 "2000-01-01T12:00:00"
2028 .parse::<DateTime>()
2029 .unwrap()
2030 .partial_cmp(&"1999-12-31T23:00:00Z".parse().unwrap())
2031 .is_none()
2032 );
2033 assert!(
2034 "2000-01-16T12:00:00"
2035 .parse::<DateTime>()
2036 .unwrap()
2037 .partial_cmp(&"2000-01-16T12:00:00Z".parse().unwrap())
2038 .is_none()
2039 );
2040 assert!(
2041 "2000-01-16T00:00:00"
2042 .parse::<DateTime>()
2043 .unwrap()
2044 .partial_cmp(&"2000-01-16T12:00:00Z".parse().unwrap())
2045 .is_none()
2046 );
2047 }
2048
2049 #[test]
2050 fn duration_comparison_test() {
2051 let p1y = "P1Y".parse::<Duration>().unwrap();
2052 assert!(p1y > "P364D".parse().unwrap());
2053 assert!(p1y < "P367D".parse().unwrap());
2054 assert!(p1y.partial_cmp(&"P365D".parse().unwrap()).is_none());
2055 assert!(p1y.partial_cmp(&"P366D".parse().unwrap()).is_none());
2056
2057 let p1m = "P1M".parse::<Duration>().unwrap();
2058 assert!(p1m > "P27D".parse().unwrap());
2059 assert!(p1m < "P32D".parse().unwrap());
2060 assert!(p1m.partial_cmp(&"P28D".parse().unwrap()).is_none());
2061 assert!(p1m.partial_cmp(&"P29D".parse().unwrap()).is_none());
2062 assert!(p1m.partial_cmp(&"P30D".parse().unwrap()).is_none());
2063 assert!(p1m.partial_cmp(&"P31D".parse().unwrap()).is_none());
2064
2065 let p5m = "P5M".parse::<Duration>().unwrap();
2066 assert!(p5m > "P149D".parse().unwrap());
2067 assert!(p5m < "P154D".parse().unwrap());
2068 assert!(p5m.partial_cmp(&"P150D".parse().unwrap()).is_none());
2069 assert!(p5m.partial_cmp(&"P151D".parse().unwrap()).is_none());
2070 assert!(p5m.partial_cmp(&"P152D".parse().unwrap()).is_none());
2071 assert!(p5m.partial_cmp(&"P153D".parse().unwrap()).is_none());
2072 }
2073}