1use core::{
4 cmp::Ordering,
5 fmt,
6 hash::{Hash, Hasher},
7};
8
9use crate::{
10 civil,
11 error::{self, Error},
12 format::{self, Format, Formatted},
13 parse,
14 timezone::{Disambiguation, TimeZone},
15};
16
17pub(crate) const MIN_YEAR: i32 = -9999;
19pub(crate) const MAX_YEAR: i32 = 9999;
21
22pub(crate) const MIN_SECS: i64 = civil::days_from_civil(MIN_YEAR, 1, 1) * civil::SECS_PER_DAY;
23pub(crate) const MAX_SECS: i64 =
24 civil::days_from_civil(MAX_YEAR, 12, 31) * civil::SECS_PER_DAY + (civil::SECS_PER_DAY - 1);
25
26pub(crate) fn local_seconds_checked(
32 year: i32,
33 month: u8,
34 day: u8,
35 hour: u8,
36 minute: u8,
37 second: u8,
38) -> Result<i64, Error> {
39 if !(MIN_YEAR..=MAX_YEAR).contains(&year) {
40 return Err(error::out_of_range("year is outside the supported range"));
41 }
42 if !(1..=12).contains(&month) {
43 return Err(error::invalid("month is not in 1..=12"));
44 }
45 if day < 1 || day > civil::days_in_month(year, month) {
46 return Err(error::invalid("day is not valid for the given month"));
47 }
48 if hour > 23 {
49 return Err(error::invalid("hour is not in 0..=23"));
50 }
51 if minute > 59 {
52 return Err(error::invalid("minute is not in 0..=59"));
53 }
54 if second > 60 {
55 return Err(error::invalid("second is not in 0..=60"));
56 }
57 let second = if second == 60 { 59 } else { second };
59 Ok(civil::days_from_civil(year, month, day) * civil::SECS_PER_DAY
60 + i64::from(hour) * 3600
61 + i64::from(minute) * 60
62 + i64::from(second))
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67pub enum Weekday {
68 Monday,
70 Tuesday,
72 Wednesday,
74 Thursday,
76 Friday,
78 Saturday,
80 Sunday,
82}
83
84impl Weekday {
85 pub(crate) const fn from_index(index: u8) -> Weekday {
86 match index {
87 0 => Weekday::Monday,
88 1 => Weekday::Tuesday,
89 2 => Weekday::Wednesday,
90 3 => Weekday::Thursday,
91 4 => Weekday::Friday,
92 5 => Weekday::Saturday,
93 _ => Weekday::Sunday,
94 }
95 }
96
97 #[must_use]
99 #[inline]
100 pub const fn name(self) -> &'static str {
101 match self {
102 Weekday::Monday => "Monday",
103 Weekday::Tuesday => "Tuesday",
104 Weekday::Wednesday => "Wednesday",
105 Weekday::Thursday => "Thursday",
106 Weekday::Friday => "Friday",
107 Weekday::Saturday => "Saturday",
108 Weekday::Sunday => "Sunday",
109 }
110 }
111
112 #[must_use]
114 #[inline]
115 pub const fn short_name(self) -> &'static str {
116 match self {
117 Weekday::Monday => "Mon",
118 Weekday::Tuesday => "Tue",
119 Weekday::Wednesday => "Wed",
120 Weekday::Thursday => "Thu",
121 Weekday::Friday => "Fri",
122 Weekday::Saturday => "Sat",
123 Weekday::Sunday => "Sun",
124 }
125 }
126
127 #[must_use]
129 #[inline]
130 pub const fn number_from_monday(self) -> u8 {
131 match self {
132 Weekday::Monday => 1,
133 Weekday::Tuesday => 2,
134 Weekday::Wednesday => 3,
135 Weekday::Thursday => 4,
136 Weekday::Friday => 5,
137 Weekday::Saturday => 6,
138 Weekday::Sunday => 7,
139 }
140 }
141
142 #[must_use]
144 #[inline]
145 pub const fn number_from_sunday(self) -> u8 {
146 match self {
147 Weekday::Sunday => 0,
148 Weekday::Monday => 1,
149 Weekday::Tuesday => 2,
150 Weekday::Wednesday => 3,
151 Weekday::Thursday => 4,
152 Weekday::Friday => 5,
153 Weekday::Saturday => 6,
154 }
155 }
156}
157
158#[derive(Clone, Copy)]
179pub struct DateTime {
180 pub(crate) secs: i64,
181 pub(crate) nanos: u32,
182 pub(crate) offset: i32,
183 pub(crate) zone: TimeZone,
184 #[cfg(feature = "std")]
187 pub(crate) monotonic: Option<std::time::Instant>,
188}
189
190impl DateTime {
191 pub const UNIX_EPOCH: DateTime = DateTime {
193 secs: 0,
194 nanos: 0,
195 offset: 0,
196 zone: TimeZone::UTC,
197 #[cfg(feature = "std")]
198 monotonic: None,
199 };
200
201 pub(crate) const fn from_raw(secs: i64, nanos: u32, offset: i32, zone: TimeZone) -> DateTime {
202 DateTime {
203 secs,
204 nanos,
205 offset,
206 zone,
207 #[cfg(feature = "std")]
208 monotonic: None,
209 }
210 }
211
212 #[inline]
218 pub fn from_unix(seconds: i64) -> Result<DateTime, Error> {
219 DateTime::from_unix_nanos(i128::from(seconds) * 1_000_000_000)
220 }
221
222 #[inline]
228 pub fn from_unix_nanos(nanoseconds: i128) -> Result<DateTime, Error> {
229 let seconds = i64::try_from(nanoseconds.div_euclid(1_000_000_000))
230 .map_err(|_| error::out_of_range("instant is outside the supported range"))?;
231 let nanos = nanoseconds.rem_euclid(1_000_000_000) as u32;
232 DateTime::from_raw_checked(seconds, nanos, TimeZone::UTC)
233 }
234
235 #[allow(clippy::too_many_arguments)]
249 pub fn from_parts(
250 year: i32,
251 month: u8,
252 day: u8,
253 hour: u8,
254 minute: u8,
255 second: u8,
256 nanosecond: u32,
257 zone: TimeZone,
258 ) -> Result<DateTime, Error> {
259 DateTime::from_parts_with(
260 year,
261 month,
262 day,
263 hour,
264 minute,
265 second,
266 nanosecond,
267 zone,
268 Disambiguation::Compatible,
269 )
270 }
271
272 #[allow(clippy::too_many_arguments)]
282 pub fn from_parts_with(
283 year: i32,
284 month: u8,
285 day: u8,
286 hour: u8,
287 minute: u8,
288 second: u8,
289 nanosecond: u32,
290 zone: TimeZone,
291 disambiguation: Disambiguation,
292 ) -> Result<DateTime, Error> {
293 if nanosecond >= civil::NANOS_PER_SEC {
294 return Err(error::invalid("nanosecond is not in 0..1_000_000_000"));
295 }
296 let local = local_seconds_checked(year, month, day, hour, minute, second)?;
297 let instant = zone.resolve_with(local, disambiguation)?;
298 DateTime::from_raw_checked(instant, nanosecond, zone)
299 }
300
301 #[inline]
302 pub(crate) fn from_raw_checked(secs: i64, nanos: u32, zone: TimeZone) -> Result<DateTime, Error> {
303 if !(MIN_SECS..=MAX_SECS).contains(&secs) {
304 return Err(error::out_of_range("instant is outside the supported range"));
305 }
306 let offset = zone.offset_at(secs);
307 Ok(DateTime {
308 secs,
309 nanos,
310 offset,
311 zone,
312 #[cfg(feature = "std")]
313 monotonic: None,
314 })
315 }
316
317 #[cfg(feature = "std")]
319 #[must_use]
320 pub fn now_in(zone: TimeZone) -> DateTime {
321 let now = std::time::SystemTime::now();
322 let (seconds, nanos) = match now.duration_since(std::time::UNIX_EPOCH) {
323 Ok(duration) => (duration.as_secs() as i64, duration.subsec_nanos()),
324 Err(err) => {
325 let duration = err.duration();
326 let seconds = duration.as_secs() as i64;
327 if duration.subsec_nanos() == 0 {
328 (-seconds, 0)
329 } else {
330 (-seconds - 1, 1_000_000_000 - duration.subsec_nanos())
331 }
332 }
333 };
334 let utc = DateTime::from_unix_nanos(i128::from(seconds) * 1_000_000_000 + i128::from(nanos))
335 .unwrap_or(DateTime::UNIX_EPOCH);
336 let mut result = utc.in_timezone(zone);
337 result.monotonic = Some(std::time::Instant::now());
338 result
339 }
340
341 #[cfg(feature = "std")]
346 #[must_use]
347 pub fn now() -> DateTime {
348 #[cfg(feature = "timezone-system")]
349 {
350 match TimeZone::system() {
351 Ok(zone) => DateTime::now_in(zone),
352 Err(_) => DateTime::now_in(TimeZone::UTC),
353 }
354 }
355 #[cfg(not(feature = "timezone-system"))]
356 {
357 DateTime::now_in(TimeZone::UTC)
358 }
359 }
360
361 pub fn parse_with_timezone(input: &str, zone: TimeZone) -> Result<DateTime, Error> {
370 parse::parse(input, Some(zone))
371 }
372
373 pub fn parse_bytes(input: &[u8]) -> Result<DateTime, Error> {
382 parse::parse_bytes(input, None)
383 }
384
385 pub fn parse_bytes_with_timezone(input: &[u8], zone: TimeZone) -> Result<DateTime, Error> {
391 parse::parse_bytes(input, Some(zone))
392 }
393
394 pub fn parse_bytes_with_format(input: &[u8], format: Format) -> Result<DateTime, Error> {
403 parse::parse_bytes_with_format(input, format)
404 }
405
406 pub fn parse_with_format(input: &str, format: Format) -> Result<DateTime, Error> {
414 parse::parse_with_format(input, format)
415 }
416
417 #[must_use]
424 pub fn format(&self, format: Format) -> Formatted<'_> {
425 Formatted::new(self, format)
426 }
427
428 pub fn try_format(&self, format: Format) -> Result<Formatted<'_>, Error> {
441 if !matches!(format, Format::TimeOnly) && !(0..=9999).contains(&self.year()) {
442 return Err(error::out_of_range("value cannot be represented in the requested format"));
443 }
444 if matches!(format, Format::Rfc3339 | Format::Rfc9557 | Format::Iso8601 | Format::Rfc2822)
445 && self.offset() % 60 != 0
446 {
447 return Err(error::out_of_range("offset cannot be represented in the requested format"));
448 }
449 Ok(Formatted::new(self, format))
450 }
451
452 pub fn format_to(&self, buffer: &mut [u8], format: Format) -> Result<usize, Error> {
467 self.try_format(format)?.write_to(buffer)
468 }
469
470 #[must_use]
472 #[inline]
473 pub const fn unix(&self) -> i64 {
474 self.secs
475 }
476
477 #[must_use]
479 #[inline]
480 pub const fn unix_nanos(&self) -> i128 {
481 self.secs as i128 * 1_000_000_000 + self.nanos as i128
482 }
483
484 #[must_use]
486 #[inline]
487 pub const fn offset(&self) -> i32 {
488 self.offset
489 }
490
491 #[must_use]
493 #[inline]
494 pub const fn timezone(&self) -> TimeZone {
495 self.zone
496 }
497
498 #[must_use]
500 #[inline]
501 pub const fn timezone_name(&self) -> Option<&'static str> {
502 self.zone.annotation_name()
503 }
504
505 #[must_use]
507 pub fn is_dst(&self) -> bool {
508 self.zone.is_dst_at(self.secs)
509 }
510
511 #[must_use]
513 #[inline]
514 pub fn in_timezone(&self, zone: TimeZone) -> DateTime {
515 let offset = zone.offset_at(self.secs);
516 DateTime {
517 secs: self.secs,
518 nanos: self.nanos,
519 offset,
520 zone,
521 #[cfg(feature = "std")]
522 monotonic: self.monotonic,
523 }
524 }
525
526 #[inline]
529 pub(crate) fn parts(&self) -> (i32, u8, u8, u8, u8, u8, u32) {
530 let local = self.secs + i64::from(self.offset);
531 let days = local.div_euclid(civil::SECS_PER_DAY);
532 let seconds = local.rem_euclid(civil::SECS_PER_DAY);
533 let (year, month, day) = civil::civil_from_days(days);
534 let hour = (seconds / 3600) as u8;
535 let minute = ((seconds % 3600) / 60) as u8;
536 let second = (seconds % 60) as u8;
537 (year, month, day, hour, minute, second, self.nanos)
538 }
539
540 #[must_use]
542 #[inline]
543 pub fn year(&self) -> i32 {
544 self.parts().0
545 }
546
547 #[must_use]
549 #[inline]
550 pub fn month(&self) -> u8 {
551 self.parts().1
552 }
553
554 #[must_use]
556 #[inline]
557 pub fn day(&self) -> u8 {
558 self.parts().2
559 }
560
561 #[must_use]
563 #[inline]
564 pub fn hour(&self) -> u8 {
565 self.parts().3
566 }
567
568 #[must_use]
570 #[inline]
571 pub fn minute(&self) -> u8 {
572 self.parts().4
573 }
574
575 #[must_use]
577 #[inline]
578 pub fn second(&self) -> u8 {
579 self.parts().5
580 }
581
582 #[must_use]
584 #[inline]
585 pub fn nanosecond(&self) -> u32 {
586 self.nanos
587 }
588
589 #[must_use]
591 #[inline]
592 pub fn millisecond(&self) -> u32 {
593 self.nanos / 1_000_000
594 }
595
596 #[must_use]
598 #[inline]
599 pub fn microsecond(&self) -> u32 {
600 self.nanos / 1_000
601 }
602
603 #[must_use]
605 #[inline]
606 pub fn weekday(&self) -> Weekday {
607 let days = (self.secs + i64::from(self.offset)).div_euclid(civil::SECS_PER_DAY);
608 Weekday::from_index(civil::weekday_from_days(days))
609 }
610
611 #[must_use]
613 #[inline]
614 pub fn ordinal(&self) -> u16 {
615 let (year, month, day, ..) = self.parts();
616 civil::ordinal(year, month, day)
617 }
618
619 #[must_use]
621 #[inline]
622 pub fn iso_week(&self) -> (i32, u8) {
623 let (year, month, day, ..) = self.parts();
624 civil::iso_week(year, month, day)
625 }
626
627 pub fn checked_add(&self, duration: core::time::Duration) -> Result<DateTime, Error> {
633 let total = self
634 .unix_nanos()
635 .checked_add(i128::from(duration.as_secs()) * 1_000_000_000)
636 .and_then(|v| v.checked_add(i128::from(duration.subsec_nanos())))
637 .ok_or_else(|| error::out_of_range("result is outside the supported range"))?;
638 #[allow(unused_mut)]
639 let mut result = DateTime::from_unix_nanos(total)?.in_timezone_checked(self.zone)?;
640 #[cfg(feature = "std")]
641 {
642 result.monotonic = self.monotonic.and_then(|reading| reading.checked_add(duration));
643 }
644 Ok(result)
645 }
646
647 pub fn checked_sub(&self, duration: core::time::Duration) -> Result<DateTime, Error> {
653 let total = self
654 .unix_nanos()
655 .checked_sub(i128::from(duration.as_secs()) * 1_000_000_000)
656 .and_then(|v| v.checked_sub(i128::from(duration.subsec_nanos())))
657 .ok_or_else(|| error::out_of_range("result is outside the supported range"))?;
658 #[allow(unused_mut)]
659 let mut result = DateTime::from_unix_nanos(total)?.in_timezone_checked(self.zone)?;
660 #[cfg(feature = "std")]
661 {
662 result.monotonic = self.monotonic.and_then(|reading| reading.checked_sub(duration));
663 }
664 Ok(result)
665 }
666
667 #[must_use]
669 pub fn saturating_add(&self, duration: core::time::Duration) -> DateTime {
670 match self.checked_add(duration) {
671 Ok(dt) => dt,
672 Err(_) => DateTime::from_raw_checked(MAX_SECS, 999_999_999, self.zone)
673 .unwrap_or_else(|_| DateTime::from_raw(MAX_SECS, 999_999_999, self.offset, self.zone)),
674 }
675 }
676
677 #[must_use]
679 pub fn saturating_sub(&self, duration: core::time::Duration) -> DateTime {
680 match self.checked_sub(duration) {
681 Ok(dt) => dt,
682 Err(_) => DateTime::from_raw_checked(MIN_SECS, 0, self.zone)
683 .unwrap_or_else(|_| DateTime::from_raw(MIN_SECS, 0, self.offset, self.zone)),
684 }
685 }
686
687 pub fn duration_since(&self, earlier: DateTime) -> Result<core::time::Duration, Error> {
698 let (seconds, nanos) = self.signed_duration_since(earlier);
699 if seconds < 0 {
700 return Err(error::out_of_range("earlier is later than self"));
701 }
702 Ok(core::time::Duration::new(seconds as u64, nanos))
703 }
704
705 pub fn duration_until(&self, later: DateTime) -> Result<core::time::Duration, Error> {
714 later.duration_since(*self)
715 }
716
717 #[must_use]
725 pub fn signed_duration_since(&self, other: DateTime) -> (i64, u32) {
726 #[cfg(feature = "std")]
727 if let (Some(left), Some(right)) = (self.monotonic, other.monotonic) {
728 return difference_between(left, right);
729 }
730 let mut seconds = self.secs - other.secs;
731 let mut nanos = i64::from(self.nanos) - i64::from(other.nanos);
732 if nanos < 0 {
733 seconds -= 1;
734 nanos += 1_000_000_000;
735 }
736 (seconds, nanos as u32)
737 }
738
739 #[cfg(feature = "std")]
746 pub fn elapsed(&self) -> Result<core::time::Duration, Error> {
747 DateTime::now_in(self.zone).duration_since(*self)
748 }
749
750 #[cfg(feature = "std")]
754 #[must_use]
755 pub const fn has_monotonic(&self) -> bool {
756 self.monotonic.is_some()
757 }
758
759 #[must_use]
762 pub fn abbreviation(&self) -> Option<&'static str> {
763 self.zone
764 .info_at(self.secs)
765 .map(|info| info.abbrev)
766 .filter(|abbrev| !abbrev.is_empty())
767 }
768
769 #[must_use]
779 pub fn zone_bounds(&self) -> (Option<DateTime>, Option<DateTime>) {
780 let (start, end) = self.zone.bounds_at(self.secs);
781 (
782 start.and_then(|secs| DateTime::from_raw_checked(secs, 0, self.zone).ok()),
783 end.and_then(|secs| DateTime::from_raw_checked(secs, 0, self.zone).ok()),
784 )
785 }
786
787 pub fn add_days(&self, days: i64) -> Result<DateTime, Error> {
794 let (year, month, day, hour, minute, second, nanos) = self.parts();
795 let target = civil::days_from_civil(year, month, day)
796 .checked_add(days)
797 .ok_or_else(|| error::out_of_range("result is outside the supported range"))?;
798 let (year, month, day) = civil::civil_from_days(target);
799 DateTime::from_parts(year, month, day, hour, minute, second, nanos, self.zone)
800 }
801
802 pub fn add_months(&self, months: i32) -> Result<DateTime, Error> {
809 let (year, month, day, hour, minute, second, nanos) = self.parts();
810 let total = i64::from(year) * 12 + i64::from(month) - 1 + i64::from(months);
811 let new_year = i32::try_from(total.div_euclid(12))
812 .map_err(|_| error::out_of_range("result is outside the supported range"))?;
813 let new_month = (total.rem_euclid(12) + 1) as u8;
814 let new_day = day.min(civil::days_in_month(new_year, new_month));
815 DateTime::from_parts(new_year, new_month, new_day, hour, minute, second, nanos, self.zone)
816 }
817
818 pub fn add_years(&self, years: i32) -> Result<DateTime, Error> {
825 let (year, month, day, hour, minute, second, nanos) = self.parts();
826 let new_year = year
827 .checked_add(years)
828 .ok_or_else(|| error::out_of_range("result is outside the supported range"))?;
829 let new_day = day.min(civil::days_in_month(new_year, month));
830 DateTime::from_parts(new_year, month, new_day, hour, minute, second, nanos, self.zone)
831 }
832
833 fn in_timezone_checked(&self, zone: TimeZone) -> Result<DateTime, Error> {
834 if self.secs < MIN_SECS || self.secs > MAX_SECS {
835 return Err(error::out_of_range("instant is outside the supported range"));
836 }
837 Ok(self.in_timezone(zone))
838 }
839}
840
841#[cfg(feature = "std")]
844fn difference_between(left: std::time::Instant, right: std::time::Instant) -> (i64, u32) {
845 if let Some(duration) = left.checked_duration_since(right) {
846 (duration.as_secs() as i64, duration.subsec_nanos())
847 } else if let Some(duration) = right.checked_duration_since(left) {
848 let seconds = duration.as_secs() as i64;
849 if duration.subsec_nanos() == 0 {
850 (-seconds, 0)
851 } else {
852 (-seconds - 1, 1_000_000_000 - duration.subsec_nanos())
853 }
854 } else {
855 (0, 0)
856 }
857}
858
859impl Default for DateTime {
860 #[inline]
861 fn default() -> DateTime {
862 DateTime::UNIX_EPOCH
863 }
864}
865
866impl PartialEq for DateTime {
867 #[inline]
868 fn eq(&self, other: &DateTime) -> bool {
869 self.secs == other.secs && self.nanos == other.nanos
870 }
871}
872
873impl Eq for DateTime {}
874
875impl PartialOrd for DateTime {
876 #[inline]
877 fn partial_cmp(&self, other: &DateTime) -> Option<Ordering> {
878 Some(self.cmp(other))
879 }
880}
881
882impl Ord for DateTime {
883 #[inline]
884 fn cmp(&self, other: &DateTime) -> Ordering {
885 (self.secs, self.nanos).cmp(&(other.secs, other.nanos))
886 }
887}
888
889impl Hash for DateTime {
890 #[inline]
891 fn hash<H: Hasher>(&self, state: &mut H) {
892 self.secs.hash(state);
893 self.nanos.hash(state);
894 }
895}
896
897impl fmt::Display for DateTime {
898 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
899 format::write_datetime(f, self)
900 }
901}
902
903impl fmt::Debug for DateTime {
904 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
905 fmt::Display::fmt(self, f)
906 }
907}
908
909impl core::str::FromStr for DateTime {
910 type Err = Error;
911
912 fn from_str(s: &str) -> Result<DateTime, Error> {
913 parse::parse(s, None)
914 }
915}
916
917impl core::ops::Add<core::time::Duration> for DateTime {
918 type Output = DateTime;
919
920 fn add(self, rhs: core::time::Duration) -> DateTime {
922 self.checked_add(rhs).expect("DateTime + Duration overflowed")
923 }
924}
925
926impl core::ops::Sub<core::time::Duration> for DateTime {
927 type Output = DateTime;
928
929 fn sub(self, rhs: core::time::Duration) -> DateTime {
931 self.checked_sub(rhs).expect("DateTime - Duration overflowed")
932 }
933}
934
935impl core::ops::AddAssign<core::time::Duration> for DateTime {
936 fn add_assign(&mut self, rhs: core::time::Duration) {
937 *self = *self + rhs;
938 }
939}
940
941impl core::ops::SubAssign<core::time::Duration> for DateTime {
942 fn sub_assign(&mut self, rhs: core::time::Duration) {
943 *self = *self - rhs;
944 }
945}
946
947#[cfg(feature = "std")]
948impl TryFrom<std::time::SystemTime> for DateTime {
949 type Error = Error;
950
951 fn try_from(value: std::time::SystemTime) -> Result<DateTime, Error> {
957 let (seconds, nanos) = match value.duration_since(std::time::UNIX_EPOCH) {
958 Ok(duration) => {
959 let seconds = i64::try_from(duration.as_secs())
960 .map_err(|_| error::out_of_range("instant is outside the supported range"))?;
961 (seconds, duration.subsec_nanos())
962 }
963 Err(err) => {
964 let duration = err.duration();
965 let seconds = i64::try_from(duration.as_secs())
966 .map_err(|_| error::out_of_range("instant is outside the supported range"))?;
967 if duration.subsec_nanos() == 0 {
968 (-seconds, 0)
969 } else {
970 (-seconds - 1, 1_000_000_000 - duration.subsec_nanos())
971 }
972 }
973 };
974 DateTime::from_unix_nanos(i128::from(seconds) * 1_000_000_000 + i128::from(nanos))
975 }
976}
977
978#[cfg(feature = "std")]
979impl From<DateTime> for std::time::SystemTime {
980 fn from(dt: DateTime) -> std::time::SystemTime {
986 let nanos = dt.unix_nanos();
987 if nanos >= 0 {
988 let seconds = (nanos / 1_000_000_000) as u64;
989 let subsec = (nanos % 1_000_000_000) as u32;
990 std::time::UNIX_EPOCH
991 .checked_add(core::time::Duration::new(seconds, subsec))
992 .unwrap_or(std::time::UNIX_EPOCH)
993 } else {
994 let magnitude = -nanos;
995 let seconds = (magnitude / 1_000_000_000) as u64;
996 let subsec = (magnitude % 1_000_000_000) as u32;
997 std::time::UNIX_EPOCH
998 .checked_sub(core::time::Duration::new(seconds, subsec))
999 .unwrap_or(std::time::UNIX_EPOCH)
1000 }
1001 }
1002}