1use std::cmp;
2use std::convert::{TryFrom, TryInto};
3use std::fmt;
4use std::time::{SystemTime, Duration as SystemDuration, UNIX_EPOCH};
5use std::u32;
6
7#[cfg(test)]
8use quickcheck::{Arbitrary, Gen};
9
10use crate::{
11 Error,
12 Result,
13};
14
15#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
65pub struct Timestamp(u32);
66assert_send_and_sync!(Timestamp);
67
68impl From<Timestamp> for u32 {
69 fn from(t: Timestamp) -> Self {
70 t.0
71 }
72}
73
74impl From<u32> for Timestamp {
75 fn from(t: u32) -> Self {
76 Timestamp(t)
77 }
78}
79
80impl TryFrom<SystemTime> for Timestamp {
81 type Error = anyhow::Error;
82
83 fn try_from(t: SystemTime) -> Result<Self> {
84 match t.duration_since(std::time::UNIX_EPOCH) {
85 Ok(d) if d.as_secs() <= std::u32::MAX as u64 =>
86 Ok(Timestamp(d.as_secs() as u32)),
87 _ => Err(Error::InvalidArgument(
88 format!("time {:?} is not representable in OpenPGP", t))
89 .into()),
90 }
91 }
92}
93
94impl From<Timestamp> for SystemTime {
100 fn from(t: Timestamp) -> Self {
101 UNIX_EPOCH.checked_add(SystemDuration::new(t.0 as u64, 0))
102 .unwrap_or_else(|| UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0))
103 }
104}
105
106impl From<Timestamp> for Option<SystemTime> {
107 fn from(t: Timestamp) -> Self {
108 Some(t.into())
109 }
110}
111
112impl fmt::Display for Timestamp {
113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 write!(f, "{}", crate::fmt::time(&SystemTime::from(*self)))
115 }
116}
117
118impl fmt::Debug for Timestamp {
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 write!(f, "{}", self.0)
121 }
122}
123
124impl Timestamp {
125 pub fn now() -> Timestamp {
127 crate::now().try_into()
128 .expect("representable for the next hundred years")
129 }
130
131 pub fn checked_add(&self, d: Duration) -> Option<Timestamp> {
136 self.0.checked_add(d.0).map(Self)
137 }
138
139 pub fn checked_sub(&self, d: Duration) -> Option<Timestamp> {
144 self.0.checked_sub(d.0).map(Self)
145 }
146
147 pub fn round_down<P, F>(&self, precision: P, floor: F) -> Result<Timestamp>
234 where P: Into<Option<u8>>,
235 F: Into<Option<SystemTime>>
236 {
237 let p = precision.into().unwrap_or(21) as u32;
238 if p < 32 {
239 let rounded = Self(self.0 & !((1 << p) - 1));
240 match floor.into() {
241 Some(floor) => {
242 Ok(cmp::max(rounded, floor.try_into()?))
243 }
244 None => { Ok(rounded) }
245 }
246 } else {
247 Err(Error::InvalidArgument(
248 format!("Invalid precision {}", p)).into())
249 }
250 }
251}
252
253#[cfg(test)]
254impl Arbitrary for Timestamp {
255 fn arbitrary(g: &mut Gen) -> Self {
256 Timestamp(u32::arbitrary(g))
257 }
258}
259
260#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
288pub struct Duration(u32);
289assert_send_and_sync!(Duration);
290
291impl From<Duration> for u32 {
292 fn from(d: Duration) -> Self {
293 d.0
294 }
295}
296
297impl From<u32> for Duration {
298 fn from(d: u32) -> Self {
299 Duration(d)
300 }
301}
302
303impl TryFrom<SystemDuration> for Duration {
304 type Error = anyhow::Error;
305
306 fn try_from(d: SystemDuration) -> Result<Self> {
307 if d.as_secs() <= std::u32::MAX as u64 {
308 Ok(Duration(d.as_secs() as u32))
309 } else {
310 Err(Error::InvalidArgument(
311 format!("Duration exceeds u32: {:?}", d))
312 .into())
313 }
314 }
315}
316
317impl From<Duration> for SystemDuration {
318 fn from(d: Duration) -> Self {
319 SystemDuration::new(d.0 as u64, 0)
320 }
321}
322
323impl From<Duration> for Option<SystemDuration> {
324 fn from(d: Duration) -> Self {
325 Some(d.into())
326 }
327}
328
329impl fmt::Debug for Duration {
330 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331 write!(f, "{:?}", SystemDuration::from(*self))
332 }
333}
334
335impl Duration {
336 pub const fn seconds(n: u32) -> Duration {
338 Self(n)
339 }
340
341 pub fn minutes(n: u32) -> Result<Duration> {
344 match 60u32.checked_mul(n) {
345 Some(val) => Ok(Self::seconds(val)),
346 None => {
347 Err(Error::InvalidArgument(format!(
348 "Not representable: {} minutes in seconds exceeds u32",
349 n
350 )).into())
351 }
352 }
353 }
354
355 pub fn hours(n: u32) -> Result<Duration> {
358 match 60u32.checked_mul(n) {
359 Some(val) => Self::minutes(val),
360 None => {
361 Err(Error::InvalidArgument(format!(
362 "Not representable: {} hours in seconds exceeds u32",
363 n
364 )).into())
365 }
366 }
367 }
368
369 pub fn days(n: u32) -> Result<Duration> {
372 match 24u32.checked_mul(n) {
373 Some(val) => Self::hours(val),
374 None => {
375 Err(Error::InvalidArgument(format!(
376 "Not representable: {} days in seconds exceeds u32",
377 n
378 )).into())
379 }
380 }
381 }
382
383 pub fn weeks(n: u32) -> Result<Duration> {
386 match 7u32.checked_mul(n) {
387 Some(val) => Self::days(val),
388 None => {
389 Err(Error::InvalidArgument(format!(
390 "Not representable: {} weeks in seconds exceeds u32",
391 n
392 )).into())
393 }
394 }
395 }
396
397 pub fn years(n: u32) -> Result<Duration> {
406 let s = (365.2425 * n as f64).trunc();
407 if s > u32::MAX as f64 {
408 Err(Error::InvalidArgument(
409 format!("Not representable: {} years in seconds exceeds u32",
410 n))
411 .into())
412 } else {
413 Ok((s as u32).into())
414 }
415 }
416
417 pub fn as_secs(self) -> u64 {
419 self.0 as u64
420 }
421
422 pub fn round_up<P, C>(&self, precision: P, ceil: C) -> Result<Duration>
440 where P: Into<Option<u8>>,
441 C: Into<Option<SystemDuration>>
442 {
443 let p = precision.into().unwrap_or(21) as u32;
444 if p < 32 {
445 if let Some(sum) = self.0.checked_add((1 << p) - 1) {
446 let rounded = Self(sum & !((1 << p) - 1));
447 match ceil.into() {
448 Some(ceil) => {
449 Ok(cmp::min(rounded, ceil.try_into()?))
450 },
451 None => Ok(rounded)
452 }
453 } else {
454 Ok(Self(std::u32::MAX))
455 }
456 } else {
457 Err(Error::InvalidArgument(
458 format!("Invalid precision {}", p)).into())
459 }
460 }
461}
462
463#[allow(unused)]
464impl Timestamp {
465 pub(crate) const UNIX_EPOCH : Timestamp = Timestamp(0);
466 pub(crate) const MAX : Timestamp = Timestamp(u32::MAX);
467
468 pub(crate) const Y1970 : Timestamp = Timestamp(0);
469 pub(crate) const Y1970M2 : Timestamp = Timestamp(2678400);
471 pub(crate) const Y1971M2 : Timestamp = Timestamp(34214400);
472 pub(crate) const Y1972M2 : Timestamp = Timestamp(65750400);
473 pub(crate) const Y1973M2 : Timestamp = Timestamp(97372800);
474 pub(crate) const Y1974M2 : Timestamp = Timestamp(128908800);
475 pub(crate) const Y1975M2 : Timestamp = Timestamp(160444800);
476 pub(crate) const Y1976M2 : Timestamp = Timestamp(191980800);
477 pub(crate) const Y1977M2 : Timestamp = Timestamp(223603200);
478 pub(crate) const Y1978M2 : Timestamp = Timestamp(255139200);
479 pub(crate) const Y1979M2 : Timestamp = Timestamp(286675200);
480 pub(crate) const Y1980M2 : Timestamp = Timestamp(318211200);
481 pub(crate) const Y1981M2 : Timestamp = Timestamp(349833600);
482 pub(crate) const Y1982M2 : Timestamp = Timestamp(381369600);
483 pub(crate) const Y1983M2 : Timestamp = Timestamp(412905600);
484 pub(crate) const Y1984M2 : Timestamp = Timestamp(444441600);
485 pub(crate) const Y1985M2 : Timestamp = Timestamp(476064000);
486 pub(crate) const Y1986M2 : Timestamp = Timestamp(507600000);
487 pub(crate) const Y1987M2 : Timestamp = Timestamp(539136000);
488 pub(crate) const Y1988M2 : Timestamp = Timestamp(570672000);
489 pub(crate) const Y1989M2 : Timestamp = Timestamp(602294400);
490 pub(crate) const Y1990M2 : Timestamp = Timestamp(633830400);
491 pub(crate) const Y1991M2 : Timestamp = Timestamp(665366400);
492 pub(crate) const Y1992M2 : Timestamp = Timestamp(696902400);
493 pub(crate) const Y1993M2 : Timestamp = Timestamp(728524800);
494 pub(crate) const Y1994M2 : Timestamp = Timestamp(760060800);
495 pub(crate) const Y1995M2 : Timestamp = Timestamp(791596800);
496 pub(crate) const Y1996M2 : Timestamp = Timestamp(823132800);
497 pub(crate) const Y1997M2 : Timestamp = Timestamp(854755200);
498 pub(crate) const Y1998M2 : Timestamp = Timestamp(886291200);
499 pub(crate) const Y1999M2 : Timestamp = Timestamp(917827200);
500 pub(crate) const Y2000M2 : Timestamp = Timestamp(949363200);
501 pub(crate) const Y2001M2 : Timestamp = Timestamp(980985600);
502 pub(crate) const Y2002M2 : Timestamp = Timestamp(1012521600);
503 pub(crate) const Y2003M2 : Timestamp = Timestamp(1044057600);
504 pub(crate) const Y2004M2 : Timestamp = Timestamp(1075593600);
505 pub(crate) const Y2005M2 : Timestamp = Timestamp(1107216000);
506 pub(crate) const Y2006M2 : Timestamp = Timestamp(1138752000);
507 pub(crate) const Y2007M2 : Timestamp = Timestamp(1170288000);
508 pub(crate) const Y2008M2 : Timestamp = Timestamp(1201824000);
509 pub(crate) const Y2009M2 : Timestamp = Timestamp(1233446400);
510 pub(crate) const Y2010M2 : Timestamp = Timestamp(1264982400);
511 pub(crate) const Y2011M2 : Timestamp = Timestamp(1296518400);
512 pub(crate) const Y2012M2 : Timestamp = Timestamp(1328054400);
513 pub(crate) const Y2013M2 : Timestamp = Timestamp(1359676800);
514 pub(crate) const Y2014M2 : Timestamp = Timestamp(1391212800);
515 pub(crate) const Y2015M2 : Timestamp = Timestamp(1422748800);
516 pub(crate) const Y2016M2 : Timestamp = Timestamp(1454284800);
517 pub(crate) const Y2017M2 : Timestamp = Timestamp(1485907200);
518 pub(crate) const Y2018M2 : Timestamp = Timestamp(1517443200);
519 pub(crate) const Y2019M2 : Timestamp = Timestamp(1548979200);
520 pub(crate) const Y2020M2 : Timestamp = Timestamp(1580515200);
521 pub(crate) const Y2021M2 : Timestamp = Timestamp(1612137600);
522 pub(crate) const Y2022M2 : Timestamp = Timestamp(1643673600);
523 pub(crate) const Y2023M2 : Timestamp = Timestamp(1675209600);
524 pub(crate) const Y2024M2 : Timestamp = Timestamp(1706745600);
525 pub(crate) const Y2025M2 : Timestamp = Timestamp(1738368000);
526 pub(crate) const Y2026M2 : Timestamp = Timestamp(1769904000);
527 pub(crate) const Y2027M2 : Timestamp = Timestamp(1801440000);
528 pub(crate) const Y2028M2 : Timestamp = Timestamp(1832976000);
529 pub(crate) const Y2029M2 : Timestamp = Timestamp(1864598400);
530 pub(crate) const Y2030M2 : Timestamp = Timestamp(1896134400);
531 pub(crate) const Y2031M2 : Timestamp = Timestamp(1927670400);
532 pub(crate) const Y2032M2 : Timestamp = Timestamp(1959206400);
533 pub(crate) const Y2033M2 : Timestamp = Timestamp(1990828800);
534 pub(crate) const Y2034M2 : Timestamp = Timestamp(2022364800);
535 pub(crate) const Y2035M2 : Timestamp = Timestamp(2053900800);
536 pub(crate) const Y2036M2 : Timestamp = Timestamp(2085436800);
537 pub(crate) const Y2037M2 : Timestamp = Timestamp(2117059200);
538 pub(crate) const Y2038M2 : Timestamp = Timestamp(2148595200);
539 pub(crate) const Y2039M2 : Timestamp = Timestamp(2180131200);
540 pub(crate) const Y2040M2 : Timestamp = Timestamp(2211667200);
541 pub(crate) const Y2041M2 : Timestamp = Timestamp(2243289600);
542 pub(crate) const Y2042M2 : Timestamp = Timestamp(2274825600);
543 pub(crate) const Y2043M2 : Timestamp = Timestamp(2306361600);
544 pub(crate) const Y2044M2 : Timestamp = Timestamp(2337897600);
545 pub(crate) const Y2045M2 : Timestamp = Timestamp(2369520000);
546 pub(crate) const Y2046M2 : Timestamp = Timestamp(2401056000);
547 pub(crate) const Y2047M2 : Timestamp = Timestamp(2432592000);
548 pub(crate) const Y2048M2 : Timestamp = Timestamp(2464128000);
549 pub(crate) const Y2049M2 : Timestamp = Timestamp(2495750400);
550 pub(crate) const Y2050M2 : Timestamp = Timestamp(2527286400);
551 pub(crate) const Y2051M2 : Timestamp = Timestamp(2558822400);
552 pub(crate) const Y2052M2 : Timestamp = Timestamp(2590358400);
553 pub(crate) const Y2053M2 : Timestamp = Timestamp(2621980800);
554 pub(crate) const Y2054M2 : Timestamp = Timestamp(2653516800);
555 pub(crate) const Y2055M2 : Timestamp = Timestamp(2685052800);
556 pub(crate) const Y2056M2 : Timestamp = Timestamp(2716588800);
557 pub(crate) const Y2057M2 : Timestamp = Timestamp(2748211200);
558 pub(crate) const Y2058M2 : Timestamp = Timestamp(2779747200);
559 pub(crate) const Y2059M2 : Timestamp = Timestamp(2811283200);
560 pub(crate) const Y2060M2 : Timestamp = Timestamp(2842819200);
561 pub(crate) const Y2061M2 : Timestamp = Timestamp(2874441600);
562 pub(crate) const Y2062M2 : Timestamp = Timestamp(2905977600);
563 pub(crate) const Y2063M2 : Timestamp = Timestamp(2937513600);
564 pub(crate) const Y2064M2 : Timestamp = Timestamp(2969049600);
565 pub(crate) const Y2065M2 : Timestamp = Timestamp(3000672000);
566 pub(crate) const Y2066M2 : Timestamp = Timestamp(3032208000);
567 pub(crate) const Y2067M2 : Timestamp = Timestamp(3063744000);
568 pub(crate) const Y2068M2 : Timestamp = Timestamp(3095280000);
569 pub(crate) const Y2069M2 : Timestamp = Timestamp(3126902400);
570 pub(crate) const Y2070M2 : Timestamp = Timestamp(3158438400);
571 pub(crate) const Y2071M2 : Timestamp = Timestamp(3189974400);
572 pub(crate) const Y2072M2 : Timestamp = Timestamp(3221510400);
573 pub(crate) const Y2073M2 : Timestamp = Timestamp(3253132800);
574 pub(crate) const Y2074M2 : Timestamp = Timestamp(3284668800);
575 pub(crate) const Y2075M2 : Timestamp = Timestamp(3316204800);
576 pub(crate) const Y2076M2 : Timestamp = Timestamp(3347740800);
577 pub(crate) const Y2077M2 : Timestamp = Timestamp(3379363200);
578 pub(crate) const Y2078M2 : Timestamp = Timestamp(3410899200);
579 pub(crate) const Y2079M2 : Timestamp = Timestamp(3442435200);
580 pub(crate) const Y2080M2 : Timestamp = Timestamp(3473971200);
581 pub(crate) const Y2081M2 : Timestamp = Timestamp(3505593600);
582 pub(crate) const Y2082M2 : Timestamp = Timestamp(3537129600);
583 pub(crate) const Y2083M2 : Timestamp = Timestamp(3568665600);
584 pub(crate) const Y2084M2 : Timestamp = Timestamp(3600201600);
585 pub(crate) const Y2085M2 : Timestamp = Timestamp(3631824000);
586 pub(crate) const Y2086M2 : Timestamp = Timestamp(3663360000);
587 pub(crate) const Y2087M2 : Timestamp = Timestamp(3694896000);
588 pub(crate) const Y2088M2 : Timestamp = Timestamp(3726432000);
589 pub(crate) const Y2089M2 : Timestamp = Timestamp(3758054400);
590 pub(crate) const Y2090M2 : Timestamp = Timestamp(3789590400);
591 pub(crate) const Y2091M2 : Timestamp = Timestamp(3821126400);
592 pub(crate) const Y2092M2 : Timestamp = Timestamp(3852662400);
593 pub(crate) const Y2093M2 : Timestamp = Timestamp(3884284800);
594 pub(crate) const Y2094M2 : Timestamp = Timestamp(3915820800);
595 pub(crate) const Y2095M2 : Timestamp = Timestamp(3947356800);
596 pub(crate) const Y2096M2 : Timestamp = Timestamp(3978892800);
597 pub(crate) const Y2097M2 : Timestamp = Timestamp(4010515200);
598 pub(crate) const Y2098M2 : Timestamp = Timestamp(4042051200);
599 pub(crate) const Y2099M2 : Timestamp = Timestamp(4073587200);
600 pub(crate) const Y2100M2 : Timestamp = Timestamp(4105123200);
601 pub(crate) const Y2101M2 : Timestamp = Timestamp(4136659200);
602 pub(crate) const Y2102M2 : Timestamp = Timestamp(4168195200);
603 pub(crate) const Y2103M2 : Timestamp = Timestamp(4199731200);
604 pub(crate) const Y2104M2 : Timestamp = Timestamp(4231267200);
605 pub(crate) const Y2105M2 : Timestamp = Timestamp(4262889600);
606 pub(crate) const Y2106M2 : Timestamp = Timestamp(4294425600);
607}
608
609#[cfg(test)]
610impl Arbitrary for Duration {
611 fn arbitrary(g: &mut Gen) -> Self {
612 Duration(u32::arbitrary(g))
613 }
614}
615
616pub(crate) fn normalize_systemtime(t: SystemTime) -> SystemTime {
619 UNIX_EPOCH + SystemDuration::new(
620 t.duration_since(UNIX_EPOCH).unwrap().as_secs(), 0)
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626
627 quickcheck! {
628 fn timestamp_round_down(t: Timestamp) -> bool {
629 let u = t.round_down(None, None).unwrap();
630 assert!(u <= t);
631 assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
632 assert!(u32::from(t) - u32::from(u) < 2_u32.pow(21));
633 true
634 }
635 }
636
637 #[test]
638 fn timestamp_round_down_floor() -> Result<()> {
639 let t = Timestamp(1585753307);
640 let floor = t.checked_sub(Duration::weeks(1).unwrap()).unwrap();
641
642 let u = t.round_down(21, floor).unwrap();
643 assert!(u < t);
644 assert!(floor < u);
645 assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
646
647 let floor = t.checked_sub(Duration::days(1).unwrap()).unwrap();
648
649 let u = t.round_down(21, floor).unwrap();
650 assert_eq!(u, floor);
651 Ok(())
652 }
653
654 quickcheck! {
655 fn duration_round_up(d: Duration) -> bool {
656 let u = d.round_up(None, None).unwrap();
657 assert!(d <= u);
658 assert!(u32::from(u) & 0b1_1111_1111_1111_1111_1111 == 0
659 || u32::from(u) == u32::MAX
660 );
661 assert!(u32::from(u) - u32::from(d) < 2_u32.pow(21));
662 true
663 }
664 }
665
666 #[test]
667 fn duration_round_up_ceil() -> Result<()> {
668 let d = Duration(123);
669
670 let ceil = Duration(2_u32.pow(23));
671
672 let u = d.round_up(21, ceil)?;
673 assert!(d < u);
674 assert!(u < ceil);
675 assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
676
677 let ceil = Duration::days(1).unwrap();
678
679 let u = d.round_up(21, ceil)?;
680 assert!(d < u);
681 assert_eq!(u, ceil);
682
683 Ok(())
684 }
685
686 #[test]
692 fn system_time_32_bit() -> Result<()> {
693 let is_system_time_too_small = UNIX_EPOCH
694 .checked_add(SystemDuration::new(i32::MAX as u64 + 1, 0))
695 .is_none();
696
697 let t1 = Timestamp::from(i32::MAX as u32 - 1);
698 let t2 = Timestamp::from(i32::MAX as u32);
699 let t3 = Timestamp::from(i32::MAX as u32 + 1);
700 let t4 = Timestamp::from(u32::MAX);
701
702 if is_system_time_too_small {
703 assert_eq!(SystemTime::from(t1),
704 UNIX_EPOCH + SystemDuration::new(i32::MAX as u64 - 1, 0));
705
706 assert_eq!(SystemTime::from(t2),
707 UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
708
709 assert_eq!(SystemTime::from(t3),
710 UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
711
712 assert_eq!(SystemTime::from(t4),
713 UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
714 } else {
715 assert_eq!(SystemTime::from(t1),
716 UNIX_EPOCH + SystemDuration::new(i32::MAX as u64 - 1, 0));
717
718 assert_eq!(SystemTime::from(t2),
719 UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
720
721 assert_eq!(SystemTime::from(t3),
722 UNIX_EPOCH + SystemDuration::new(i32::MAX as u64 + 1, 0));
723
724 assert_eq!(SystemTime::from(t4),
725 UNIX_EPOCH + SystemDuration::new(u32::MAX as u64, 0));
726 }
727 Ok(())
728 }
729}