1 use std::cmp;
2 use std::convert::{TryFrom, TryInto};
3 use std::fmt;
4 use std::time::{SystemTime, Duration as SystemDuration, UNIX_EPOCH};
5 use std::u32;
6
7 #[cfg(any(test, feature = "quickcheck"))]
8 use quickcheck::{Arbitrary, Gen};
9
10 use 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 4880]).
19 ///
20 /// They cannot express dates further than 7th February of 2106 or earlier than
21 /// the [UNIX epoch]. Unlike Unix's `time_t`, OpenPGP's timestamp is unsigned so
22 /// it rollsover 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 [`SubpacketArea::signature_creation_time`] to get the signature creation time.
30 ///
31 /// [`SubpacketArea::signature_creation_time`]: ../packet/signature/subpacket/struct.SubpacketArea.html#method.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(None, Some("alice@example.org"))
45 /// .generate()?;
46 ///
47 /// let subkey = cert.keys().subkeys().next().unwrap();
48 /// let packets = subkey.bundle().self_signatures()[0].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 4880]: https://tools.ietf.org/html/rfc4880#section-3.5
62 /// [UNIX epoch]: https://en.wikipedia.org/wiki/Unix_time
63 /// [`Timestamp::round_down`]: ../types/struct.Timestamp.html#method.round_down
64 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
65 pub struct Timestamp(u32);
66
67 impl From<Timestamp> for u32 {
from(t: Timestamp) -> Self68 fn from(t: Timestamp) -> Self {
69 t.0
70 }
71 }
72
73 impl From<u32> for Timestamp {
from(t: u32) -> Self74 fn from(t: u32) -> Self {
75 Timestamp(t)
76 }
77 }
78
79 impl TryFrom<SystemTime> for Timestamp {
80 type Error = anyhow::Error;
81
try_from(t: SystemTime) -> Result<Self>82 fn try_from(t: SystemTime) -> Result<Self> {
83 match t.duration_since(std::time::UNIX_EPOCH) {
84 Ok(d) if d.as_secs() <= std::u32::MAX as u64 =>
85 Ok(Timestamp(d.as_secs() as u32)),
86 _ => Err(Error::InvalidArgument(
87 format!("Time exceeds u32 epoch: {:?}", t))
88 .into()),
89 }
90 }
91 }
92
93 impl From<Timestamp> for SystemTime {
from(t: Timestamp) -> Self94 fn from(t: Timestamp) -> Self {
95 UNIX_EPOCH + SystemDuration::new(t.0 as u64, 0)
96 }
97 }
98
99 impl From<Timestamp> for Option<SystemTime> {
from(t: Timestamp) -> Self100 fn from(t: Timestamp) -> Self {
101 Some(t.into())
102 }
103 }
104
105 impl fmt::Display for Timestamp {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 write!(f, "{}", crate::fmt::time(&SystemTime::from(*self)))
108 }
109 }
110
111 impl fmt::Debug for Timestamp {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113 write!(f, "{}", self.0)
114 }
115 }
116
117 impl Timestamp {
118 /// Returns the current time.
now() -> Timestamp119 pub fn now() -> Timestamp {
120 SystemTime::now().try_into()
121 .expect("representable for the next hundred years")
122 }
123
124 /// Adds a duration to this timestamp.
125 ///
126 /// Returns `None` if the resulting timestamp is not
127 /// representable.
checked_add(&self, d: Duration) -> Option<Timestamp>128 pub fn checked_add(&self, d: Duration) -> Option<Timestamp> {
129 self.0.checked_add(d.0).map(|v| Self(v))
130 }
131
132 /// Subtracts a duration from this timestamp.
133 ///
134 /// Returns `None` if the resulting timestamp is not
135 /// representable.
checked_sub(&self, d: Duration) -> Option<Timestamp>136 pub fn checked_sub(&self, d: Duration) -> Option<Timestamp> {
137 self.0.checked_sub(d.0).map(|v| Self(v))
138 }
139
140 /// Rounds down to the given level of precision.
141 ///
142 /// This can be used to reduce the metadata leak resulting from
143 /// time stamps. For example, a group of people attending a key
144 /// signing event could be identified by comparing the time stamps
145 /// of resulting certifications. By rounding the creation time of
146 /// these signatures down, all of them, and others, fall into the
147 /// same bucket.
148 ///
149 /// The given level `p` determines the resulting resolution of
150 /// `2^p` seconds. The default is `21`, which results in a
151 /// resolution of 24 days, or roughly a month. `p` must be lower
152 /// than 32.
153 ///
154 /// The lower limit `floor` represents the earliest time the timestamp will be
155 /// rounded down to.
156 ///
157 /// See also [`Duration::round_up`](struct.Duration.html#method.round_up).
158 ///
159 /// # Important note
160 ///
161 /// If we create a signature, it is important that the signature's
162 /// creation time does not predate the signing keys creation time,
163 /// or otherwise violate the key's validity constraints.
164 /// This can be achieved by using the `floor` parameter.
165 ///
166 /// To ensure validity, use this function to round the time down,
167 /// using the latest known relevant timestamp as a floor.
168 /// Then, lookup all keys and other objects like userids using this
169 /// timestamp, and on success create the signature:
170 ///
171 /// ```rust
172 /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*};
173 /// use sequoia_openpgp::policy::StandardPolicy;
174 ///
175 /// # f().unwrap();
176 /// # fn f() -> Result<()> {
177 /// let policy = &StandardPolicy::new();
178 ///
179 /// // Let's fix a time.
180 /// let now = Timestamp::from(1583436160);
181 ///
182 /// let cert_creation_alice = now.checked_sub(Duration::weeks(2)?).unwrap();
183 /// let cert_creation_bob = now.checked_sub(Duration::weeks(1)?).unwrap();
184 ///
185 /// // Generate a Cert for Alice.
186 /// let (alice, _) = CertBuilder::new()
187 /// .set_creation_time(cert_creation_alice)
188 /// .set_primary_key_flags(KeyFlags::empty().set_certification())
189 /// .add_userid("alice@example.org")
190 /// .generate()?;
191 ///
192 /// // Generate a Cert for Bob.
193 /// let (bob, _) = CertBuilder::new()
194 /// .set_creation_time(cert_creation_bob)
195 /// .set_primary_key_flags(KeyFlags::empty().set_certification())
196 /// .add_userid("bob@example.org")
197 /// .generate()?;
198 ///
199 /// let sign_with_p = |p| -> Result<Signature> {
200 /// // Round `now` down, then use `t` for all lookups.
201 /// // Use the creation time of Bob's Cert as lower bound for rounding.
202 /// let t: std::time::SystemTime = now.round_down(p, cert_creation_bob)?.into();
203 ///
204 /// // First, get the certification key.
205 /// let mut keypair =
206 /// alice.keys().with_policy(policy, t).secret().for_certification()
207 /// .nth(0).ok_or_else(|| anyhow::anyhow!("no valid key at"))?
208 /// .key().clone().into_keypair()?;
209 ///
210 /// // Then, lookup the binding between `bob@example.org` and
211 /// // `bob` at `t`.
212 /// let ca = bob.userids().with_policy(policy, t)
213 /// .filter(|ca| ca.userid().value() == b"bob@example.org")
214 /// .nth(0).ok_or_else(|| anyhow::anyhow!("no valid userid"))?;
215 ///
216 /// // Finally, Alice certifies the binding between
217 /// // `bob@example.org` and `bob` at `t`.
218 /// ca.userid().certify(&mut keypair, &bob,
219 /// SignatureType::PositiveCertification, None, t)
220 /// };
221 ///
222 /// assert!(sign_with_p(21).is_ok());
223 /// assert!(sign_with_p(22).is_ok()); // Rounded to bob's cert's creation time.
224 /// assert!(sign_with_p(32).is_err()); // Invalid precision
225 /// # Ok(()) }
226 /// ```
round_down<P, F>(&self, precision: P, floor: F) -> Result<Timestamp> where P: Into<Option<u8>>, F: Into<Option<SystemTime>>227 pub fn round_down<P, F>(&self, precision: P, floor: F) -> Result<Timestamp>
228 where P: Into<Option<u8>>,
229 F: Into<Option<SystemTime>>
230 {
231 let p = precision.into().unwrap_or(21) as u32;
232 if p < 32 {
233 let rounded = Self(self.0 & !((1 << p) - 1));
234 match floor.into() {
235 Some(floor) => {
236 Ok(cmp::max(rounded, floor.try_into()?))
237 }
238 None => { Ok(rounded) }
239 }
240 } else {
241 Err(Error::InvalidArgument(
242 format!("Invalid precision {}", p)).into())
243 }
244 }
245 }
246
247 #[cfg(any(test, feature = "quickcheck"))]
248 impl Arbitrary for Timestamp {
arbitrary<G: Gen>(g: &mut G) -> Self249 fn arbitrary<G: Gen>(g: &mut G) -> Self {
250 Timestamp(u32::arbitrary(g))
251 }
252 }
253
254 /// A duration representable by OpenPGP.
255 ///
256 /// # Examples
257 ///
258 /// ```
259 /// use sequoia_openpgp as openpgp;
260 /// # use openpgp::Result;
261 /// use openpgp::cert::prelude::*;
262 /// use openpgp::policy::StandardPolicy;
263 /// use openpgp::packet::signature::subpacket::{SubpacketTag, SubpacketValue};
264 /// use openpgp::types::{Timestamp, Duration};
265 ///
266 /// # fn main() -> Result<()> {
267 /// let p = &StandardPolicy::new();
268 ///
269 /// let now = Timestamp::now();
270 /// let validity_period = Duration::days(365)?;
271 ///
272 /// let (cert,_) = CertBuilder::new()
273 /// .set_creation_time(now)
274 /// .set_validity_period(validity_period)
275 /// .generate()?;
276 ///
277 /// let vc = cert.with_policy(p, now)?;
278 /// assert!(vc.alive().is_ok());
279 /// # Ok(()) }
280 /// ```
281 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
282 pub struct Duration(u32);
283
284 impl From<Duration> for u32 {
from(d: Duration) -> Self285 fn from(d: Duration) -> Self {
286 d.0
287 }
288 }
289
290 impl From<u32> for Duration {
from(d: u32) -> Self291 fn from(d: u32) -> Self {
292 Duration(d)
293 }
294 }
295
296 impl TryFrom<SystemDuration> for Duration {
297 type Error = anyhow::Error;
298
try_from(d: SystemDuration) -> Result<Self>299 fn try_from(d: SystemDuration) -> Result<Self> {
300 if d.as_secs() <= std::u32::MAX as u64 {
301 Ok(Duration(d.as_secs() as u32))
302 } else {
303 Err(Error::InvalidArgument(
304 format!("Duration exceeds u32: {:?}", d))
305 .into())
306 }
307 }
308 }
309
310 impl From<Duration> for SystemDuration {
from(d: Duration) -> Self311 fn from(d: Duration) -> Self {
312 SystemDuration::new(d.0 as u64, 0)
313 }
314 }
315
316 impl From<Duration> for Option<SystemDuration> {
from(d: Duration) -> Self317 fn from(d: Duration) -> Self {
318 Some(d.into())
319 }
320 }
321
322 impl fmt::Debug for Duration {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result323 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
324 write!(f, "{:?}", SystemDuration::from(*self))
325 }
326 }
327
328 impl Duration {
329 /// Returns a `Duration` with the given number of seconds.
seconds(n: u32) -> Duration330 pub fn seconds(n: u32) -> Duration {
331 n.into()
332 }
333
334 /// Returns a `Duration` with the given number of minutes, if
335 /// representable.
minutes(n: u32) -> Result<Duration>336 pub fn minutes(n: u32) -> Result<Duration> {
337 60u32.checked_mul(n).ok_or(())
338 .map(Self::seconds)
339 .map_err(|_| Error::InvalidArgument(
340 format!("Not representable: {} minutes in seconds exceeds u32",
341 n)).into())
342 }
343
344 /// Returns a `Duration` with the given number of hours, if
345 /// representable.
hours(n: u32) -> Result<Duration>346 pub fn hours(n: u32) -> Result<Duration> {
347 60u32.checked_mul(n)
348 .ok_or(Error::InvalidArgument("".into()).into())
349 .and_then(Self::minutes)
350 .map_err(|_| Error::InvalidArgument(
351 format!("Not representable: {} hours in seconds exceeds u32",
352 n)).into())
353 }
354
355 /// Returns a `Duration` with the given number of days, if
356 /// representable.
days(n: u32) -> Result<Duration>357 pub fn days(n: u32) -> Result<Duration> {
358 24u32.checked_mul(n)
359 .ok_or(Error::InvalidArgument("".into()).into())
360 .and_then(Self::hours)
361 .map_err(|_| Error::InvalidArgument(
362 format!("Not representable: {} days in seconds exceeds u32",
363 n)).into())
364 }
365
366 /// Returns a `Duration` with the given number of weeks, if
367 /// representable.
weeks(n: u32) -> Result<Duration>368 pub fn weeks(n: u32) -> Result<Duration> {
369 7u32.checked_mul(n)
370 .ok_or(Error::InvalidArgument("".into()).into())
371 .and_then(Self::days)
372 .map_err(|_| Error::InvalidArgument(
373 format!("Not representable: {} weeks in seconds exceeds u32",
374 n)).into())
375 }
376
377 /// Returns the duration as seconds.
as_secs(self) -> u64378 pub fn as_secs(self) -> u64 {
379 self.0 as u64
380 }
381
382 /// Rounds up to the given level of precision.
383 ///
384 /// If [`Timestamp::round_down`] is used to round the creation
385 /// timestamp of a key or signature down, then this function may
386 /// be used to round the corresponding expiration time up. This
387 /// ensures validity during the originally intended lifetime,
388 /// while avoiding the metadata leak associated with preserving
389 /// the originally intended expiration time.
390 ///
391 /// [`Timestamp::round_down`]: struct.Timestamp.html#method.round_down
392 ///
393 /// The given level `p` determines the resulting resolution of
394 /// `2^p` seconds. The default is `21`, which results in a
395 /// resolution of 24 days, or roughly a month. `p` must be lower
396 /// than 32.
397 ///
398 /// The upper limit `ceil` represents the maximum time to round up to.
round_up<P, C>(&self, precision: P, ceil: C) -> Result<Duration> where P: Into<Option<u8>>, C: Into<Option<SystemDuration>>399 pub fn round_up<P, C>(&self, precision: P, ceil: C) -> Result<Duration>
400 where P: Into<Option<u8>>,
401 C: Into<Option<SystemDuration>>
402 {
403 let p = precision.into().unwrap_or(21) as u32;
404 if p < 32 {
405 if let Some(sum) = self.0.checked_add((1 << p) - 1) {
406 let rounded = Self(sum & !((1 << p) - 1));
407 match ceil.into() {
408 Some(ceil) => {
409 Ok(cmp::min(rounded, ceil.try_into()?))
410 },
411 None => Ok(rounded)
412 }
413 } else {
414 Ok(Self(std::u32::MAX))
415 }
416 } else {
417 Err(Error::InvalidArgument(
418 format!("Invalid precision {}", p)).into())
419 }
420 }
421 }
422
423 #[allow(unused)]
424 impl Timestamp {
425 pub(crate) const UNIX_EPOCH : Timestamp = Timestamp(0);
426 pub(crate) const MAX : Timestamp = Timestamp(u32::MAX);
427
428 // for y in $(seq 1970 2106); do echo " const Y$y : Timestamp = Timestamp($(date -u --date="Jan. 1, $y" '+%s'));"; done
429 pub(crate) const Y1970 : Timestamp = Timestamp(0);
430 pub(crate) const Y1971 : Timestamp = Timestamp(31536000);
431 pub(crate) const Y1972 : Timestamp = Timestamp(63072000);
432 pub(crate) const Y1973 : Timestamp = Timestamp(94694400);
433 pub(crate) const Y1974 : Timestamp = Timestamp(126230400);
434 pub(crate) const Y1975 : Timestamp = Timestamp(157766400);
435 pub(crate) const Y1976 : Timestamp = Timestamp(189302400);
436 pub(crate) const Y1977 : Timestamp = Timestamp(220924800);
437 pub(crate) const Y1978 : Timestamp = Timestamp(252460800);
438 pub(crate) const Y1979 : Timestamp = Timestamp(283996800);
439 pub(crate) const Y1980 : Timestamp = Timestamp(315532800);
440 pub(crate) const Y1981 : Timestamp = Timestamp(347155200);
441 pub(crate) const Y1982 : Timestamp = Timestamp(378691200);
442 pub(crate) const Y1983 : Timestamp = Timestamp(410227200);
443 pub(crate) const Y1984 : Timestamp = Timestamp(441763200);
444 pub(crate) const Y1985 : Timestamp = Timestamp(473385600);
445 pub(crate) const Y1986 : Timestamp = Timestamp(504921600);
446 pub(crate) const Y1987 : Timestamp = Timestamp(536457600);
447 pub(crate) const Y1988 : Timestamp = Timestamp(567993600);
448 pub(crate) const Y1989 : Timestamp = Timestamp(599616000);
449 pub(crate) const Y1990 : Timestamp = Timestamp(631152000);
450 pub(crate) const Y1991 : Timestamp = Timestamp(662688000);
451 pub(crate) const Y1992 : Timestamp = Timestamp(694224000);
452 pub(crate) const Y1993 : Timestamp = Timestamp(725846400);
453 pub(crate) const Y1994 : Timestamp = Timestamp(757382400);
454 pub(crate) const Y1995 : Timestamp = Timestamp(788918400);
455 pub(crate) const Y1996 : Timestamp = Timestamp(820454400);
456 pub(crate) const Y1997 : Timestamp = Timestamp(852076800);
457 pub(crate) const Y1998 : Timestamp = Timestamp(883612800);
458 pub(crate) const Y1999 : Timestamp = Timestamp(915148800);
459 pub(crate) const Y2000 : Timestamp = Timestamp(946684800);
460 pub(crate) const Y2001 : Timestamp = Timestamp(978307200);
461 pub(crate) const Y2002 : Timestamp = Timestamp(1009843200);
462 pub(crate) const Y2003 : Timestamp = Timestamp(1041379200);
463 pub(crate) const Y2004 : Timestamp = Timestamp(1072915200);
464 pub(crate) const Y2005 : Timestamp = Timestamp(1104537600);
465 pub(crate) const Y2006 : Timestamp = Timestamp(1136073600);
466 pub(crate) const Y2007 : Timestamp = Timestamp(1167609600);
467 pub(crate) const Y2008 : Timestamp = Timestamp(1199145600);
468 pub(crate) const Y2009 : Timestamp = Timestamp(1230768000);
469 pub(crate) const Y2010 : Timestamp = Timestamp(1262304000);
470 pub(crate) const Y2011 : Timestamp = Timestamp(1293840000);
471 pub(crate) const Y2012 : Timestamp = Timestamp(1325376000);
472 pub(crate) const Y2013 : Timestamp = Timestamp(1356998400);
473 pub(crate) const Y2014 : Timestamp = Timestamp(1388534400);
474 pub(crate) const Y2015 : Timestamp = Timestamp(1420070400);
475 pub(crate) const Y2016 : Timestamp = Timestamp(1451606400);
476 pub(crate) const Y2017 : Timestamp = Timestamp(1483228800);
477 pub(crate) const Y2018 : Timestamp = Timestamp(1514764800);
478 pub(crate) const Y2019 : Timestamp = Timestamp(1546300800);
479 pub(crate) const Y2020 : Timestamp = Timestamp(1577836800);
480 pub(crate) const Y2021 : Timestamp = Timestamp(1609459200);
481 pub(crate) const Y2022 : Timestamp = Timestamp(1640995200);
482 pub(crate) const Y2023 : Timestamp = Timestamp(1672531200);
483 pub(crate) const Y2024 : Timestamp = Timestamp(1704067200);
484 pub(crate) const Y2025 : Timestamp = Timestamp(1735689600);
485 pub(crate) const Y2026 : Timestamp = Timestamp(1767225600);
486 pub(crate) const Y2027 : Timestamp = Timestamp(1798761600);
487 pub(crate) const Y2028 : Timestamp = Timestamp(1830297600);
488 pub(crate) const Y2029 : Timestamp = Timestamp(1861920000);
489 pub(crate) const Y2030 : Timestamp = Timestamp(1893456000);
490 pub(crate) const Y2031 : Timestamp = Timestamp(1924992000);
491 pub(crate) const Y2032 : Timestamp = Timestamp(1956528000);
492 pub(crate) const Y2033 : Timestamp = Timestamp(1988150400);
493 pub(crate) const Y2034 : Timestamp = Timestamp(2019686400);
494 pub(crate) const Y2035 : Timestamp = Timestamp(2051222400);
495 pub(crate) const Y2036 : Timestamp = Timestamp(2082758400);
496 pub(crate) const Y2037 : Timestamp = Timestamp(2114380800);
497 pub(crate) const Y2038 : Timestamp = Timestamp(2145916800);
498 pub(crate) const Y2039 : Timestamp = Timestamp(2177452800);
499 pub(crate) const Y2040 : Timestamp = Timestamp(2208988800);
500 pub(crate) const Y2041 : Timestamp = Timestamp(2240611200);
501 pub(crate) const Y2042 : Timestamp = Timestamp(2272147200);
502 pub(crate) const Y2043 : Timestamp = Timestamp(2303683200);
503 pub(crate) const Y2044 : Timestamp = Timestamp(2335219200);
504 pub(crate) const Y2045 : Timestamp = Timestamp(2366841600);
505 pub(crate) const Y2046 : Timestamp = Timestamp(2398377600);
506 pub(crate) const Y2047 : Timestamp = Timestamp(2429913600);
507 pub(crate) const Y2048 : Timestamp = Timestamp(2461449600);
508 pub(crate) const Y2049 : Timestamp = Timestamp(2493072000);
509 pub(crate) const Y2050 : Timestamp = Timestamp(2524608000);
510 pub(crate) const Y2051 : Timestamp = Timestamp(2556144000);
511 pub(crate) const Y2052 : Timestamp = Timestamp(2587680000);
512 pub(crate) const Y2053 : Timestamp = Timestamp(2619302400);
513 pub(crate) const Y2054 : Timestamp = Timestamp(2650838400);
514 pub(crate) const Y2055 : Timestamp = Timestamp(2682374400);
515 pub(crate) const Y2056 : Timestamp = Timestamp(2713910400);
516 pub(crate) const Y2057 : Timestamp = Timestamp(2745532800);
517 pub(crate) const Y2058 : Timestamp = Timestamp(2777068800);
518 pub(crate) const Y2059 : Timestamp = Timestamp(2808604800);
519 pub(crate) const Y2060 : Timestamp = Timestamp(2840140800);
520 pub(crate) const Y2061 : Timestamp = Timestamp(2871763200);
521 pub(crate) const Y2062 : Timestamp = Timestamp(2903299200);
522 pub(crate) const Y2063 : Timestamp = Timestamp(2934835200);
523 pub(crate) const Y2064 : Timestamp = Timestamp(2966371200);
524 pub(crate) const Y2065 : Timestamp = Timestamp(2997993600);
525 pub(crate) const Y2066 : Timestamp = Timestamp(3029529600);
526 pub(crate) const Y2067 : Timestamp = Timestamp(3061065600);
527 pub(crate) const Y2068 : Timestamp = Timestamp(3092601600);
528 pub(crate) const Y2069 : Timestamp = Timestamp(3124224000);
529 pub(crate) const Y2070 : Timestamp = Timestamp(3155760000);
530 pub(crate) const Y2071 : Timestamp = Timestamp(3187296000);
531 pub(crate) const Y2072 : Timestamp = Timestamp(3218832000);
532 pub(crate) const Y2073 : Timestamp = Timestamp(3250454400);
533 pub(crate) const Y2074 : Timestamp = Timestamp(3281990400);
534 pub(crate) const Y2075 : Timestamp = Timestamp(3313526400);
535 pub(crate) const Y2076 : Timestamp = Timestamp(3345062400);
536 pub(crate) const Y2077 : Timestamp = Timestamp(3376684800);
537 pub(crate) const Y2078 : Timestamp = Timestamp(3408220800);
538 pub(crate) const Y2079 : Timestamp = Timestamp(3439756800);
539 pub(crate) const Y2080 : Timestamp = Timestamp(3471292800);
540 pub(crate) const Y2081 : Timestamp = Timestamp(3502915200);
541 pub(crate) const Y2082 : Timestamp = Timestamp(3534451200);
542 pub(crate) const Y2083 : Timestamp = Timestamp(3565987200);
543 pub(crate) const Y2084 : Timestamp = Timestamp(3597523200);
544 pub(crate) const Y2085 : Timestamp = Timestamp(3629145600);
545 pub(crate) const Y2086 : Timestamp = Timestamp(3660681600);
546 pub(crate) const Y2087 : Timestamp = Timestamp(3692217600);
547 pub(crate) const Y2088 : Timestamp = Timestamp(3723753600);
548 pub(crate) const Y2089 : Timestamp = Timestamp(3755376000);
549 pub(crate) const Y2090 : Timestamp = Timestamp(3786912000);
550 pub(crate) const Y2091 : Timestamp = Timestamp(3818448000);
551 pub(crate) const Y2092 : Timestamp = Timestamp(3849984000);
552 pub(crate) const Y2093 : Timestamp = Timestamp(3881606400);
553 pub(crate) const Y2094 : Timestamp = Timestamp(3913142400);
554 pub(crate) const Y2095 : Timestamp = Timestamp(3944678400);
555 pub(crate) const Y2096 : Timestamp = Timestamp(3976214400);
556 pub(crate) const Y2097 : Timestamp = Timestamp(4007836800);
557 pub(crate) const Y2098 : Timestamp = Timestamp(4039372800);
558 pub(crate) const Y2099 : Timestamp = Timestamp(4070908800);
559 pub(crate) const Y2100 : Timestamp = Timestamp(4102444800);
560 pub(crate) const Y2101 : Timestamp = Timestamp(4133980800);
561 pub(crate) const Y2102 : Timestamp = Timestamp(4165516800);
562 pub(crate) const Y2103 : Timestamp = Timestamp(4197052800);
563 pub(crate) const Y2104 : Timestamp = Timestamp(4228588800);
564 pub(crate) const Y2105 : Timestamp = Timestamp(4260211200);
565 pub(crate) const Y2106 : Timestamp = Timestamp(4291747200);
566 }
567
568 #[cfg(any(test, feature = "quickcheck"))]
569 impl Arbitrary for Duration {
arbitrary<G: Gen>(g: &mut G) -> Self570 fn arbitrary<G: Gen>(g: &mut G) -> Self {
571 Duration(u32::arbitrary(g))
572 }
573 }
574
575 /// Normalizes the given SystemTime to the resolution OpenPGP
576 /// supports.
normalize_systemtime(t: SystemTime) -> SystemTime577 pub(crate) fn normalize_systemtime(t: SystemTime) -> SystemTime {
578 UNIX_EPOCH + SystemDuration::new(
579 t.duration_since(UNIX_EPOCH).unwrap().as_secs(), 0)
580 }
581
582 #[cfg(test)]
583 mod tests {
584 use super::*;
585
586 quickcheck! {
587 fn timestamp_round_down(t: Timestamp) -> bool {
588 let u = t.round_down(None, None).unwrap();
589 assert!(u <= t);
590 assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
591 assert!(u32::from(t) - u32::from(u) < 2_u32.pow(21));
592 true
593 }
594 }
595
596 #[test]
timestamp_round_down_floor() -> Result<()>597 fn timestamp_round_down_floor() -> Result<()> {
598 let t = Timestamp(1585753307);
599 let floor = t.checked_sub(Duration::weeks(1).unwrap()).unwrap();
600
601 let u = t.round_down(21, floor).unwrap();
602 assert!(u < t);
603 assert!(floor < u);
604 assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
605
606 let floor = t.checked_sub(Duration::days(1).unwrap()).unwrap();
607
608 let u = t.round_down(21, floor).unwrap();
609 assert_eq!(u, floor);
610 Ok(())
611 }
612
613 quickcheck! {
614 fn duration_round_up(d: Duration) -> bool {
615 let u = d.round_up(None, None).unwrap();
616 assert!(d <= u);
617 assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
618 assert!(u32::from(u) - u32::from(d) < 2_u32.pow(21));
619 true
620 }
621 }
622
623 #[test]
duration_round_up_ceil() -> Result<()>624 fn duration_round_up_ceil() -> Result<()> {
625 let d = Duration(123);
626
627 let ceil = Duration(2_u32.pow(23));
628
629 let u = d.round_up(21, ceil)?;
630 assert!(d < u);
631 assert!(u < ceil);
632 assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
633
634 let ceil = Duration::days(1).unwrap();
635
636 let u = d.round_up(21, ceil)?;
637 assert!(d < u);
638 assert_eq!(u, ceil);
639
640 Ok(())
641 }
642 }
643