1use crate::date_time::format::rfc3339::AllowOffsets;
9use crate::date_time::format::DateTimeParseErrorKind;
10use num_integer::div_mod_floor;
11use num_integer::Integer;
12use std::cmp::Ordering;
13use std::error::Error as StdError;
14use std::fmt;
15use std::fmt::Display;
16use std::time::Duration;
17use std::time::SystemTime;
18use std::time::UNIX_EPOCH;
19
20#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
21mod de;
22mod format;
23#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
24mod ser;
25
26pub use self::format::DateTimeFormatError;
27pub use self::format::DateTimeParseError;
28
29const MILLIS_PER_SECOND: i64 = 1000;
30const NANOS_PER_MILLI: u32 = 1_000_000;
31const NANOS_PER_SECOND: i128 = 1_000_000_000;
32const NANOS_PER_SECOND_U32: u32 = 1_000_000_000;
33
34#[derive(PartialEq, Eq, Hash, Clone, Copy)]
57pub struct DateTime {
58 pub(crate) seconds: i64,
59 pub(crate) subsecond_nanos: u32,
63}
64
65impl DateTime {
68 pub fn from_secs(epoch_seconds: i64) -> Self {
70 DateTime {
71 seconds: epoch_seconds,
72 subsecond_nanos: 0,
73 }
74 }
75
76 pub fn from_millis(epoch_millis: i64) -> DateTime {
78 let (seconds, millis) = div_mod_floor(epoch_millis, MILLIS_PER_SECOND);
79 DateTime::from_secs_and_nanos(seconds, millis as u32 * NANOS_PER_MILLI)
80 }
81
82 pub fn from_nanos(epoch_nanos: i128) -> Result<Self, ConversionError> {
84 let (seconds, subsecond_nanos) = epoch_nanos.div_mod_floor(&NANOS_PER_SECOND);
85 let seconds = i64::try_from(seconds).map_err(|_| {
86 ConversionError("given epoch nanos are too large to fit into a DateTime")
87 })?;
88 let subsecond_nanos = subsecond_nanos as u32; Ok(DateTime {
90 seconds,
91 subsecond_nanos,
92 })
93 }
94
95 pub fn as_nanos(&self) -> i128 {
97 let seconds = self.seconds as i128 * NANOS_PER_SECOND;
98 seconds + self.subsecond_nanos as i128
99 }
100
101 pub fn from_fractional_secs(mut epoch_seconds: i64, fraction: f64) -> Self {
112 let mut subsecond_nanos = (fraction * 1_000_000_000_f64) as u32;
115 if subsecond_nanos == 1_000_000_000 {
116 epoch_seconds += 1;
117 subsecond_nanos = 0;
118 }
119 DateTime::from_secs_and_nanos(epoch_seconds, subsecond_nanos)
120 }
121
122 pub fn from_secs_and_nanos(seconds: i64, subsecond_nanos: u32) -> Self {
136 if subsecond_nanos >= 1_000_000_000 {
137 panic!("{} is > 1_000_000_000", subsecond_nanos)
138 }
139 DateTime {
140 seconds,
141 subsecond_nanos,
142 }
143 }
144
145 pub fn as_secs_f64(&self) -> f64 {
149 self.seconds as f64 + self.subsecond_nanos as f64 / 1_000_000_000_f64
150 }
151
152 pub fn from_secs_f64(epoch_seconds: f64) -> Self {
163 let seconds = epoch_seconds.floor() as i64;
164 let rem = epoch_seconds - epoch_seconds.floor();
165 DateTime::from_fractional_secs(seconds, rem)
166 }
167
168 pub fn from_str(s: &str, format: Format) -> Result<Self, DateTimeParseError> {
170 match format {
171 Format::DateTime => format::rfc3339::parse(s, AllowOffsets::OffsetsForbidden),
172 Format::DateTimeWithOffset => format::rfc3339::parse(s, AllowOffsets::OffsetsAllowed),
173 Format::HttpDate => format::http_date::parse(s),
174 Format::EpochSeconds => format::epoch_seconds::parse(s),
175 }
176 }
177
178 pub fn has_subsec_nanos(&self) -> bool {
180 self.subsecond_nanos != 0
181 }
182
183 pub fn secs(&self) -> i64 {
187 self.seconds
188 }
189
190 pub fn set_seconds(&mut self, seconds: i64) -> &mut Self {
192 self.seconds = seconds;
193 self
194 }
195
196 pub fn subsec_nanos(&self) -> u32 {
200 self.subsecond_nanos
201 }
202
203 pub fn set_subsec_nanos(&mut self, subsec_nanos: u32) -> &mut Self {
205 self.subsecond_nanos = subsec_nanos;
206 self
207 }
208
209 pub fn to_millis(self) -> Result<i64, ConversionError> {
214 let subsec_millis =
215 Integer::div_floor(&i64::from(self.subsecond_nanos), &(NANOS_PER_MILLI as i64));
216 if self.seconds < 0 {
217 self.seconds
218 .checked_add(1)
219 .and_then(|seconds| seconds.checked_mul(MILLIS_PER_SECOND))
220 .and_then(|millis| millis.checked_sub(1000 - subsec_millis))
221 } else {
222 self.seconds
223 .checked_mul(MILLIS_PER_SECOND)
224 .and_then(|millis| millis.checked_add(subsec_millis))
225 }
226 .ok_or(ConversionError(
227 "DateTime value too large to fit into i64 epoch millis",
228 ))
229 }
230
231 pub fn read(s: &str, format: Format, delim: char) -> Result<(Self, &str), DateTimeParseError> {
235 let (inst, next) = match format {
236 Format::DateTime => format::rfc3339::read(s, AllowOffsets::OffsetsForbidden)?,
237 Format::DateTimeWithOffset => format::rfc3339::read(s, AllowOffsets::OffsetsAllowed)?,
238 Format::HttpDate => format::http_date::read(s)?,
239 Format::EpochSeconds => {
240 let split_point = s.find(delim).unwrap_or(s.len());
241 let (s, rest) = s.split_at(split_point);
242 (Self::from_str(s, format)?, rest)
243 }
244 };
245 if next.is_empty() {
246 Ok((inst, next))
247 } else if next.starts_with(delim) {
248 Ok((inst, &next[1..]))
249 } else {
250 Err(DateTimeParseErrorKind::Invalid("didn't find expected delimiter".into()).into())
251 }
252 }
253
254 pub fn fmt(&self, format: Format) -> Result<String, DateTimeFormatError> {
258 match format {
259 Format::DateTime | Format::DateTimeWithOffset => format::rfc3339::format(self),
260 Format::EpochSeconds => Ok(format::epoch_seconds::format(self)),
261 Format::HttpDate => format::http_date::format(self),
262 }
263 }
264}
265
266impl TryFrom<DateTime> for SystemTime {
275 type Error = ConversionError;
276
277 fn try_from(date_time: DateTime) -> Result<Self, Self::Error> {
278 if date_time.secs() < 0 {
279 let mut secs = date_time.secs().unsigned_abs();
280 let mut nanos = date_time.subsec_nanos();
281 if date_time.has_subsec_nanos() {
282 secs -= 1;
284 nanos = NANOS_PER_SECOND_U32 - nanos;
286 }
287 UNIX_EPOCH
288 .checked_sub(Duration::new(secs, nanos))
289 .ok_or(ConversionError(
290 "overflow occurred when subtracting duration from UNIX_EPOCH",
291 ))
292 } else {
293 UNIX_EPOCH
294 .checked_add(Duration::new(
295 date_time.secs().unsigned_abs(),
296 date_time.subsec_nanos(),
297 ))
298 .ok_or(ConversionError(
299 "overflow occurred when adding duration to UNIX_EPOCH",
300 ))
301 }
302 }
303}
304
305impl From<SystemTime> for DateTime {
306 fn from(time: SystemTime) -> Self {
307 if time < UNIX_EPOCH {
308 let duration = UNIX_EPOCH.duration_since(time).expect("time < UNIX_EPOCH");
309 let mut secs = -(duration.as_secs() as i128);
310 let mut nanos = duration.subsec_nanos() as i128;
311 if nanos != 0 {
312 secs -= 1;
313 nanos = NANOS_PER_SECOND - nanos;
314 }
315 DateTime::from_nanos(secs * NANOS_PER_SECOND + nanos)
316 .expect("SystemTime has same precision as DateTime")
317 } else {
318 let duration = time.duration_since(UNIX_EPOCH).expect("UNIX_EPOCH <= time");
319 DateTime::from_secs_and_nanos(
320 i64::try_from(duration.as_secs())
321 .expect("SystemTime has same precision as DateTime"),
322 duration.subsec_nanos(),
323 )
324 }
325 }
326}
327
328impl PartialOrd for DateTime {
329 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
330 Some(self.cmp(other))
331 }
332}
333
334impl Ord for DateTime {
335 fn cmp(&self, other: &Self) -> Ordering {
336 self.as_nanos().cmp(&other.as_nanos())
337 }
338}
339
340impl Display for DateTime {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 let date = match self.fmt(Format::DateTime) {
345 Ok(date) => date,
346 Err(_err) => format::epoch_seconds::format(self),
347 };
348 write!(f, "{}", date)
349 }
350}
351
352impl fmt::Debug for DateTime {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 fmt::Display::fmt(self, f)
355 }
356}
357#[derive(Debug)]
359#[non_exhaustive]
360pub struct ConversionError(&'static str);
361
362impl StdError for ConversionError {}
363
364impl fmt::Display for ConversionError {
365 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366 write!(f, "{}", self.0)
367 }
368}
369
370#[derive(Clone, Copy, Debug, Eq, PartialEq)]
372pub enum Format {
373 DateTime,
376
377 DateTimeWithOffset,
380
381 HttpDate,
384
385 EpochSeconds,
388}
389
390#[cfg(test)]
391mod test {
392 use crate::date_time::Format;
393 use crate::DateTime;
394 use proptest::proptest;
395 use std::time::SystemTime;
396 use time::format_description::well_known::Rfc3339;
397 use time::OffsetDateTime;
398
399 #[test]
400 fn test_display_date_time() {
401 let date_time = DateTime::from_secs(1576540098);
402 assert_eq!(format!("{}", date_time), "2019-12-16T23:48:18Z");
403
404 let date_time = DateTime::from_fractional_secs(1576540098, 0.52);
405 assert_eq!(format!("{}", date_time), "2019-12-16T23:48:18.52Z");
406
407 let date_time = DateTime::from_secs(1699942527);
408 assert_eq!(format!("{}", date_time), "2023-11-14T06:15:27Z");
409
410 let date_time = DateTime::from_secs(16995123);
411 assert_eq!(format!("{}", date_time), "1970-07-16T16:52:03Z");
412 }
413
414 #[test]
415 fn test_debug_date_time() {
416 let date_time = DateTime::from_secs(1576540098);
417 assert_eq!(format!("{:?}", date_time), "2019-12-16T23:48:18Z");
418
419 let date_time = DateTime::from_fractional_secs(1576540098, 0.52);
420 assert_eq!(format!("{:?}", date_time), "2019-12-16T23:48:18.52Z");
421
422 let date_time = DateTime::from_secs(1699942527);
423 assert_eq!(format!("{:?}", date_time), "2023-11-14T06:15:27Z");
424
425 let date_time = DateTime::from_secs(16995123);
426 assert_eq!(format!("{:?}", date_time), "1970-07-16T16:52:03Z");
427 }
428
429 #[test]
430 fn test_fmt() {
431 let date_time = DateTime::from_secs(1576540098);
432 assert_eq!(
433 date_time.fmt(Format::DateTime).unwrap(),
434 "2019-12-16T23:48:18Z"
435 );
436 assert_eq!(date_time.fmt(Format::EpochSeconds).unwrap(), "1576540098");
437 assert_eq!(
438 date_time.fmt(Format::HttpDate).unwrap(),
439 "Mon, 16 Dec 2019 23:48:18 GMT"
440 );
441
442 let date_time = DateTime::from_fractional_secs(1576540098, 0.52);
443 assert_eq!(
444 date_time.fmt(Format::DateTime).unwrap(),
445 "2019-12-16T23:48:18.52Z"
446 );
447 assert_eq!(
448 date_time.fmt(Format::EpochSeconds).unwrap(),
449 "1576540098.52"
450 );
451 assert_eq!(
452 date_time.fmt(Format::HttpDate).unwrap(),
453 "Mon, 16 Dec 2019 23:48:18 GMT"
454 );
455 }
456
457 #[test]
458 fn test_fmt_zero_seconds() {
459 let date_time = DateTime::from_secs(1576540080);
460 assert_eq!(
461 date_time.fmt(Format::DateTime).unwrap(),
462 "2019-12-16T23:48:00Z"
463 );
464 assert_eq!(date_time.fmt(Format::EpochSeconds).unwrap(), "1576540080");
465 assert_eq!(
466 date_time.fmt(Format::HttpDate).unwrap(),
467 "Mon, 16 Dec 2019 23:48:00 GMT"
468 );
469 }
470
471 #[test]
472 fn test_read_single_http_date() {
473 let s = "Mon, 16 Dec 2019 23:48:18 GMT";
474 let (_, next) = DateTime::read(s, Format::HttpDate, ',').expect("valid");
475 assert_eq!(next, "");
476 }
477
478 #[test]
479 fn test_read_single_float() {
480 let s = "1576540098.52";
481 let (_, next) = DateTime::read(s, Format::EpochSeconds, ',').expect("valid");
482 assert_eq!(next, "");
483 }
484
485 #[test]
486 fn test_read_many_float() {
487 let s = "1576540098.52,1576540098.53";
488 let (_, next) = DateTime::read(s, Format::EpochSeconds, ',').expect("valid");
489 assert_eq!(next, "1576540098.53");
490 }
491
492 #[test]
493 fn test_ready_many_http_date() {
494 let s = "Mon, 16 Dec 2019 23:48:18 GMT,Tue, 17 Dec 2019 23:48:18 GMT";
495 let (_, next) = DateTime::read(s, Format::HttpDate, ',').expect("valid");
496 assert_eq!(next, "Tue, 17 Dec 2019 23:48:18 GMT");
497 }
498
499 #[derive(Debug)]
500 struct EpochMillisTestCase {
501 _rfc3339: &'static str,
502 epoch_millis: i64,
503 epoch_seconds: i64,
504 epoch_subsec_nanos: u32,
505 }
506
507 const EPOCH_MILLIS_TEST_CASES: &[EpochMillisTestCase] = &[
515 EpochMillisTestCase {
516 _rfc3339: "2021-07-30T21:20:04.123Z",
517 epoch_millis: 1627680004123,
518 epoch_seconds: 1627680004,
519 epoch_subsec_nanos: 123000000,
520 },
521 EpochMillisTestCase {
522 _rfc3339: "1918-06-04T02:39:55.877Z",
523 epoch_millis: -1627680004123,
524 epoch_seconds: -1627680005,
525 epoch_subsec_nanos: 877000000,
526 },
527 EpochMillisTestCase {
528 _rfc3339: "+292278994-08-17T07:12:55.807Z",
529 epoch_millis: i64::MAX,
530 epoch_seconds: 9223372036854775,
531 epoch_subsec_nanos: 807000000,
532 },
533 EpochMillisTestCase {
534 _rfc3339: "-292275055-05-16T16:47:04.192Z",
535 epoch_millis: i64::MIN,
536 epoch_seconds: -9223372036854776,
537 epoch_subsec_nanos: 192000000,
538 },
539 ];
540
541 #[test]
542 fn to_millis() {
543 for test_case in EPOCH_MILLIS_TEST_CASES {
544 println!("Test case: {:?}", test_case);
545 let date_time = DateTime::from_secs_and_nanos(
546 test_case.epoch_seconds,
547 test_case.epoch_subsec_nanos,
548 );
549 assert_eq!(test_case.epoch_seconds, date_time.secs());
550 assert_eq!(test_case.epoch_subsec_nanos, date_time.subsec_nanos());
551 assert_eq!(test_case.epoch_millis, date_time.to_millis().unwrap());
552 }
553
554 assert!(DateTime::from_secs_and_nanos(i64::MAX, 0)
555 .to_millis()
556 .is_err());
557 }
558
559 #[test]
560 fn from_millis() {
561 for test_case in EPOCH_MILLIS_TEST_CASES {
562 println!("Test case: {:?}", test_case);
563 let date_time = DateTime::from_millis(test_case.epoch_millis);
564 assert_eq!(test_case.epoch_seconds, date_time.secs());
565 assert_eq!(test_case.epoch_subsec_nanos, date_time.subsec_nanos());
566 }
567 }
568
569 #[test]
570 fn to_from_millis_round_trip() {
571 for millis in &[0, 1627680004123, -1627680004123, i64::MAX, i64::MIN] {
572 assert_eq!(*millis, DateTime::from_millis(*millis).to_millis().unwrap());
573 }
574 }
575
576 #[test]
577 fn as_nanos() {
578 assert_eq!(
579 -9_223_372_036_854_775_807_000_000_001_i128,
580 DateTime::from_secs_and_nanos(i64::MIN, 999_999_999).as_nanos()
581 );
582 assert_eq!(
583 -10_876_543_211,
584 DateTime::from_secs_and_nanos(-11, 123_456_789).as_nanos()
585 );
586 assert_eq!(0, DateTime::from_secs_and_nanos(0, 0).as_nanos());
587 assert_eq!(
588 11_123_456_789,
589 DateTime::from_secs_and_nanos(11, 123_456_789).as_nanos()
590 );
591 assert_eq!(
592 9_223_372_036_854_775_807_999_999_999_i128,
593 DateTime::from_secs_and_nanos(i64::MAX, 999_999_999).as_nanos()
594 );
595 }
596
597 #[test]
598 fn from_nanos() {
599 assert_eq!(
600 DateTime::from_secs_and_nanos(i64::MIN, 999_999_999),
601 DateTime::from_nanos(-9_223_372_036_854_775_807_000_000_001_i128).unwrap(),
602 );
603 assert_eq!(
604 DateTime::from_secs_and_nanos(-11, 123_456_789),
605 DateTime::from_nanos(-10_876_543_211).unwrap(),
606 );
607 assert_eq!(
608 DateTime::from_secs_and_nanos(0, 0),
609 DateTime::from_nanos(0).unwrap(),
610 );
611 assert_eq!(
612 DateTime::from_secs_and_nanos(11, 123_456_789),
613 DateTime::from_nanos(11_123_456_789).unwrap(),
614 );
615 assert_eq!(
616 DateTime::from_secs_and_nanos(i64::MAX, 999_999_999),
617 DateTime::from_nanos(9_223_372_036_854_775_807_999_999_999_i128).unwrap(),
618 );
619 assert!(DateTime::from_nanos(-10_000_000_000_000_000_000_999_999_999_i128).is_err());
620 assert!(DateTime::from_nanos(10_000_000_000_000_000_000_999_999_999_i128).is_err());
621 }
622
623 #[cfg(not(any(target_arch = "powerpc", target_arch = "x86")))]
625 #[test]
626 fn system_time_conversions() {
627 let date_time = DateTime::from_str("1000-01-02T01:23:10.123Z", Format::DateTime).unwrap();
629 let off_date_time = OffsetDateTime::parse("1000-01-02T01:23:10.123Z", &Rfc3339).unwrap();
630 assert_eq!(
631 SystemTime::from(off_date_time),
632 SystemTime::try_from(date_time).unwrap()
633 );
634
635 let date_time = DateTime::from_str("2039-10-31T23:23:10.456Z", Format::DateTime).unwrap();
636 let off_date_time = OffsetDateTime::parse("2039-10-31T23:23:10.456Z", &Rfc3339).unwrap();
637 assert_eq!(
638 SystemTime::from(off_date_time),
639 SystemTime::try_from(date_time).unwrap()
640 );
641 }
642
643 #[test]
644 fn formatting_of_early_dates() {
645 let date: DateTime =
646 DateTime::from_str("Mon, 16 Dec -019 23:48:18 GMT", Format::HttpDate).unwrap();
647 assert_eq!(format!("{}", date), "-62736509502");
648 }
649
650 #[test]
651 fn ord() {
652 let first = DateTime::from_secs_and_nanos(-1, 0);
653 let second = DateTime::from_secs_and_nanos(-1, 1);
654 let third = DateTime::from_secs_and_nanos(0, 0);
655 let fourth = DateTime::from_secs_and_nanos(0, 1);
656 let fifth = DateTime::from_secs_and_nanos(1, 0);
657
658 assert!(first == first);
659 assert!(first < second);
660 assert!(first < third);
661 assert!(first < fourth);
662 assert!(first < fifth);
663
664 assert!(second > first);
665 assert!(second == second);
666 assert!(second < third);
667 assert!(second < fourth);
668 assert!(second < fifth);
669
670 assert!(third > first);
671 assert!(third > second);
672 assert!(third == third);
673 assert!(third < fourth);
674 assert!(third < fifth);
675
676 assert!(fourth > first);
677 assert!(fourth > second);
678 assert!(fourth > third);
679 assert!(fourth == fourth);
680 assert!(fourth < fifth);
681
682 assert!(fifth > first);
683 assert!(fifth > second);
684 assert!(fifth > third);
685 assert!(fifth > fourth);
686 assert!(fifth == fifth);
687 }
688
689 #[test]
691 fn panic_in_fromsecs_f64() {
692 assert_eq!(DateTime::from_secs_f64(-1.0), DateTime::from_secs(-1));
693
694 assert_eq!(
695 DateTime::from_secs_f64(-1.95877825437922e-309),
696 DateTime::from_secs(0)
697 );
698 }
699
700 const MIN_RFC_3339_MILLIS: i64 = -62135596800000;
701 const MAX_RFC_3339_MILLIS: i64 = 253402300799999;
702
703 proptest! {
705 #[test]
706 fn ord_proptest(
707 left_millis in MIN_RFC_3339_MILLIS..MAX_RFC_3339_MILLIS,
708 right_millis in MIN_RFC_3339_MILLIS..MAX_RFC_3339_MILLIS,
709 ) {
710 let left = DateTime::from_millis(left_millis);
711 let right = DateTime::from_millis(right_millis);
712
713 let left_str = left.fmt(Format::DateTime).unwrap();
714 let right_str = right.fmt(Format::DateTime).unwrap();
715
716 assert_eq!(left.cmp(&right), left_str.cmp(&right_str));
717 }
718 }
719
720 proptest! {
721 #[test]
722 fn from_secs_f64_proptest(secs: f64) {
723 let _date = DateTime::from_secs_f64(secs);
724 }
725 }
726}