sequoia_openpgp/types/
timestamp.rs

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/// A timestamp representable by OpenPGP.
16///
17/// OpenPGP timestamps are represented as `u32` containing the number of seconds
18/// elapsed since midnight, 1 January 1970 UTC ([Section 3.5 of RFC 9580]).
19///
20/// They cannot express dates further than 7th February 2106 or earlier than
21/// the [UNIX epoch]. Unlike Unix's `time_t`, OpenPGP's timestamp is unsigned so
22/// it rolls over in 2106, not 2038.
23///
24/// # Examples
25///
26/// Signature creation time is internally stored as a `Timestamp`:
27///
28/// Note that this example retrieves raw packet value.
29/// Use [`SubpacketAreas::signature_creation_time`] to get the signature creation time.
30///
31/// [`SubpacketAreas::signature_creation_time`]: crate::packet::signature::subpacket::SubpacketAreas::signature_creation_time()
32///
33/// ```
34/// use sequoia_openpgp as openpgp;
35/// # use openpgp::Result;
36/// use std::convert::From;
37/// use std::time::SystemTime;
38/// use openpgp::cert::prelude::*;
39/// use openpgp::policy::StandardPolicy;
40/// use openpgp::packet::signature::subpacket::{SubpacketTag, SubpacketValue};
41///
42/// # fn main() -> Result<()> {
43/// let (cert, _) =
44///     CertBuilder::general_purpose(Some("alice@example.org"))
45///     .generate()?;
46///
47/// let subkey = cert.keys().subkeys().next().unwrap();
48/// let packets = subkey.bundle().self_signatures().next().unwrap().hashed_area();
49///
50/// match packets.subpacket(SubpacketTag::SignatureCreationTime).unwrap().value() {
51///     SubpacketValue::SignatureCreationTime(ts) => assert!(u32::from(*ts) > 0),
52///     v => panic!("Unexpected subpacket: {:?}", v),
53/// }
54///
55/// let p = &StandardPolicy::new();
56/// let now = SystemTime::now();
57/// assert!(subkey.binding_signature(p, now)?.signature_creation_time().is_some());
58/// # Ok(()) }
59/// ```
60///
61/// [Section 3.5 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-3.5
62/// [UNIX epoch]: https://en.wikipedia.org/wiki/Unix_time
63/// [`Timestamp::round_down`]: crate::types::Timestamp::round_down()
64#[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
94/// SystemTime's underlying datatype may be only `i32`, e.g. on 32bit Unix.
95/// As OpenPGP's timestamp datatype is `u32`, there are timestamps (`i32::MAX + 1`
96/// to `u32::MAX`) which are not representable on such systems.
97///
98/// In this case, the result is clamped to `i32::MAX`.
99impl 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    /// Returns the current time.
126    pub fn now() -> Timestamp {
127        crate::now().try_into()
128            .expect("representable for the next hundred years")
129    }
130
131    /// Adds a duration to this timestamp.
132    ///
133    /// Returns `None` if the resulting timestamp is not
134    /// representable.
135    pub fn checked_add(&self, d: Duration) -> Option<Timestamp> {
136        self.0.checked_add(d.0).map(Self)
137    }
138
139    /// Subtracts a duration from this timestamp.
140    ///
141    /// Returns `None` if the resulting timestamp is not
142    /// representable.
143    pub fn checked_sub(&self, d: Duration) -> Option<Timestamp> {
144        self.0.checked_sub(d.0).map(Self)
145    }
146
147    /// Rounds down to the given level of precision.
148    ///
149    /// This can be used to reduce the metadata leak resulting from
150    /// time stamps.  For example, a group of people attending a key
151    /// signing event could be identified by comparing the time stamps
152    /// of resulting certifications.  By rounding the creation time of
153    /// these signatures down, all of them, and others, fall into the
154    /// same bucket.
155    ///
156    /// The given level `p` determines the resulting resolution of
157    /// `2^p` seconds.  The default is `21`, which results in a
158    /// resolution of 24 days, or roughly a month.  `p` must be lower
159    /// than 32.
160    ///
161    /// The lower limit `floor` represents the earliest time the timestamp will be
162    /// rounded down to.
163    ///
164    /// See also [`Duration::round_up`](Duration::round_up()).
165    ///
166    /// # Important note
167    ///
168    /// If we create a signature, it is important that the signature's
169    /// creation time does not predate the signing keys creation time,
170    /// or otherwise violate the key's validity constraints.
171    /// This can be achieved by using the `floor` parameter.
172    ///
173    /// To ensure validity, use this function to round the time down,
174    /// using the latest known relevant timestamp as a floor.
175    /// Then, lookup all keys and other objects like userids using this
176    /// timestamp, and on success create the signature:
177    ///
178    /// ```rust
179    /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*};
180    /// use sequoia_openpgp::policy::StandardPolicy;
181    ///
182    /// # fn main() -> Result<()> {
183    /// let policy = &StandardPolicy::new();
184    ///
185    /// // Let's fix a time.
186    /// let now = Timestamp::from(1583436160);
187    ///
188    /// let cert_creation_alice = now.checked_sub(Duration::weeks(2)?).unwrap();
189    /// let cert_creation_bob = now.checked_sub(Duration::weeks(1)?).unwrap();
190    ///
191    /// // Generate a Cert for Alice.
192    /// let (alice, _) = CertBuilder::new()
193    ///     .set_creation_time(cert_creation_alice)
194    ///     .set_primary_key_flags(KeyFlags::empty().set_certification())
195    ///     .add_userid("alice@example.org")
196    ///     .generate()?;
197    ///
198    /// // Generate a Cert for Bob.
199    /// let (bob, _) = CertBuilder::new()
200    ///     .set_creation_time(cert_creation_bob)
201    ///     .set_primary_key_flags(KeyFlags::empty().set_certification())
202    ///     .add_userid("bob@example.org")
203    ///     .generate()?;
204    ///
205    /// let sign_with_p = |p| -> Result<Signature> {
206    ///     // Round `now` down, then use `t` for all lookups.
207    ///     // Use the creation time of Bob's Cert as lower bound for rounding.
208    ///     let t: std::time::SystemTime = now.round_down(p, cert_creation_bob)?.into();
209    ///
210    ///     // First, get the certification key.
211    ///     let mut keypair =
212    ///         alice.keys().with_policy(policy, t).secret().for_certification()
213    ///         .nth(0).ok_or_else(|| anyhow::anyhow!("no valid key at"))?
214    ///         .key().clone().into_keypair()?;
215    ///
216    ///     // Then, lookup the binding between `bob@example.org` and
217    ///     // `bob` at `t`.
218    ///     let ca = bob.userids().with_policy(policy, t)
219    ///         .filter(|ca| ca.userid().value() == b"bob@example.org")
220    ///         .nth(0).ok_or_else(|| anyhow::anyhow!("no valid userid"))?;
221    ///
222    ///     // Finally, Alice certifies the binding between
223    ///     // `bob@example.org` and `bob` at `t`.
224    ///     ca.userid().certify(&mut keypair, &bob,
225    ///                         SignatureType::PositiveCertification, None, t)
226    /// };
227    ///
228    /// assert!(sign_with_p(21).is_ok());
229    /// assert!(sign_with_p(22).is_ok());  // Rounded to Bob's cert's creation time.
230    /// assert!(sign_with_p(32).is_err()); // Invalid precision
231    /// # Ok(()) }
232    /// ```
233    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/// A duration representable by OpenPGP.
261///
262/// # Examples
263///
264/// ```
265/// use sequoia_openpgp as openpgp;
266/// # use openpgp::Result;
267/// use openpgp::cert::prelude::*;
268/// use openpgp::policy::StandardPolicy;
269/// use openpgp::packet::signature::subpacket::{SubpacketTag, SubpacketValue};
270/// use openpgp::types::{Timestamp, Duration};
271///
272/// # fn main() -> Result<()> {
273/// let p = &StandardPolicy::new();
274///
275/// let now = Timestamp::now();
276/// let validity_period = Duration::days(365)?;
277///
278/// let (cert,_) = CertBuilder::new()
279///     .set_creation_time(now)
280///     .set_validity_period(validity_period)
281///     .generate()?;
282///
283/// let vc = cert.with_policy(p, now)?;
284/// assert!(vc.alive().is_ok());
285/// # Ok(()) }
286/// ```
287#[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    /// Returns a `Duration` with the given number of seconds.
337    pub const fn seconds(n: u32) -> Duration {
338        Self(n)
339    }
340
341    /// Returns a `Duration` with the given number of minutes, if
342    /// representable.
343    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    /// Returns a `Duration` with the given number of hours, if
356    /// representable.
357    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    /// Returns a `Duration` with the given number of days, if
370    /// representable.
371    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    /// Returns a `Duration` with the given number of weeks, if
384    /// representable.
385    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    /// Returns a `Duration` with the given number of years, if
398    /// representable.
399    ///
400    /// This function assumes that there are 365.2425 [days in a
401    /// year], the average number of days in a year in the Gregorian
402    /// calendar.
403    ///
404    ///   [days in a year]: https://en.wikipedia.org/wiki/Year
405    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    /// Returns the duration as seconds.
418    pub fn as_secs(self) -> u64 {
419        self.0 as u64
420    }
421
422    /// Rounds up to the given level of precision.
423    ///
424    /// If [`Timestamp::round_down`] is used to round the creation
425    /// timestamp of a key or signature down, then this function may
426    /// be used to round the corresponding expiration time up.  This
427    /// ensures validity during the originally intended lifetime,
428    /// while avoiding the metadata leak associated with preserving
429    /// the originally intended expiration time.
430    ///
431    ///   [`Timestamp::round_down`]: Timestamp::round_down()
432    ///
433    /// The given level `p` determines the resulting resolution of
434    /// `2^p` seconds.  The default is `21`, which results in a
435    /// resolution of 24 days, or roughly a month.  `p` must be lower
436    /// than 32.
437    ///
438    /// The upper limit `ceil` represents the maximum time to round up to.
439    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    // for y in $(seq 1970 2106); do echo "    pub(crate) const Y${y}M2 : Timestamp = Timestamp($(date -u --date="Feb. 1, $y" '+%s'));"; done
470    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
616/// Normalizes the given SystemTime to the resolution OpenPGP
617/// supports.
618pub(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    // #668
687    // Ensure that, on systems where the SystemTime can only represent values
688    // up to i32::MAX (generally, 32-bit systems), Timestamps between
689    // i32::MAX + 1 and u32::MAX are clamped down to i32::MAX, and values below
690    // are not altered.
691    #[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}