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 if let Some(sep) = s.bytes().position(|b| !b.is_ascii_digit()) {
372 let (month, tz) = s.split_at(sep);
373 Ok(Self {
374 month: month.parse()?,
375 tz: Some(tz.parse()?),
376 })
377 } else {
378 Ok(Self {
379 month: s.parse()?,
380 tz: None,
381 })
382 }
383 }
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
387struct NaiveDay(u8);
388
389impl std::fmt::Display for NaiveDay {
390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 write!(f, "{:02}", self.0)
392 }
393}
394
395impl FromStr for NaiveDay {
396 type Err = DateTimeError;
397
398 fn from_str(s: &str) -> Result<Self, Self::Err> {
399 let ret = s
400 .parse()
401 .map(Self)
402 .map_err(|err| datetime_error!(Day, ParseIntError(err)))?;
403 if s.starts_with('+') {
404 Err(datetime_error!(Day, InvalidFormat))
405 } else if s.len() < 2 {
406 Err(datetime_error!(Day, NotEnoughDigits))
407 } else if !(1..=31).contains(&ret.0) {
408 Err(datetime_error!(Day, OutOfRange))
409 } else {
410 Ok(ret)
411 }
412 }
413}
414
415impl Default for NaiveDay {
416 fn default() -> Self {
417 Self(1)
418 }
419}
420
421#[derive(Debug, Clone, Copy)]
422pub struct GDay {
423 day: NaiveDay,
424 tz: Option<TimeZone>,
425}
426
427impl PartialOrd for GDay {
428 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
429 let year = NaiveYear(2015);
430 let month = NaiveMonth(5);
432 let time = NaiveTime {
433 hour: 0,
434 minute: 0,
435 nanosecond: 0,
436 };
437
438 DateTime {
439 datetime: NaiveDateTime {
440 date: NaiveDate {
441 year,
442 month,
443 day: self.day,
444 },
445 time,
446 },
447 tz: self.tz,
448 }
449 .partial_cmp(&DateTime {
450 datetime: NaiveDateTime {
451 date: NaiveDate {
452 year,
453 month,
454 day: other.day,
455 },
456 time,
457 },
458 tz: other.tz,
459 })
460 }
461}
462
463impl std::fmt::Display for GDay {
464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
465 write!(f, "{}", self.day)?;
466 if let Some(tz) = self.tz.as_ref() {
467 write!(f, "{}", tz)?;
468 }
469 Ok(())
470 }
471}
472
473impl FromStr for GDay {
474 type Err = DateTimeError;
475
476 fn from_str(s: &str) -> Result<Self, Self::Err> {
477 if let Some(sep) = s.bytes().position(|b| !b.is_ascii_digit()) {
478 let (day, tz) = s.split_at(sep);
479 Ok(Self {
480 day: day.parse()?,
481 tz: Some(tz.parse()?),
482 })
483 } else {
484 Ok(Self {
485 day: s.parse()?,
486 tz: None,
487 })
488 }
489 }
490}
491
492#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
493struct NaiveYearMonth {
494 year: NaiveYear,
495 month: NaiveMonth,
496}
497
498impl std::fmt::Display for NaiveYearMonth {
499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500 write!(f, "{}-{}", self.year, self.month)
501 }
502}
503
504impl FromStr for NaiveYearMonth {
505 type Err = DateTimeError;
506
507 fn from_str(s: &str) -> Result<Self, Self::Err> {
508 let (year, month) = s
509 .rsplit_once('-')
510 .ok_or(datetime_error!(Year, InvalidFormat))?;
511 Ok(Self {
512 year: year.parse()?,
513 month: month.parse()?,
514 })
515 }
516}
517
518#[derive(Debug, Clone, Copy)]
519pub struct GYearMonth {
520 ym: NaiveYearMonth,
521 tz: Option<TimeZone>,
522}
523
524impl PartialOrd for GYearMonth {
525 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
526 match (self.tz, other.tz) {
527 (Some(stz), Some(otz)) => match self.ym.cmp(&other.ym) {
528 std::cmp::Ordering::Equal => Some(stz.cmp(&otz)),
529 cmp => Some(cmp),
530 },
531 (None, None) => self.ym.partial_cmp(&other.ym),
532 _ => None,
533 }
534 }
535}
536
537impl std::fmt::Display for GYearMonth {
538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539 write!(f, "{}", self.ym)?;
540 if let Some(tz) = self.tz.as_ref() {
541 write!(f, "{}", tz)?;
542 }
543 Ok(())
544 }
545}
546
547impl FromStr for GYearMonth {
548 type Err = DateTimeError;
549
550 fn from_str(s: &str) -> Result<Self, Self::Err> {
551 if s.len() < 7 {
552 Err(datetime_error!(Year, InvalidFormat))
554 } else if s.ends_with('Z') {
555 let (ym, tz) = s.split_at(s.len() - 1);
556 Ok(Self {
557 ym: ym.parse()?,
558 tz: Some(tz.parse()?),
559 })
560 } else if let (ym, tz) = s.split_at(s.len() - 6)
561 && let Ok(tz) = tz.parse()
562 && let Ok(ym) = ym.parse()
563 {
564 Ok(Self { ym, tz: Some(tz) })
565 } else {
566 Ok(Self {
567 ym: s.parse()?,
568 tz: None,
569 })
570 }
571 }
572}
573
574#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
575struct NaiveMonthDay {
576 month: NaiveMonth,
577 day: NaiveDay,
578}
579
580impl std::fmt::Display for NaiveMonthDay {
581 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
582 write!(f, "{}-{}", self.month, self.day)
583 }
584}
585
586impl FromStr for NaiveMonthDay {
587 type Err = DateTimeError;
588
589 fn from_str(s: &str) -> Result<Self, Self::Err> {
590 let (month, day) = s
591 .split_once('-')
592 .ok_or(datetime_error!(Month, InvalidFormat))?;
593 let ret = Self {
594 month: month.parse()?,
595 day: day.parse()?,
596 };
597 if NUM_OF_DAYS_IN_A_MONTH[ret.month.0 as usize] < ret.day.0 {
598 return Err(datetime_error!(Day, TooLarge));
599 }
600 Ok(ret)
601 }
602}
603
604#[derive(Debug, Clone, Copy)]
605pub struct GMonthDay {
606 md: NaiveMonthDay,
607 tz: Option<TimeZone>,
608}
609
610impl PartialOrd for GMonthDay {
611 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
612 let year = NaiveYear(2000);
614 let time = NaiveTime {
615 hour: 0,
616 minute: 0,
617 nanosecond: 0,
618 };
619
620 DateTime {
621 datetime: NaiveDateTime {
622 date: NaiveDate {
623 year,
624 month: self.md.month,
625 day: self.md.day,
626 },
627 time,
628 },
629 tz: self.tz,
630 }
631 .partial_cmp(&DateTime {
632 datetime: NaiveDateTime {
633 date: NaiveDate {
634 year,
635 month: other.md.month,
636 day: other.md.day,
637 },
638 time,
639 },
640 tz: other.tz,
641 })
642 }
643}
644
645impl std::fmt::Display for GMonthDay {
646 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
647 write!(f, "{}", self.md)?;
648 if let Some(tz) = self.tz.as_ref() {
649 write!(f, "{}", tz)?;
650 }
651 Ok(())
652 }
653}
654
655impl FromStr for GMonthDay {
656 type Err = DateTimeError;
657
658 fn from_str(s: &str) -> Result<Self, Self::Err> {
659 if s.len() < 5 {
660 Err(datetime_error!(Month, InvalidFormat))
662 } else if let Some(md) = s.strip_suffix('Z') {
663 Ok(Self {
664 md: md.parse()?,
665 tz: Some("Z".parse()?),
666 })
667 } else if s.len() >= 6
668 && let (md, tz) = s.split_at(s.len() - 6)
669 && let Ok(tz) = tz.parse()
670 && let Ok(md) = md.parse()
671 {
672 Ok(Self { md, tz: Some(tz) })
673 } else {
674 Ok(Self {
675 md: s.parse()?,
676 tz: None,
677 })
678 }
679 }
680}
681
682#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
683struct NaiveDate {
684 year: NaiveYear,
685 month: NaiveMonth,
686 day: NaiveDay,
687}
688
689impl std::fmt::Display for NaiveDate {
690 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
691 write!(f, "{}-{}-{}", self.year, self.month, self.day)
692 }
693}
694
695impl FromStr for NaiveDate {
696 type Err = DateTimeError;
697
698 fn from_str(s: &str) -> Result<Self, Self::Err> {
699 let len = s.len();
700 if len < 10 {
701 return Err(datetime_error!(Year, InvalidFormat));
703 }
704 let (year, md) = s.split_at(len - 5);
705 let md = md.parse::<NaiveMonthDay>()?;
706 let year = year
707 .strip_suffix('-')
708 .ok_or(datetime_error!(Year, InvalidFormat))?;
709 let year = year.parse::<NaiveYear>()?;
710 if !is_leap(year) && md.month.0 == 2 && md.day.0 == 29 {
711 return Err(datetime_error!(Day, TooLarge));
712 }
713 Ok(Self {
714 year,
715 month: md.month,
716 day: md.day,
717 })
718 }
719}
720
721#[derive(Debug, Clone, Copy)]
722pub struct Date {
723 ymd: NaiveDate,
724 tz: Option<TimeZone>,
725}
726
727impl PartialOrd for Date {
728 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
729 let time = NaiveTime {
730 hour: 0,
731 minute: 0,
732 nanosecond: 0,
733 };
734 DateTime {
735 datetime: NaiveDateTime {
736 date: self.ymd,
737 time,
738 },
739 tz: self.tz,
740 }
741 .partial_cmp(&DateTime {
742 datetime: NaiveDateTime {
743 date: other.ymd,
744 time,
745 },
746 tz: other.tz,
747 })
748 }
749}
750
751impl std::fmt::Display for Date {
752 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
753 write!(f, "{}", self.ymd)?;
754 if let Some(tz) = self.tz.as_ref() {
755 write!(f, "{}", tz)?;
756 }
757 Ok(())
758 }
759}
760
761impl FromStr for Date {
762 type Err = DateTimeError;
763
764 fn from_str(s: &str) -> Result<Self, Self::Err> {
765 if s.len() < 10 {
766 Err(datetime_error!(Month, InvalidFormat))
768 } else if let Some(ymd) = s.strip_suffix('Z') {
769 Ok(Self {
770 ymd: ymd.parse()?,
771 tz: Some("Z".parse()?),
772 })
773 } else if let (ymd, tz) = s.split_at(s.len() - 6)
774 && let Ok(tz) = tz.parse()
775 && let Ok(ymd) = ymd.parse()
776 {
777 Ok(Self { ymd, tz: Some(tz) })
778 } else {
779 Ok(Self {
780 ymd: s.parse()?,
781 tz: None,
782 })
783 }
784 }
785}
786
787const INSERTED_LEAPSECONDS: &[(u16, u8, u8)] = include!("../resources/inserted-leapseconds.rs");
792const REMOVED_LEAPSECONDS: &[(u16, u8, u8)] = include!("../resources/removed-leapseconds.rs");
793
794#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
795struct NaiveTime {
796 hour: u8,
797 minute: u8,
798 nanosecond: u64,
800}
801
802impl std::fmt::Display for NaiveTime {
803 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
804 let second = self.nanosecond / NANOSECONDS_PER_SECOND;
805 let mut nano = self.nanosecond % NANOSECONDS_PER_SECOND;
806 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, second)?;
807 if nano != 0 {
808 while nano % 10 == 0 {
809 nano /= 10;
810 }
811 write!(f, ".{}", nano)?;
812 }
813 Ok(())
814 }
815}
816
817impl FromStr for NaiveTime {
818 type Err = DateTimeError;
819
820 fn from_str(s: &str) -> Result<Self, Self::Err> {
821 if s.len() < 8 {
822 return Err(datetime_error!(Hour, InvalidFormat));
824 }
825
826 macro_rules! parse_number {
827 ( $s:expr, $seg:ident, $max:expr ) => {{
828 let num = $s
829 .parse()
830 .map_err(|err| datetime_error!($seg, ParseIntError(err)))?;
831 if $s.starts_with('+') {
832 return Err(datetime_error!($seg, InvalidFormat));
833 }
834 if num > $max {
835 return Err(datetime_error!($seg, TooLarge));
836 }
837 num
838 }};
839 }
840
841 let (sh, ms) = s.split_at(2);
842 let hour = parse_number!(sh, Hour, 24);
843 let (sm, second) = ms
844 .strip_prefix(':')
845 .ok_or(datetime_error!(Hour, InvalidFormat))?
846 .split_at(2);
847 let minute = parse_number!(sm, Minute, 59);
848 let (ss, nano) = second
849 .strip_prefix(':')
850 .ok_or(datetime_error!(Minute, InvalidFormat))?
851 .split_at(2);
852 let second: u64 = parse_number!(ss, Second, 60);
853 let ret = if nano.is_empty() {
854 Self {
855 hour,
856 minute,
857 nanosecond: second * NANOSECONDS_PER_SECOND,
858 }
859 } else if nano.len() == 1 {
860 return Err(datetime_error!(Second, InvalidFormat));
863 } else {
864 let sn = nano
865 .strip_prefix('.')
866 .ok_or(datetime_error!(Second, InvalidFormat))?;
867 let mut nano: u64 = parse_number!(sn, Second, 999_999_999);
870 let base = 9 - sn.len();
871 nano *= 10u64.pow(base as u32);
872 Self {
873 hour,
874 minute,
875 nanosecond: second * NANOSECONDS_PER_SECOND + nano,
876 }
877 };
878
879 if ret.hour == 24 && (ret.minute != 0 || ret.nanosecond != 0) {
880 return Err(datetime_error!(Hour, OutOfRange));
881 }
882 if ret.nanosecond == 60 * NANOSECONDS_PER_SECOND && (ret.hour != 23 || ret.minute != 59) {
883 return Err(datetime_error!(Second, OutOfRange));
884 }
885
886 Ok(ret)
887 }
888}
889
890#[derive(Debug, Clone, Copy)]
891pub struct Time {
892 time: NaiveTime,
893 tz: Option<TimeZone>,
894}
895
896impl PartialOrd for Time {
897 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
898 let date = NaiveDate {
899 year: NaiveYear(2015),
900 month: NaiveMonth(5),
901 day: NaiveDay(15),
902 };
903
904 DateTime {
905 datetime: NaiveDateTime {
906 date,
907 time: self.time,
908 },
909 tz: self.tz,
910 }
911 .partial_cmp(&DateTime {
912 datetime: NaiveDateTime {
913 date,
914 time: other.time,
915 },
916 tz: other.tz,
917 })
918 }
919}
920
921impl std::fmt::Display for Time {
922 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
923 write!(f, "{}", self.time)?;
924 if let Some(tz) = self.tz.as_ref() {
925 write!(f, "{}", tz)?;
926 }
927 Ok(())
928 }
929}
930
931impl FromStr for Time {
932 type Err = DateTimeError;
933
934 fn from_str(s: &str) -> Result<Self, Self::Err> {
935 if let Some(time) = s.strip_suffix('Z') {
936 Ok(Self {
937 time: time.parse()?,
938 tz: Some("Z".parse()?),
939 })
940 } else if s.len() >= 14 && matches!(s.as_bytes()[s.len() - 6], b'+' | b'-') {
941 let (time, tz) = s.split_at(s.len() - 6);
943 Ok(Self {
944 time: time.parse()?,
945 tz: Some(tz.parse()?),
946 })
947 } else {
948 Ok(Self {
949 time: s.parse()?,
950 tz: None,
951 })
952 }
953 }
954}
955
956#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
957struct NaiveDateTime {
958 date: NaiveDate,
959 time: NaiveTime,
960}
961
962impl std::fmt::Display for NaiveDateTime {
963 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
964 write!(f, "{}T{}", self.date, self.time)
965 }
966}
967
968impl FromStr for NaiveDateTime {
969 type Err = DateTimeError;
970
971 fn from_str(s: &str) -> Result<Self, Self::Err> {
972 if s.len() < 19 {
973 return Err(datetime_error!(Year, InvalidFormat));
975 }
976
977 let (date, time) = s
978 .split_once('T')
979 .ok_or(datetime_error!(Year, InvalidFormat))?;
980 let ret = Self {
981 date: date.parse()?,
982 time: time.parse()?,
983 };
984
985 if ret.time.nanosecond == 60 * NANOSECONDS_PER_SECOND {
986 if let Ok(year) = u16::try_from(ret.date.year.0)
988 && INSERTED_LEAPSECONDS
989 .binary_search(&(year, ret.date.month.0, ret.date.day.0))
990 .is_err()
991 {
992 return Err(datetime_error!(Second, OutOfRange));
993 }
994 } else if ret.time.nanosecond == 59 * NANOSECONDS_PER_SECOND {
995 if let Ok(year) = u16::try_from(ret.date.year.0)
997 && REMOVED_LEAPSECONDS
998 .binary_search(&(year, ret.date.month.0, ret.date.day.0))
999 .is_ok()
1000 {
1001 return Err(datetime_error!(Second, OutOfRange));
1002 }
1003 }
1004
1005 Ok(ret)
1006 }
1007}
1008
1009#[derive(Debug, Clone, Copy)]
1010pub struct DateTime {
1011 datetime: NaiveDateTime,
1012 tz: Option<TimeZone>,
1013}
1014
1015impl DateTime {
1016 pub const unsafe fn new_unchecked(
1022 year: i128,
1023 month: u8,
1024 day: u8,
1025 hour: u8,
1026 minute: u8,
1027 nanosecond: u64,
1028 tz: Option<TimeZone>,
1029 ) -> Self {
1030 DateTime {
1031 datetime: NaiveDateTime {
1032 date: NaiveDate {
1033 year: NaiveYear(year),
1034 month: NaiveMonth(month),
1035 day: NaiveDay(day),
1036 },
1037 time: NaiveTime {
1038 hour,
1039 minute,
1040 nanosecond,
1041 },
1042 },
1043 tz,
1044 }
1045 }
1046
1047 pub fn checked_add(&self, rhs: Duration) -> Option<DateTime> {
1050 if rhs.neg {
1051 return self.checked_sub(Duration { neg: false, ..rhs });
1052 }
1053
1054 let mut ret = DateTime {
1055 datetime: NaiveDateTime::default(),
1056 tz: None,
1057 };
1058
1059 let temp = rhs
1064 .month
1065 .map(|m| m.get())
1066 .unwrap_or(0)
1067 .checked_add(self.datetime.date.month.0 as u64)?;
1068 ret.datetime.date.month.0 = ((temp - 1) % 12 + 1) as u8;
1069 let carry = (temp - 1) / 12;
1070
1071 ret.datetime.date.year = self
1074 .datetime
1075 .date
1076 .year
1077 .checked_add(rhs.year.map(|y| y.get()).unwrap_or(0) as i128)?
1078 .checked_add(carry as i128)?;
1079
1080 ret.tz = self.tz;
1083
1084 let temp = rhs
1089 .nanosecond
1090 .map(|n| n.get())
1091 .unwrap_or(0)
1092 .checked_add(self.datetime.time.nanosecond)?;
1093 ret.datetime.time.nanosecond = temp % (60 * NANOSECONDS_PER_SECOND);
1094 let carry = temp / (60 * NANOSECONDS_PER_SECOND);
1095
1096 let temp = rhs
1101 .minute
1102 .map(|m| m.get())
1103 .unwrap_or(0)
1104 .checked_add(self.datetime.time.minute as u64)?
1105 .checked_add(carry)?;
1106 ret.datetime.time.minute = (temp % 60) as u8;
1107 let carry = temp / 60;
1108
1109 let temp = rhs
1114 .hour
1115 .map(|h| h.get())
1116 .unwrap_or(0)
1117 .checked_add(self.datetime.time.hour as u64)?
1118 .checked_add(carry)?;
1119 ret.datetime.time.hour = (temp % 24) as u8;
1120 let carry = temp / 24;
1121
1122 let temp_days = self.datetime.date.day.0.clamp(
1130 1,
1131 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?,
1132 );
1133 let mut day = rhs
1135 .day
1136 .map(|d| d.get())
1137 .unwrap_or(0)
1138 .checked_add(temp_days as u64)?
1139 .checked_add(carry)?;
1140 const NUM_OF_DAYS_OVER_400YEARS: u64 = 365 * 400 + 100 - 4 + 1;
1157 ret.datetime.date.year = ret
1158 .datetime
1159 .date
1160 .year
1161 .checked_add((day / NUM_OF_DAYS_OVER_400YEARS) as i128)?;
1162 day %= NUM_OF_DAYS_OVER_400YEARS;
1163 const NUM_OF_DAYS_OVER_100YEARS: u64 = 365 * 100 + 25 - 1;
1165 ret.datetime.date.year = ret
1166 .datetime
1167 .date
1168 .year
1169 .checked_add((day / NUM_OF_DAYS_OVER_100YEARS) as i128)?;
1170 day %= NUM_OF_DAYS_OVER_100YEARS;
1171 const NUM_OF_DAYS_OVER_4YEARS: u64 = 365 * 4 + 1;
1173 ret.datetime.date.year = ret
1174 .datetime
1175 .date
1176 .year
1177 .checked_add((day / NUM_OF_DAYS_OVER_4YEARS) as i128)?;
1178 day %= NUM_OF_DAYS_OVER_4YEARS;
1179 loop {
1183 let temp = if day < 1 {
1184 let month = ret.datetime.date.month.0 as i8 - 1;
1185 day = day
1186 .checked_add(maximum_day_in_month_for(ret.datetime.date.year, month)? as u64)?;
1187 month
1188 } else if let max_days =
1189 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?
1190 as u64
1191 && day > max_days
1192 {
1193 day -= max_days;
1194 ret.datetime.date.month.0 as i8 + 1
1195 } else {
1196 break;
1197 };
1198
1199 ret.datetime.date.month.0 = (temp - 1).rem_euclid(12) as u8 + 1;
1200 ret.datetime.date.year = ret
1201 .datetime
1202 .date
1203 .year
1204 .checked_add((temp - 1).div_euclid(12) as i128)?;
1205 }
1206 ret.datetime.date.day.0 = day as u8;
1207
1208 Some(ret)
1209 }
1210
1211 pub fn checked_sub(&self, rhs: Duration) -> Option<DateTime> {
1214 if rhs.neg {
1215 return self.checked_add(Duration { neg: false, ..rhs });
1216 }
1217
1218 let mut ret = DateTime {
1219 datetime: NaiveDateTime::default(),
1220 tz: None,
1221 };
1222
1223 let temp = (self.datetime.date.month.0 as i128)
1228 .checked_sub(rhs.month.map(|m| m.get() as i128).unwrap_or(0))?;
1229 ret.datetime.date.month.0 = temp.checked_sub(1)?.rem_euclid(12) as u8 + 1;
1230 let carry = (temp - 1).div_euclid(12);
1231
1232 ret.datetime.date.year = self
1235 .datetime
1236 .date
1237 .year
1238 .checked_sub(rhs.year.map(|y| y.get()).unwrap_or(0) as i128)?
1239 .checked_add(carry)?;
1240
1241 ret.tz = self.tz;
1244
1245 let temp = (self.datetime.time.nanosecond as i128)
1250 .checked_sub(rhs.nanosecond.map(|n| n.get()).unwrap_or(0) as i128)?;
1251 ret.datetime.time.nanosecond =
1252 temp.rem_euclid((60 * NANOSECONDS_PER_SECOND) as i128) as u64;
1253 let carry = temp.div_euclid((60 * NANOSECONDS_PER_SECOND) as i128);
1254
1255 let temp = (self.datetime.time.minute as i128)
1260 .checked_sub(rhs.minute.map(|m| m.get()).unwrap_or(0) as i128)?
1261 .checked_add(carry)?;
1262 ret.datetime.time.minute = temp.rem_euclid(60) as u8;
1263 let carry = temp.div_euclid(60);
1264
1265 let temp = (self.datetime.time.hour as i128)
1270 .checked_sub(rhs.hour.map(|h| h.get()).unwrap_or(0) as i128)?
1271 .checked_add(carry)?;
1272 ret.datetime.time.hour = temp.rem_euclid(24) as u8;
1273 let carry = temp.div_euclid(24);
1274
1275 let temp_days = self.datetime.date.day.0.clamp(
1283 1,
1284 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?,
1285 );
1286 let mut day = (temp_days as i128)
1288 .checked_sub(rhs.day.map(|d| d.get()).unwrap_or(0) as i128)?
1289 .checked_add(carry)?;
1290 const NUM_OF_DAYS_OVER_400YEARS: i128 = 365 * 400 + 100 - 4 + 1;
1307 ret.datetime.date.year = ret
1308 .datetime
1309 .date
1310 .year
1311 .checked_add(day / NUM_OF_DAYS_OVER_400YEARS)?;
1312 day %= NUM_OF_DAYS_OVER_400YEARS;
1313 const NUM_OF_DAYS_OVER_100YEARS: i128 = 365 * 100 + 25 - 1;
1315 ret.datetime.date.year = ret
1316 .datetime
1317 .date
1318 .year
1319 .checked_add(day / NUM_OF_DAYS_OVER_100YEARS)?;
1320 day %= NUM_OF_DAYS_OVER_100YEARS;
1321 const NUM_OF_DAYS_OVER_4YEARS: i128 = 365 * 4 + 1;
1323 ret.datetime.date.year = ret
1324 .datetime
1325 .date
1326 .year
1327 .checked_add(day / NUM_OF_DAYS_OVER_4YEARS)?;
1328 day %= NUM_OF_DAYS_OVER_4YEARS;
1329 loop {
1333 let temp = if day < 1 {
1334 let month = ret.datetime.date.month.0 as i8 - 1;
1335 day = day
1336 .checked_add(maximum_day_in_month_for(ret.datetime.date.year, month)? as i128)?;
1337 month
1338 } else if let max_days =
1339 maximum_day_in_month_for(ret.datetime.date.year, ret.datetime.date.month.0 as i8)?
1340 as i128
1341 && day > max_days
1342 {
1343 day -= max_days;
1344 ret.datetime.date.month.0 as i8 + 1
1345 } else {
1346 break;
1347 };
1348
1349 ret.datetime.date.month.0 = (temp - 1).rem_euclid(12) as u8 + 1;
1350 ret.datetime.date.year = ret
1351 .datetime
1352 .date
1353 .year
1354 .checked_add((temp - 1).div_euclid(12) as i128)?;
1355 }
1356 ret.datetime.date.day.0 = day as u8;
1357
1358 Some(ret)
1359 }
1360
1361 pub fn to_utc(&self) -> DateTime {
1363 let Some(tz) = self.tz else {
1364 return *self;
1365 };
1366
1367 if tz.0 == 0 {
1369 return *self;
1370 }
1371
1372 let h = tz.0 / 60;
1373 let m = tz.0 % 60;
1374 let duration = Duration {
1375 neg: tz.0 > 0,
1376 hour: NonZeroU64::new(h.unsigned_abs() as u64),
1377 minute: NonZeroU64::new(m.unsigned_abs() as u64),
1378 ..Default::default()
1379 };
1380
1381 DateTime {
1382 tz: Some(TimeZone(0)),
1383 ..*self + duration
1384 }
1385 }
1386}
1387
1388impl PartialOrd for DateTime {
1389 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1392 let lhs = self.to_utc();
1393 let rhs = other.to_utc();
1394
1395 if lhs.tz.is_some() == rhs.tz.is_some() {
1396 Some(lhs.datetime.cmp(&rhs.datetime))
1397 } else if lhs.tz.is_some() {
1398 let min = DateTime {
1399 datetime: rhs.datetime,
1400 tz: Some(TimeZone::MAX),
1401 };
1402 let max = DateTime {
1403 datetime: rhs.datetime,
1404 tz: Some(TimeZone::MIN),
1405 };
1406 if lhs < min {
1407 Some(std::cmp::Ordering::Less)
1408 } else if max < lhs {
1409 Some(std::cmp::Ordering::Greater)
1410 } else {
1411 None
1412 }
1413 } else {
1414 let min = DateTime {
1415 datetime: lhs.datetime,
1416 tz: Some(TimeZone::MAX),
1417 };
1418 let max = DateTime {
1419 datetime: lhs.datetime,
1420 tz: Some(TimeZone::MIN),
1421 };
1422 if max < rhs {
1423 Some(std::cmp::Ordering::Less)
1424 } else if rhs < min {
1425 Some(std::cmp::Ordering::Greater)
1426 } else {
1427 None
1428 }
1429 }
1430 }
1431}
1432
1433impl std::fmt::Display for DateTime {
1434 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1435 write!(f, "{}", self.datetime)?;
1436 if let Some(tz) = self.tz.as_ref() {
1437 write!(f, "{}", tz)?;
1438 }
1439 Ok(())
1440 }
1441}
1442
1443impl FromStr for DateTime {
1444 type Err = DateTimeError;
1445
1446 fn from_str(s: &str) -> Result<Self, Self::Err> {
1447 if let Some(datetime) = s.strip_suffix('Z') {
1448 Ok(Self {
1449 datetime: datetime.parse()?,
1450 tz: Some("Z".parse()?),
1451 })
1452 } else if s.len() >= 25 && matches!(s.as_bytes()[s.len() - 6], b'+' | b'-') {
1453 let (datetime, tz) = s.split_at(s.len() - 6);
1455 Ok(Self {
1456 datetime: datetime.parse()?,
1457 tz: Some(tz.parse()?),
1458 })
1459 } else {
1460 Ok(Self {
1461 datetime: s.parse()?,
1462 tz: None,
1463 })
1464 }
1465 }
1466}
1467
1468impl Add<Duration> for DateTime {
1469 type Output = DateTime;
1470
1471 fn add(self, rhs: Duration) -> Self::Output {
1472 self.checked_add(rhs).expect("attempt to add with overflow")
1473 }
1474}
1475
1476impl AddAssign<Duration> for DateTime {
1477 fn add_assign(&mut self, rhs: Duration) {
1478 *self = *self + rhs;
1479 }
1480}
1481
1482impl Sub<Duration> for DateTime {
1483 type Output = DateTime;
1484
1485 fn sub(self, rhs: Duration) -> Self::Output {
1486 self.checked_sub(rhs)
1487 .expect("attempt to subtract with overflow")
1488 }
1489}
1490
1491impl SubAssign<Duration> for DateTime {
1492 fn sub_assign(&mut self, rhs: Duration) {
1493 *self = *self - rhs;
1494 }
1495}
1496
1497const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;
1498
1499#[derive(Debug, Clone, Copy, Default)]
1500pub struct Duration {
1501 neg: bool,
1502 year: Option<NonZeroU64>,
1503 month: Option<NonZeroU64>,
1504 day: Option<NonZeroU64>,
1505 hour: Option<NonZeroU64>,
1506 minute: Option<NonZeroU64>,
1507 nanosecond: Option<NonZeroU64>,
1508}
1509
1510impl PartialOrd for Duration {
1511 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1514 const BASELINE: [DateTime; 4] = unsafe {
1523 [
1527 DateTime::new_unchecked(1696, 9, 1, 0, 0, 0, Some(TimeZone::UTC)),
1528 DateTime::new_unchecked(1697, 2, 1, 0, 0, 0, Some(TimeZone::UTC)),
1529 DateTime::new_unchecked(1903, 3, 1, 0, 0, 0, Some(TimeZone::UTC)),
1530 DateTime::new_unchecked(1903, 7, 1, 0, 0, 0, Some(TimeZone::UTC)),
1531 ]
1532 };
1533
1534 let ret0 = (BASELINE[0] + *self).partial_cmp(&(BASELINE[0] + *other))?;
1535 let ret1 = (BASELINE[1] + *self).partial_cmp(&(BASELINE[1] + *other))?;
1536 let ret2 = (BASELINE[2] + *self).partial_cmp(&(BASELINE[2] + *other))?;
1537 let ret3 = (BASELINE[3] + *self).partial_cmp(&(BASELINE[3] + *other))?;
1538 (ret0 == ret1 && ret0 == ret2 && ret0 == ret3).then_some(ret0)
1539 }
1540}
1541
1542impl std::fmt::Display for Duration {
1543 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1544 if self.neg {
1545 write!(f, "-")?;
1546 }
1547 write!(f, "P")?;
1548 if self.year.is_none()
1549 && self.month.is_none()
1550 && self.day.is_none()
1551 && self.hour.is_none()
1552 && self.minute.is_none()
1553 && self.nanosecond.is_none()
1554 {
1555 write!(f, "0Y")?;
1556 return Ok(());
1557 }
1558 if let Some(year) = self.year {
1559 write!(f, "{}Y", year)?;
1560 }
1561 if let Some(month) = self.month {
1562 write!(f, "{}M", month)?;
1563 }
1564 if let Some(day) = self.day {
1565 write!(f, "{}D", day)?;
1566 }
1567
1568 match (self.hour, self.minute, self.nanosecond) {
1569 (None, None, None) => {}
1570 (hour, minute, nanosecond) => {
1571 write!(f, "T")?;
1572 if let Some(hour) = hour {
1573 write!(f, "{}H", hour)?;
1574 }
1575 if let Some(minute) = minute {
1576 write!(f, "{}M", minute)?;
1577 }
1578 if let Some(nanosecond) = nanosecond {
1579 let sec = nanosecond.get() / NANOSECONDS_PER_SECOND;
1580 let mut nano = nanosecond.get() % NANOSECONDS_PER_SECOND;
1581 if nano == 0 {
1582 write!(f, "{}S", sec)?;
1583 } else {
1584 while nano % 10 == 0 {
1585 nano /= 10;
1586 }
1587 write!(f, "{}.{}S", sec, nano)?;
1588 }
1589 }
1590 }
1591 }
1592
1593 Ok(())
1594 }
1595}
1596
1597impl FromStr for Duration {
1598 type Err = DateTimeError;
1599
1600 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
1601 let mut neg = false;
1602 if let Some(rem) = s.strip_prefix('-') {
1603 neg = true;
1604 s = rem;
1605 }
1606
1607 s = s
1608 .strip_prefix('P')
1609 .ok_or(datetime_error!(Year, InvalidFormat))?;
1610 if s.is_empty() {
1611 return Err(datetime_error!(Year, InvalidFormat))?;
1612 }
1613
1614 let mut year = None;
1615 let mut month = None;
1616 let mut day = None;
1617 let mut hour = None;
1618 let mut minute = None;
1619 let mut nanosecond = None;
1620
1621 macro_rules! parse_number {
1622 ( $s:expr, $ind:literal, $seg:ident, $res:expr ) => {
1623 if let Some((seg, rem)) = $s.split_once($ind) {
1624 let seg = seg
1625 .parse::<u64>()
1626 .map_err(|err| datetime_error!($seg, ParseIntError(err)))?;
1627 $res = NonZeroU64::new(seg);
1628 $s = rem;
1629 }
1630 };
1631 }
1632 parse_number!(s, 'Y', Year, year);
1633 parse_number!(s, 'M', Month, month);
1634 parse_number!(s, 'D', Day, day);
1635 if let Some(rem) = s.strip_prefix('T') {
1636 s = rem;
1637 if s.is_empty() {
1638 return Err(datetime_error!(Hour, InvalidFormat));
1639 }
1640 parse_number!(s, 'H', Hour, hour);
1641 parse_number!(s, 'M', Minute, minute);
1642 if let Some((sec, rem)) = s.split_once('S') {
1643 s = rem;
1644
1645 if let Some((sec, frac)) = sec.split_once('.') {
1646 let sec = sec
1647 .parse::<u64>()
1648 .map_err(|err| datetime_error!(Second, ParseIntError(err)))?
1649 .checked_mul(NANOSECONDS_PER_SECOND)
1650 .ok_or(datetime_error!(Second, TooLarge))?;
1651 let base = 10u64.pow(9 - frac.len() as u32);
1652 let frac = frac
1653 .parse::<u64>()
1654 .map_err(|err| datetime_error!(Second, ParseIntError(err)))?
1655 .checked_mul(base)
1656 .ok_or(datetime_error!(Second, TooLarge))?;
1657 let nano = sec
1658 .checked_add(frac)
1659 .ok_or(datetime_error!(Second, TooLarge))?;
1660 nanosecond = NonZeroU64::new(nano);
1661 } else {
1662 let sec = sec
1663 .parse::<u64>()
1664 .map_err(|err| datetime_error!(Second, ParseIntError(err)))?;
1665 let nano = sec
1666 .checked_mul(NANOSECONDS_PER_SECOND)
1667 .ok_or(datetime_error!(Second, TooLarge))?;
1668 nanosecond = NonZeroU64::new(nano);
1669 }
1670 }
1671 }
1672
1673 if !s.is_empty() {
1674 return Err(datetime_error!(Second, InvalidFormat));
1675 }
1676
1677 Ok(Self {
1678 neg,
1679 year,
1680 month,
1681 day,
1682 hour,
1683 minute,
1684 nanosecond,
1685 })
1686 }
1687}
1688
1689fn is_leap(year: NaiveYear) -> bool {
1690 year.0 % 400 == 0 || (year.0 % 100 != 0 && year.0 % 4 == 0)
1691}
1692
1693macro_rules! impl_partial_eq_for_datetime_objects {
1694 ( $( $t:ty ),* ) => {
1695 $(
1696 impl PartialEq for $t {
1697 fn eq(&self, other: &Self) -> bool {
1698 self.partial_cmp(other).is_some_and(|ret| ret.is_eq())
1699 }
1700 }
1701 )*
1702 };
1703}
1704impl_partial_eq_for_datetime_objects!(
1705 Duration, DateTime, Time, Date, GYearMonth, GYear, GMonthDay, GDay, GMonth
1706);
1707
1708fn maximum_day_in_month_for(year: NaiveYear, month: i8) -> Option<u8> {
1713 let m = (month - 1).rem_euclid(12) + 1;
1714 let mut y = year.0.checked_add((month - 1).div_euclid(12) as i128)?;
1715 if y == 0 {
1716 y = 1;
1717 }
1718 if m == 2 {
1719 Some(28 + is_leap(NaiveYear(y)) as u8)
1720 } else {
1721 Some(NUM_OF_DAYS_IN_A_MONTH[m as usize])
1722 }
1723}
1724
1725#[cfg(test)]
1726mod tests {
1727 use super::*;
1728
1729 #[test]
1730 fn gyear_parse_test() {
1731 assert!("1999".parse::<GYear>().is_ok());
1732 assert!("-1999".parse::<GYear>().is_ok());
1733 assert!("1999Z".parse::<GYear>().is_ok());
1734 assert!("-1999Z".parse::<GYear>().is_ok());
1735 assert!("1999+09:00".parse::<GYear>().is_ok());
1736 assert!("1999-09:00".parse::<GYear>().is_ok());
1737 assert!("-1999+09:00".parse::<GYear>().is_ok());
1738 assert!("-1999-09:00".parse::<GYear>().is_ok());
1739 assert!("-1999+14:00".parse::<GYear>().is_ok());
1741 assert!("-1999-14:00".parse::<GYear>().is_ok());
1743 assert!("1999+09:59".parse::<GYear>().is_ok());
1745
1746 assert!("231999".parse::<GYear>().is_ok());
1747 assert!("-231999".parse::<GYear>().is_ok());
1748 assert!("231999+09:00".parse::<GYear>().is_ok());
1749 assert!("231999-09:00".parse::<GYear>().is_ok());
1750 assert!("-231999+09:00".parse::<GYear>().is_ok());
1751 assert!("-231999-09:00".parse::<GYear>().is_ok());
1752
1753 assert!("0000".parse::<GYear>().is_err());
1755 assert!("0000+09:00".parse::<GYear>().is_err());
1756 assert!(
1758 "1000000000000000000000000000000000000000"
1759 .parse::<GYear>()
1760 .is_err()
1761 );
1762 assert!(
1764 "-1000000000000000000000000000000000000000"
1765 .parse::<GYear>()
1766 .is_err()
1767 );
1768 assert!("1999+12:60".parse::<GYear>().is_err());
1770 assert!("1999+12:000".parse::<GYear>().is_err());
1771 assert!("1999+012:00".parse::<GYear>().is_err());
1772 assert!("+1999".parse::<GYear>().is_err());
1774 assert!("+1999+09:00".parse::<GYear>().is_err());
1775 assert!("1999+15:00".parse::<GYear>().is_err());
1777 assert!("1999-15:00".parse::<GYear>().is_err());
1779 assert!("1999+14:01".parse::<GYear>().is_err());
1781 assert!("1999-14:01".parse::<GYear>().is_err());
1783 }
1784
1785 #[test]
1786 fn gmonth_parse_test() {
1787 assert!("12".parse::<GMonth>().is_ok());
1788 assert!("12Z".parse::<GMonth>().is_ok());
1789 assert!("12+09:00".parse::<GMonth>().is_ok());
1790 assert!("12-09:00".parse::<GMonth>().is_ok());
1791
1792 assert!("00".parse::<GMonth>().is_err());
1794 assert!("00+09:00".parse::<GMonth>().is_err());
1795 assert!("13".parse::<GMonth>().is_err());
1797 assert!("12+12:60".parse::<GMonth>().is_err());
1799 assert!("12+12:000".parse::<GMonth>().is_err());
1800 assert!("12+012:00".parse::<GMonth>().is_err());
1801 assert!("+12+09:00".parse::<GMonth>().is_err());
1803 assert!("-12+09:00".parse::<GMonth>().is_err());
1805 }
1806
1807 #[test]
1808 fn gday_parse_test() {
1809 assert!("08".parse::<GDay>().is_ok());
1810 assert!("08Z".parse::<GDay>().is_ok());
1811 assert!("08+09:00".parse::<GDay>().is_ok());
1812 assert!("08-09:00".parse::<GDay>().is_ok());
1813
1814 assert!("00".parse::<GDay>().is_err());
1816 assert!("00+09:00".parse::<GDay>().is_err());
1817 assert!("32".parse::<GDay>().is_err());
1819 assert!("12+12:60".parse::<GDay>().is_err());
1821 assert!("12+12:000".parse::<GDay>().is_err());
1822 assert!("12+012:00".parse::<GDay>().is_err());
1823 assert!("+12+09:00".parse::<GDay>().is_err());
1825 assert!("-12+09:00".parse::<GDay>().is_err());
1827 }
1828
1829 #[test]
1830 fn gyearmonth_parse_test() {
1831 assert!("2015-05".parse::<GYearMonth>().is_ok());
1832 assert!("2015-05Z".parse::<GYearMonth>().is_ok());
1833 assert!("2015-05+09:00".parse::<GYearMonth>().is_ok());
1834 assert!("2015-05-09:00".parse::<GYearMonth>().is_ok());
1835 assert!("-0660-02+09:00".parse::<GYearMonth>().is_ok());
1836 assert!("-0660-02-09:00".parse::<GYearMonth>().is_ok());
1837
1838 assert!("2015-05+12:60".parse::<GYearMonth>().is_err());
1840 assert!("2015-05+12:000".parse::<GYearMonth>().is_err());
1841 assert!("2025-05+012:00".parse::<GYearMonth>().is_err());
1842 assert!("+2015-05+09:00".parse::<GYearMonth>().is_err());
1844 }
1845
1846 #[test]
1847 fn gmonthday_parse_test() {
1848 assert!("05-15".parse::<GMonthDay>().is_ok());
1849 assert!("05-15Z".parse::<GMonthDay>().is_ok());
1850 assert!("05-15+09:00".parse::<GMonthDay>().is_ok());
1851 assert!("05-15-09:00".parse::<GMonthDay>().is_ok());
1852 assert!("02-11+09:00".parse::<GMonthDay>().is_ok());
1853 assert!("02-11-09:00".parse::<GMonthDay>().is_ok());
1854 assert!("01-31".parse::<GMonthDay>().is_ok());
1856 assert!("02-29".parse::<GMonthDay>().is_ok());
1857 assert!("03-31".parse::<GMonthDay>().is_ok());
1858 assert!("04-30".parse::<GMonthDay>().is_ok());
1859 assert!("05-31".parse::<GMonthDay>().is_ok());
1860 assert!("06-30".parse::<GMonthDay>().is_ok());
1861 assert!("07-31".parse::<GMonthDay>().is_ok());
1862 assert!("08-31".parse::<GMonthDay>().is_ok());
1863 assert!("09-30".parse::<GMonthDay>().is_ok());
1864 assert!("10-31".parse::<GMonthDay>().is_ok());
1865 assert!("11-30".parse::<GMonthDay>().is_ok());
1866 assert!("12-31".parse::<GMonthDay>().is_ok());
1867
1868 assert!("05-15+12:60".parse::<GMonthDay>().is_err());
1870 assert!("05-15+12:000".parse::<GMonthDay>().is_err());
1871 assert!("05-15+012:00".parse::<GMonthDay>().is_err());
1872 assert!("+05-15+09:00".parse::<GMonthDay>().is_err());
1874 assert!("01-32".parse::<GMonthDay>().is_err());
1876 assert!("02-30".parse::<GMonthDay>().is_err());
1877 assert!("03-32".parse::<GMonthDay>().is_err());
1878 assert!("04-31".parse::<GMonthDay>().is_err());
1879 assert!("05-32".parse::<GMonthDay>().is_err());
1880 assert!("06-31".parse::<GMonthDay>().is_err());
1881 assert!("07-32".parse::<GMonthDay>().is_err());
1882 assert!("08-32".parse::<GMonthDay>().is_err());
1883 assert!("09-31".parse::<GMonthDay>().is_err());
1884 assert!("10-32".parse::<GMonthDay>().is_err());
1885 assert!("11-31".parse::<GMonthDay>().is_err());
1886 assert!("12-32".parse::<GMonthDay>().is_err());
1887 }
1888
1889 #[test]
1890 fn date_parse_test() {
1891 assert!("2015-05-15".parse::<Date>().is_ok());
1892 assert!("2015-05-15Z".parse::<Date>().is_ok());
1893 assert!("2015-05-15+09:00".parse::<Date>().is_ok());
1894 assert!("2015-05-15-09:00".parse::<Date>().is_ok());
1895 assert!("-0660-02-11+09:00".parse::<Date>().is_ok());
1896 assert!("-0660-02-11-09:00".parse::<Date>().is_ok());
1897 assert!("2024-02-29".parse::<Date>().is_ok());
1899 assert!("2000-02-29".parse::<Date>().is_ok());
1900
1901 assert!("2015-05-15+12:60".parse::<Date>().is_err());
1903 assert!("2015-05-15+12:000".parse::<Date>().is_err());
1904 assert!("2015-05-15+012:00".parse::<Date>().is_err());
1905 assert!("+2015-05-15+09:00".parse::<Date>().is_err());
1907 assert!("2015-02-29".parse::<Date>().is_err());
1909 assert!("1900-02-29".parse::<Date>().is_err());
1910 }
1911
1912 #[test]
1913 fn time_parse_test() {
1914 assert!("00:00:00".parse::<Time>().is_ok());
1915 assert!("12:00:00".parse::<Time>().is_ok());
1916 assert!("24:00:00".parse::<Time>().is_ok());
1918 assert!("23:59:60".parse::<Time>().is_ok());
1920 assert!("12:30:00Z".parse::<Time>().is_ok());
1921 assert!("12:00:30+09:00".parse::<Time>().is_ok());
1922 assert!("12:15:15-09:00".parse::<Time>().is_ok());
1923
1924 assert!("25:00:00".parse::<Time>().is_err());
1925 assert!("12:60:00".parse::<Time>().is_err());
1926 assert!("09:00:60".parse::<Time>().is_err());
1927 assert!("9:00:00".parse::<Time>().is_err());
1928 assert!("+09:00:00".parse::<Time>().is_err());
1929 assert!("-09:00:00".parse::<Time>().is_err());
1930 assert!("+9:00:00".parse::<Time>().is_err());
1931 assert!("-9:00:00".parse::<Time>().is_err());
1932 assert!("09:+0:00".parse::<Time>().is_err());
1933 assert!("09:-0:00".parse::<Time>().is_err());
1934 assert!("09:00:+0".parse::<Time>().is_err());
1935 assert!("09:00:-0".parse::<Time>().is_err());
1936 assert!("09:00:00++1:00".parse::<Time>().is_err());
1937 assert!("09:00:00+-1:00".parse::<Time>().is_err());
1938 assert!("09:00:00-+1:00".parse::<Time>().is_err());
1939 assert!("09:00:00--1:00".parse::<Time>().is_err());
1940 assert!("09:00:00+01:+0".parse::<Time>().is_err());
1941 assert!("09:00:00+01:-0".parse::<Time>().is_err());
1942 assert!("+09:00:10".parse::<Time>().is_err());
1944 assert!("-09:00:10".parse::<Time>().is_err());
1946 assert!("23:00:60".parse::<Time>().is_err());
1948 assert!("24:00:01".parse::<Time>().is_err());
1950 }
1951
1952 #[test]
1953 fn datetime_parse_test() {
1954 assert!("2000-01-20T12:00:00-13:00".parse::<DateTime>().is_ok());
1955 assert!("2000-01-20T12:00:00Z".parse::<DateTime>().is_ok());
1956 assert!("2000-01-12T12:13:14Z".parse::<DateTime>().is_ok());
1957 assert!("-0660-02-11T00:00:00+09:00".parse::<DateTime>().is_ok());
1958
1959 assert!("+2000-01-20T12:00:00-13:00".parse::<DateTime>().is_err());
1960 assert!("2000-01-20t12:00:00-13:00".parse::<DateTime>().is_err());
1961 assert!("2000-+1-20T12:00:00-13:00".parse::<DateTime>().is_err());
1962 assert!("+000-01-20T12:00:00-13:00".parse::<DateTime>().is_err());
1963 assert!("0000-01-20T12:00:00-13:00".parse::<DateTime>().is_err());
1964 assert!("2000-01-2012:00:00-13:00".parse::<DateTime>().is_err());
1965 assert!("2000001-20T12:00:00-13:00".parse::<DateTime>().is_err());
1966 assert!("2000-01-20T-12:00".parse::<DateTime>().is_err());
1967 }
1968
1969 #[test]
1970 fn duration_parse_test() {
1971 assert!("P1347Y".parse::<Duration>().is_ok());
1972 assert!("P1347M".parse::<Duration>().is_ok());
1973 assert!("P1Y2MT2H".parse::<Duration>().is_ok());
1974 assert!("P0Y1347M".parse::<Duration>().is_ok());
1975 assert!("P0Y1347M0D".parse::<Duration>().is_ok());
1976 assert!("-P1347M".parse::<Duration>().is_ok());
1977
1978 assert!("P-1347M".parse::<Duration>().is_err());
1979 assert!("P1Y2MT".parse::<Duration>().is_err());
1980 }
1981
1982 #[test]
1983 fn datetime_addition_test() {
1984 let datetime = "2000-01-12T12:13:14Z".parse::<DateTime>().unwrap();
1985 let duration = "P1Y3M5DT7H10M3.3S".parse::<Duration>().unwrap();
1986 let ret = datetime + duration;
1987 assert_eq!(ret.to_string(), "2001-04-17T19:23:17.3Z");
1988
1989 let datetime = "2000-01-01T00:00:00Z".parse::<DateTime>().unwrap();
1990 let duration = "-P3M".parse::<Duration>().unwrap();
1991 let ret = datetime + duration;
1992 assert_eq!(ret.to_string(), "1999-10-01T00:00:00Z");
1993
1994 let datetime = "2000-01-12T00:00:00Z".parse::<DateTime>().unwrap();
1995 let duration = "PT33H".parse::<Duration>().unwrap();
1996 let ret = datetime + duration;
1997 assert_eq!(ret.to_string(), "2000-01-13T09:00:00Z");
1998
1999 let datetime = "2000-03-04T23:00:00+03:00".parse::<DateTime>().unwrap();
2000 let utc = datetime.to_utc();
2001 assert_eq!(utc.to_string(), "2000-03-04T20:00:00Z");
2002 }
2003
2004 #[test]
2005 fn datetime_comparison_test() {
2006 assert!(
2008 "2000-01-15T00:00:00".parse::<DateTime>().unwrap()
2009 < "2000-02-15T00:00:00".parse::<DateTime>().unwrap()
2010 );
2011 assert!(
2012 "2000-01-15T12:00:00".parse::<DateTime>().unwrap()
2013 < "2000-01-16T12:00:00Z".parse::<DateTime>().unwrap()
2014 );
2015
2016 assert!(
2018 "2000-01-01T12:00:00"
2019 .parse::<DateTime>()
2020 .unwrap()
2021 .partial_cmp(&"1999-12-31T23:00:00Z".parse().unwrap())
2022 .is_none()
2023 );
2024 assert!(
2025 "2000-01-16T12:00:00"
2026 .parse::<DateTime>()
2027 .unwrap()
2028 .partial_cmp(&"2000-01-16T12:00:00Z".parse().unwrap())
2029 .is_none()
2030 );
2031 assert!(
2032 "2000-01-16T00:00:00"
2033 .parse::<DateTime>()
2034 .unwrap()
2035 .partial_cmp(&"2000-01-16T12:00:00Z".parse().unwrap())
2036 .is_none()
2037 );
2038 }
2039
2040 #[test]
2041 fn duration_comparison_test() {
2042 let p1y = "P1Y".parse::<Duration>().unwrap();
2043 assert!(p1y > "P364D".parse().unwrap());
2044 assert!(p1y < "P367D".parse().unwrap());
2045 assert!(p1y.partial_cmp(&"P365D".parse().unwrap()).is_none());
2046 assert!(p1y.partial_cmp(&"P366D".parse().unwrap()).is_none());
2047
2048 let p1m = "P1M".parse::<Duration>().unwrap();
2049 assert!(p1m > "P27D".parse().unwrap());
2050 assert!(p1m < "P32D".parse().unwrap());
2051 assert!(p1m.partial_cmp(&"P28D".parse().unwrap()).is_none());
2052 assert!(p1m.partial_cmp(&"P29D".parse().unwrap()).is_none());
2053 assert!(p1m.partial_cmp(&"P30D".parse().unwrap()).is_none());
2054 assert!(p1m.partial_cmp(&"P31D".parse().unwrap()).is_none());
2055
2056 let p5m = "P5M".parse::<Duration>().unwrap();
2057 assert!(p5m > "P149D".parse().unwrap());
2058 assert!(p5m < "P154D".parse().unwrap());
2059 assert!(p5m.partial_cmp(&"P150D".parse().unwrap()).is_none());
2060 assert!(p5m.partial_cmp(&"P151D".parse().unwrap()).is_none());
2061 assert!(p5m.partial_cmp(&"P152D".parse().unwrap()).is_none());
2062 assert!(p5m.partial_cmp(&"P153D".parse().unwrap()).is_none());
2063 }
2064}