1
use std::{
2
    borrow::Cow,
3
    fmt::Debug,
4
    time::{Duration, SystemTime, UNIX_EPOCH},
5
};
6

            
7
use ordered_varint::Variable;
8

            
9
use crate::key::{
10
    time::limited::{BonsaiEpoch, UnixEpoch},
11
    Key, KeyEncoding,
12
};
13

            
14
impl<'a> Key<'a> for Duration {
15
4
    fn from_ord_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
16
4
        let merged = u128::decode_variable(bytes).map_err(|_| TimeError::InvalidValue)?;
17
4
        let seconds = u64::try_from(merged >> 30).map_err(|_| TimeError::DeltaNotRepresentable)?;
18
4
        let nanos = u32::try_from(merged & (2_u128.pow(30) - 1)).unwrap();
19
4
        Ok(Self::new(seconds, nanos))
20
4
    }
21
}
22

            
23
impl<'a> KeyEncoding<'a, Self> for Duration {
24
    type Error = TimeError;
25

            
26
    const LENGTH: Option<usize> = None;
27

            
28
4
    fn as_ord_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
29
4
        let merged = u128::from(self.as_secs()) << 30 | u128::from(self.subsec_nanos());
30
4
        // It's safe to unwrap here, because under the hood ordered-varint can
31
4
        // only raise an error if the top bits are set. Since we only ever add
32
4
        // 94 bits, the top bits will not have any data set.
33
4
        Ok(Cow::Owned(merged.to_variable_vec().unwrap()))
34
4
    }
35
}
36

            
37
1
#[test]
38
1
fn duration_key_tests() {
39
1
    assert_eq!(
40
1
        Duration::ZERO,
41
1
        Duration::from_ord_bytes(&Duration::ZERO.as_ord_bytes().unwrap()).unwrap()
42
1
    );
43
1
    assert_eq!(
44
1
        Duration::MAX,
45
1
        Duration::from_ord_bytes(&Duration::MAX.as_ord_bytes().unwrap()).unwrap()
46
1
    );
47
1
}
48

            
49
impl<'a> Key<'a> for SystemTime {
50
2
    fn from_ord_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
51
2
        let since_epoch = Duration::from_ord_bytes(bytes)?;
52
2
        UNIX_EPOCH
53
2
            .checked_add(since_epoch)
54
2
            .ok_or(TimeError::DeltaNotRepresentable)
55
2
    }
56
}
57

            
58
impl<'a> KeyEncoding<'a, Self> for SystemTime {
59
    type Error = TimeError;
60

            
61
    const LENGTH: Option<usize> = None;
62

            
63
2
    fn as_ord_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
64
2
        let since_epoch = self.duration_since(UNIX_EPOCH).unwrap();
65
2
        match since_epoch.as_ord_bytes()? {
66
2
            Cow::Owned(bytes) => Ok(Cow::Owned(bytes)),
67
            Cow::Borrowed(_) => unreachable!(),
68
        }
69
2
    }
70
}
71

            
72
1
#[test]
73
1
fn system_time_tests() {
74
1
    assert_eq!(
75
1
        UNIX_EPOCH,
76
1
        SystemTime::from_ord_bytes(&UNIX_EPOCH.as_ord_bytes().unwrap()).unwrap()
77
1
    );
78
1
    let now = SystemTime::now();
79
1
    assert_eq!(
80
1
        now,
81
1
        SystemTime::from_ord_bytes(&now.as_ord_bytes().unwrap()).unwrap()
82
1
    );
83
1
}
84

            
85
/// An error that indicates that the stored timestamp is unable to be converted
86
/// to the destination type without losing data.
87
#[derive(thiserror::Error, Debug)]
88
#[error("the stored timestamp is outside the allowed range")]
89
pub struct DeltaNotRepresentable;
90

            
91
/// Errors that can arise from parsing times serialized with [`Key`].
92
#[derive(thiserror::Error, Debug)]
93
pub enum TimeError {
94
    /// An error that indicates that the stored timestamp is unable to be converted
95
    /// to the destination type without losing data.
96
    #[error("the stored timestamp is outside the allowed range")]
97
    DeltaNotRepresentable,
98
    /// The value stored was not encoded correctly.
99
    #[error("invalid value")]
100
    InvalidValue,
101
}
102

            
103
impl From<DeltaNotRepresentable> for TimeError {
104
    fn from(_: DeltaNotRepresentable) -> Self {
105
        Self::DeltaNotRepresentable
106
    }
107
}
108

            
109
impl From<std::io::Error> for TimeError {
110
    fn from(_: std::io::Error) -> Self {
111
        Self::InvalidValue
112
    }
113
}
114

            
115
/// Types for storing limited-precision Durations.
116
pub mod limited {
117
    use std::{
118
        borrow::Cow,
119
        fmt::{self, Debug, Display, Write},
120
        marker::PhantomData,
121
        str::FromStr,
122
        time::{Duration, SystemTime, UNIX_EPOCH},
123
    };
124

            
125
    use derive_where::derive_where;
126
    use ordered_varint::Variable;
127
    use serde::{Deserialize, Serialize};
128

            
129
    use crate::key::{time::TimeError, Key, KeyEncoding};
130

            
131
    /// A [`Duration`] of time stored with a limited `Resolution`. This type may be
132
    /// preferred to [`std::time::Duration`] because `Duration` takes a full 12
133
    /// bytes to achieve its nanosecond resolution.
134
    ///
135
    /// Converting from [`Duration`] truncates the duration and performs no rounding.
136
    ///
137
    /// The `Resolution` type controls the storage size. The resolutions
138
    /// provided by BonsaiDb:
139
    ///
140
    /// - [`Weeks`]
141
    /// - [`Days`]
142
    /// - [`Hours`]
143
    /// - [`Minutes`]
144
    /// - [`Seconds`]
145
    /// - [`Milliseconds`]
146
    /// - [`Microseconds`]
147
    /// - [`Nanoseconds`]
148
    ///
149
    /// Other resolutions can be used by implementing [`TimeResolution`].
150
61
    #[derive_where(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
151
    pub struct LimitedResolutionDuration<Resolution: TimeResolution> {
152
        step: Resolution::Representation,
153
        _resolution: PhantomData<Resolution>,
154
    }
155

            
156
    /// A resolution of a time measurement.
157
    pub trait TimeResolution: Debug + Send + Sync {
158
        /// The in-memory and serialized representation for this resolution.
159
        type Representation: Variable
160
            + Serialize
161
            + for<'de> Deserialize<'de>
162
            + Display
163
            + Eq
164
            + PartialEq
165
            + Ord
166
            + PartialOrd
167
            + Clone
168
            + Copy
169
            + Send
170
            + Sync
171
            + Debug
172
            + Default;
173

            
174
        /// The label used when formatting times with this resolution.
175
        const FORMAT_SUFFIX: &'static str;
176

            
177
        /// Converts a [`Self::Representation`] to [`Duration`].
178
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError>;
179

            
180
        /// Converts a [`Duration`] to [`Self::Representation`].
181
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError>;
182
    }
183

            
184
    /// A [`Duration`] that can be either negative or positive.
185
    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
186
    pub enum SignedDuration {
187
        /// A duration representing a positive measurement of time.
188
        Positive(Duration),
189
        /// A duration representing a negative measurement of time.
190
        Negative(Duration),
191
    }
192

            
193
    impl<Resolution> LimitedResolutionDuration<Resolution>
194
    where
195
        Resolution: TimeResolution,
196
    {
197
        /// Returns a new instance with the `step` provided, which conceptually
198
        /// is a unit of `Resolution`.
199
8
        pub fn new(step: Resolution::Representation) -> Self {
200
8
            Self {
201
8
                step,
202
8
                _resolution: PhantomData,
203
8
            }
204
8
        }
205
    }
206

            
207
    impl<Resolution: TimeResolution> Debug for LimitedResolutionDuration<Resolution> {
208
14
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209
14
            write!(f, "{:?}{}", self.step, Resolution::FORMAT_SUFFIX)
210
14
        }
211
    }
212

            
213
    impl<Resolution: TimeResolution> Display for LimitedResolutionDuration<Resolution> {
214
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215
            write!(f, "{}{}", self.step, Resolution::FORMAT_SUFFIX)
216
        }
217
    }
218

            
219
    impl<'a, Resolution> Key<'a> for LimitedResolutionDuration<Resolution>
220
    where
221
        Resolution: TimeResolution,
222
    {
223
14
        fn from_ord_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
224
14
            let step = <Resolution::Representation as Variable>::decode_variable(bytes)
225
14
                .map_err(|_| TimeError::InvalidValue)?;
226

            
227
14
            Ok(Self {
228
14
                step,
229
14
                _resolution: PhantomData,
230
14
            })
231
14
        }
232
    }
233

            
234
    impl<'a, Resolution> KeyEncoding<'a, Self> for LimitedResolutionDuration<Resolution>
235
    where
236
        Resolution: TimeResolution,
237
    {
238
        type Error = TimeError;
239

            
240
        const LENGTH: Option<usize> = None;
241

            
242
14
        fn as_ord_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
243
14
            self.step
244
14
                .to_variable_vec()
245
14
                .map(Cow::Owned)
246
14
                .map_err(|_| TimeError::InvalidValue)
247
14
        }
248
    }
249

            
250
    impl<Resolution> Default for LimitedResolutionDuration<Resolution>
251
    where
252
        Resolution: TimeResolution,
253
    {
254
        fn default() -> Self {
255
            Self {
256
                step: <Resolution::Representation as Default>::default(),
257
                _resolution: PhantomData,
258
            }
259
        }
260
    }
261

            
262
    impl<Resolution> Serialize for LimitedResolutionDuration<Resolution>
263
    where
264
        Resolution: TimeResolution,
265
    {
266
8
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
267
8
        where
268
8
            S: serde::Serializer,
269
8
        {
270
8
            self.step.serialize(serializer)
271
8
        }
272
    }
273

            
274
    impl<'de, Resolution> Deserialize<'de> for LimitedResolutionDuration<Resolution>
275
    where
276
        Resolution: TimeResolution,
277
    {
278
8
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
279
8
        where
280
8
            D: serde::Deserializer<'de>,
281
8
        {
282
8
            <Resolution::Representation as Deserialize<'de>>::deserialize(deserializer)
283
8
                .map(Self::new)
284
8
        }
285
    }
286

            
287
    impl<Resolution> TryFrom<SignedDuration> for LimitedResolutionDuration<Resolution>
288
    where
289
        Resolution: TimeResolution,
290
    {
291
        type Error = TimeError;
292

            
293
107
        fn try_from(duration: SignedDuration) -> Result<Self, Self::Error> {
294
107
            Resolution::duration_to_repr(duration).map(|step| Self {
295
107
                step,
296
107
                _resolution: PhantomData,
297
107
            })
298
107
        }
299
    }
300

            
301
    impl<Resolution> TryFrom<LimitedResolutionDuration<Resolution>> for SignedDuration
302
    where
303
        Resolution: TimeResolution,
304
    {
305
        type Error = TimeError;
306

            
307
39
        fn try_from(value: LimitedResolutionDuration<Resolution>) -> Result<Self, Self::Error> {
308
39
            Resolution::repr_to_duration(value.step)
309
39
        }
310
    }
311

            
312
    impl<Resolution> TryFrom<Duration> for LimitedResolutionDuration<Resolution>
313
    where
314
        Resolution: TimeResolution,
315
    {
316
        type Error = TimeError;
317

            
318
21
        fn try_from(duration: Duration) -> Result<Self, Self::Error> {
319
21
            Self::try_from(SignedDuration::Positive(duration))
320
21
        }
321
    }
322

            
323
    impl<Resolution> TryFrom<LimitedResolutionDuration<Resolution>> for Duration
324
    where
325
        Resolution: TimeResolution,
326
    {
327
        type Error = TimeError;
328

            
329
        fn try_from(value: LimitedResolutionDuration<Resolution>) -> Result<Self, Self::Error> {
330
7
            match SignedDuration::try_from(value) {
331
7
                Ok(SignedDuration::Positive(value)) => Ok(value),
332
                _ => Err(TimeError::DeltaNotRepresentable),
333
            }
334
7
        }
335
    }
336

            
337
    /// A [`TimeResolution`] implementation that preserves nanosecond
338
    /// resolution. Internally, the number of microseconds is represented as an
339
    /// `i64`, allowing a range of +/- ~292.5 years.
340
    #[derive(Debug)]
341
    pub enum Nanoseconds {}
342

            
343
    impl TimeResolution for Nanoseconds {
344
        type Representation = i64;
345

            
346
        const FORMAT_SUFFIX: &'static str = "ns";
347

            
348
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
349
6
            if let Ok(unsigned) = u64::try_from(value) {
350
4
                Ok(SignedDuration::Positive(Duration::from_nanos(unsigned)))
351
2
            } else if let Some(positive) = value
352
2
                .checked_abs()
353
2
                .and_then(|value| u64::try_from(value).ok())
354
            {
355
2
                Ok(SignedDuration::Negative(Duration::from_nanos(positive)))
356
            } else {
357
                Err(TimeError::DeltaNotRepresentable)
358
            }
359
6
        }
360

            
361
11
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
362
11
            match duration {
363
6
                SignedDuration::Positive(duration) => {
364
6
                    i64::try_from(duration.as_nanos()).map_err(|_| TimeError::DeltaNotRepresentable)
365
                }
366
5
                SignedDuration::Negative(duration) => i64::try_from(duration.as_nanos())
367
5
                    .map(|repr| -repr)
368
5
                    .map_err(|_| TimeError::DeltaNotRepresentable),
369
            }
370
11
        }
371
    }
372

            
373
    /// A [`TimeResolution`] implementation that truncates time measurements to
374
    /// microseconds. Internally, the number of microseconds is represented as
375
    /// an `i64`, allowing a range of +/- ~292,471 years.
376
    #[derive(Debug)]
377
    pub enum Microseconds {}
378

            
379
    impl TimeResolution for Microseconds {
380
        type Representation = i64;
381

            
382
        const FORMAT_SUFFIX: &'static str = "us";
383

            
384
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
385
7
            if let Ok(unsigned) = u64::try_from(value) {
386
4
                Ok(SignedDuration::Positive(Duration::from_micros(unsigned)))
387
3
            } else if let Some(positive) = value
388
3
                .checked_abs()
389
3
                .and_then(|value| u64::try_from(value).ok())
390
            {
391
3
                Ok(SignedDuration::Negative(Duration::from_micros(positive)))
392
            } else {
393
                Err(TimeError::DeltaNotRepresentable)
394
            }
395
7
        }
396

            
397
14
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
398
14
            match duration {
399
7
                SignedDuration::Positive(duration) => i64::try_from(duration.as_micros())
400
7
                    .map_err(|_| TimeError::DeltaNotRepresentable),
401
7
                SignedDuration::Negative(duration) => {
402
7
                    let rounded_up = duration
403
7
                        .checked_add(Duration::from_nanos(999))
404
7
                        .ok_or(TimeError::DeltaNotRepresentable)?;
405
7
                    i64::try_from(rounded_up.as_micros())
406
7
                        .map(|repr| -repr)
407
7
                        .map_err(|_| TimeError::DeltaNotRepresentable)
408
                }
409
            }
410
14
        }
411
    }
412

            
413
    /// A [`TimeResolution`] implementation that truncates time measurements to
414
    /// milliseconds. Internally, the number of milliseconds is represented as
415
    /// an `i64`, allowing a range of +/- ~292.5 million years.
416
    #[derive(Debug)]
417
    pub enum Milliseconds {}
418

            
419
    impl TimeResolution for Milliseconds {
420
        type Representation = i64;
421

            
422
        const FORMAT_SUFFIX: &'static str = "ms";
423

            
424
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
425
7
            if let Ok(unsigned) = u64::try_from(value) {
426
4
                Ok(SignedDuration::Positive(Duration::from_millis(unsigned)))
427
3
            } else if let Some(positive) = value
428
3
                .checked_abs()
429
3
                .and_then(|value| u64::try_from(value).ok())
430
            {
431
3
                Ok(SignedDuration::Negative(Duration::from_millis(positive)))
432
            } else {
433
                Err(TimeError::DeltaNotRepresentable)
434
            }
435
7
        }
436

            
437
14
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
438
14
            match duration {
439
7
                SignedDuration::Positive(duration) => i64::try_from(duration.as_millis())
440
7
                    .map_err(|_| TimeError::DeltaNotRepresentable),
441
7
                SignedDuration::Negative(duration) => {
442
7
                    let rounded_up = duration
443
7
                        .checked_add(Duration::from_nanos(999_999))
444
7
                        .ok_or(TimeError::DeltaNotRepresentable)?;
445
7
                    i64::try_from(rounded_up.as_millis())
446
7
                        .map(|repr| -repr)
447
7
                        .map_err(|_| TimeError::DeltaNotRepresentable)
448
                }
449
            }
450
14
        }
451
    }
452

            
453
    /// A [`TimeResolution`] implementation that truncates time measurements to
454
    /// seconds. Internally, the number of seconds is represented as an `i64`,
455
    /// allowing a range of +/- ~21 times the age of the universe.
456
    #[derive(Debug)]
457
    pub enum Seconds {}
458

            
459
    impl TimeResolution for Seconds {
460
        type Representation = i64;
461

            
462
        const FORMAT_SUFFIX: &'static str = "s";
463

            
464
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
465
7
            if let Ok(unsigned) = u64::try_from(value) {
466
4
                Ok(SignedDuration::Positive(Duration::from_secs(unsigned)))
467
3
            } else if let Some(positive) = value
468
3
                .checked_abs()
469
3
                .and_then(|value| u64::try_from(value).ok())
470
            {
471
3
                Ok(SignedDuration::Negative(Duration::from_secs(positive)))
472
            } else {
473
                Err(TimeError::DeltaNotRepresentable)
474
            }
475
7
        }
476

            
477
14
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
478
14
            match duration {
479
7
                SignedDuration::Positive(duration) => {
480
7
                    i64::try_from(duration.as_secs()).map_err(|_| TimeError::DeltaNotRepresentable)
481
                }
482
7
                SignedDuration::Negative(duration) => {
483
7
                    let rounded_up = duration
484
7
                        .checked_add(Duration::from_nanos(999_999_999))
485
7
                        .ok_or(TimeError::DeltaNotRepresentable)?;
486
7
                    i64::try_from(rounded_up.as_secs())
487
7
                        .map(|repr| -repr)
488
7
                        .map_err(|_| TimeError::DeltaNotRepresentable)
489
                }
490
            }
491
14
        }
492
    }
493

            
494
    /// A [`TimeResolution`] implementation that truncates time measurements to
495
    /// minutes. Internally, the number of minutes is represented as an `i32`,
496
    /// allowing a range of +/- ~4,086 years.
497
    #[derive(Debug)]
498
    pub enum Minutes {}
499

            
500
    impl TimeResolution for Minutes {
501
        type Representation = i32;
502

            
503
        const FORMAT_SUFFIX: &'static str = "m";
504

            
505
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
506
7
            if let Ok(unsigned) = u64::try_from(value) {
507
4
                Ok(SignedDuration::Positive(Duration::from_secs(unsigned * 60)))
508
            } else {
509
3
                let positive = u64::try_from(i64::from(value).abs()).unwrap();
510
3
                Ok(SignedDuration::Negative(Duration::from_secs(positive * 60)))
511
            }
512
7
        }
513

            
514
14
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
515
14
            match duration {
516
7
                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / 60)
517
7
                    .map_err(|_| TimeError::DeltaNotRepresentable),
518
7
                SignedDuration::Negative(duration) => i32::try_from((duration.as_secs() + 59) / 60)
519
7
                    .map(|repr| -repr)
520
7
                    .map_err(|_| TimeError::DeltaNotRepresentable),
521
            }
522
14
        }
523
    }
524

            
525
    /// A [`TimeResolution`] implementation that truncates time measurements to
526
    /// hours. Internally, the number of hours is represented as an `i32`,
527
    /// allowing a range of +/- ~245,147 years.
528
    #[derive(Debug)]
529
    pub enum Hours {}
530

            
531
    impl TimeResolution for Hours {
532
        type Representation = i32;
533

            
534
        const FORMAT_SUFFIX: &'static str = "h";
535

            
536
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
537
6
            if let Ok(unsigned) = u64::try_from(value) {
538
4
                Ok(SignedDuration::Positive(Duration::from_secs(
539
4
                    unsigned * 60 * 60,
540
4
                )))
541
            } else {
542
2
                let positive = u64::try_from(i64::from(value).abs()).unwrap();
543
2
                Ok(SignedDuration::Negative(Duration::from_secs(
544
2
                    positive * 60 * 60,
545
2
                )))
546
            }
547
6
        }
548

            
549
12
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
550
12
            const FACTOR: u64 = 60 * 60;
551
12
            match duration {
552
7
                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / FACTOR)
553
7
                    .map_err(|_| TimeError::DeltaNotRepresentable),
554
5
                SignedDuration::Negative(duration) => {
555
5
                    i32::try_from((duration.as_secs() + FACTOR - 1) / FACTOR)
556
5
                        .map(|repr| -repr)
557
5
                        .map_err(|_| TimeError::DeltaNotRepresentable)
558
                }
559
            }
560
12
        }
561
    }
562

            
563
    /// A [`TimeResolution`] implementation that truncates time measurements to
564
    /// days. Internally, the number of days is represented as an `i32`,
565
    /// allowing a range of +/- ~5.88 million years.
566
    #[derive(Debug)]
567
    pub enum Days {}
568

            
569
    impl TimeResolution for Days {
570
        type Representation = i32;
571

            
572
        const FORMAT_SUFFIX: &'static str = "d";
573

            
574
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
575
7
            if let Ok(unsigned) = u64::try_from(value) {
576
4
                Ok(SignedDuration::Positive(Duration::from_secs(
577
4
                    unsigned * 24 * 60 * 60,
578
4
                )))
579
            } else {
580
3
                let positive = u64::try_from(i64::from(value).abs()).unwrap();
581
3
                Ok(SignedDuration::Negative(Duration::from_secs(
582
3
                    positive * 24 * 60 * 60,
583
3
                )))
584
            }
585
7
        }
586

            
587
14
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
588
14
            const FACTOR: u64 = 24 * 60 * 60;
589
14
            match duration {
590
7
                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / FACTOR)
591
7
                    .map_err(|_| TimeError::DeltaNotRepresentable),
592
7
                SignedDuration::Negative(duration) => {
593
7
                    Ok(i32::try_from((duration.as_secs() + FACTOR - 1) / FACTOR)
594
7
                        .map_or(i32::MIN, |repr| -repr))
595
                }
596
            }
597
14
        }
598
    }
599

            
600
    /// A [`TimeResolution`] implementation that truncates time measurements to
601
    /// weeks. Internally, the number of weeks is represented as an `i32`,
602
    /// allowing a range of +/- ~41.18 million years.
603
    #[derive(Debug)]
604
    pub enum Weeks {}
605

            
606
    impl TimeResolution for Weeks {
607
        type Representation = i32;
608

            
609
        const FORMAT_SUFFIX: &'static str = "w";
610

            
611
        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
612
7
            if let Ok(unsigned) = u64::try_from(value) {
613
4
                Ok(SignedDuration::Positive(Duration::from_secs(
614
4
                    unsigned * 7 * 24 * 60 * 60,
615
4
                )))
616
            } else {
617
3
                let positive = u64::try_from(i64::from(value).abs()).unwrap();
618
3
                Ok(SignedDuration::Negative(Duration::from_secs(
619
3
                    positive * 7 * 24 * 60 * 60,
620
3
                )))
621
            }
622
7
        }
623

            
624
14
        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
625
14
            const FACTOR: u64 = 7 * 24 * 60 * 60;
626
14
            match duration {
627
7
                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / FACTOR)
628
7
                    .map_err(|_| TimeError::DeltaNotRepresentable),
629
7
                SignedDuration::Negative(duration) => {
630
7
                    i32::try_from((duration.as_secs() + FACTOR - 1) / FACTOR)
631
7
                        .map(|repr| -repr)
632
7
                        .map_err(|_| TimeError::DeltaNotRepresentable)
633
                }
634
            }
635
14
        }
636
    }
637

            
638
1
    #[test]
639
1
    fn limited_resolution_duration_tests() {
640
14
        fn test_limited<Resolution: TimeResolution>(
641
14
            duration: Duration,
642
14
            expected_step: Resolution::Representation,
643
14
        ) {
644
14
            let limited = LimitedResolutionDuration::<Resolution>::try_from(duration).unwrap();
645
14
            assert_eq!(limited.step, expected_step);
646
14
            let encoded = limited.as_ord_bytes().unwrap();
647
14
            println!("Encoded {:?} to {} bytes", limited, encoded.len());
648
14
            let decoded = LimitedResolutionDuration::from_ord_bytes(&encoded).unwrap();
649
14
            assert_eq!(limited, decoded);
650
14
        }
651
1

            
652
7
        fn test_eq_limited<Resolution: TimeResolution>(
653
7
            duration: Duration,
654
7
            expected_step: Resolution::Representation,
655
7
        ) {
656
7
            test_limited::<Resolution>(duration, expected_step);
657
7
            let limited = LimitedResolutionDuration::<Resolution>::try_from(duration).unwrap();
658
7
            assert_eq!(duration, Duration::try_from(limited).unwrap());
659
7
        }
660
1

            
661
1
        let truncating_seconds = 7 * 24 * 60 * 60 + 24 * 60 * 60 + 60 * 60 + 60 + 1;
662
1
        let truncating = Duration::new(u64::try_from(truncating_seconds).unwrap(), 987_654_321);
663
1
        test_limited::<Weeks>(truncating, 1);
664
1
        test_limited::<Days>(truncating, 8);
665
1
        test_limited::<Hours>(truncating, 8 * 24 + 1);
666
1
        test_limited::<Minutes>(truncating, 8 * 24 * 60 + 60 + 1);
667
1
        test_limited::<Seconds>(truncating, 8 * 24 * 60 * 60 + 60 * 60 + 60 + 1);
668
1
        test_limited::<Milliseconds>(truncating, truncating_seconds * 1_000 + 987);
669
1
        test_limited::<Microseconds>(truncating, truncating_seconds * 1_000_000 + 987_654);
670
1

            
671
1
        let forty_two_days = Duration::from_secs(42 * 24 * 60 * 60);
672
1
        test_eq_limited::<Weeks>(forty_two_days, 6);
673
1
        test_eq_limited::<Days>(forty_two_days, 42);
674
1
        test_eq_limited::<Hours>(forty_two_days, 42 * 24);
675
1
        test_eq_limited::<Minutes>(forty_two_days, 42 * 24 * 60);
676
1
        test_eq_limited::<Seconds>(forty_two_days, 42 * 24 * 60 * 60);
677
1
        test_eq_limited::<Milliseconds>(forty_two_days, 42 * 24 * 60 * 60 * 1_000);
678
1
        test_eq_limited::<Microseconds>(forty_two_days, 42 * 24 * 60 * 60 * 1_000_000);
679
1
    }
680

            
681
    /// A timestamp (moment in time) stored with a limited `Resolution`. This
682
    /// type may be preferred to [`std::time::SystemTime`] because `SystemTime`
683
    /// serializes with nanosecond resolution. Often this level of precision is
684
    /// not needed and less storage and memory can be used.
685
    ///
686
    /// This type stores the representation of the timestamp as a
687
    /// [`LimitedResolutionDuration`] relative to `Epoch`.
688
    ///
689
    /// The `Resolution` type controls the storage size. The resolutions
690
    /// provided by BonsaiDb:
691
    ///
692
    /// - [`Weeks`]
693
    /// - [`Days`]
694
    /// - [`Hours`]
695
    /// - [`Minutes`]
696
    /// - [`Seconds`]
697
    /// - [`Milliseconds`]
698
    /// - [`Microseconds`]
699
    /// - [`Nanoseconds`]
700
    ///
701
    /// Other resolutions can be used by implementing [`TimeResolution`].
702
    /// BonsaiDb provides two [`TimeEpoch`] implementations:
703
    ///
704
    /// - [`UnixEpoch`]
705
    /// - [`BonsaiEpoch`]
706
47
    #[derive_where(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
707
    pub struct LimitedResolutionTimestamp<Resolution: TimeResolution, Epoch: TimeEpoch>(
708
        LimitedResolutionDuration<Resolution>,
709
        PhantomData<Epoch>,
710
    );
711

            
712
    impl<Resolution, Epoch> Default for LimitedResolutionTimestamp<Resolution, Epoch>
713
    where
714
        Resolution: TimeResolution,
715
        Epoch: TimeEpoch,
716
    {
717
        fn default() -> Self {
718
            Self(LimitedResolutionDuration::default(), PhantomData)
719
        }
720
    }
721

            
722
    impl<Resolution, Epoch> Serialize for LimitedResolutionTimestamp<Resolution, Epoch>
723
    where
724
        Resolution: TimeResolution,
725
        Epoch: TimeEpoch,
726
    {
727
8
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
728
8
        where
729
8
            S: serde::Serializer,
730
8
        {
731
8
            self.0.serialize(serializer)
732
8
        }
733
    }
734

            
735
    impl<'de, Resolution, Epoch> Deserialize<'de> for LimitedResolutionTimestamp<Resolution, Epoch>
736
    where
737
        Resolution: TimeResolution,
738
        Epoch: TimeEpoch,
739
    {
740
8
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
741
8
        where
742
8
            D: serde::Deserializer<'de>,
743
8
        {
744
8
            LimitedResolutionDuration::deserialize(deserializer)
745
8
                .map(|duration| Self(duration, PhantomData))
746
8
        }
747
    }
748

            
749
    /// An epoch for [`LimitedResolutionTimestamp`].
750
    pub trait TimeEpoch: Sized + Send + Sync {
751
        /// The offset from [`UNIX_EPOCH`] for this epoch.
752
        fn epoch_offset() -> Duration;
753
    }
754

            
755
    impl<Resolution, Epoch> LimitedResolutionTimestamp<Resolution, Epoch>
756
    where
757
        Resolution: TimeResolution,
758
        Epoch: TimeEpoch,
759
    {
760
        /// Returns [`SystemTime::now()`] limited to `Resolution`. The timestamp
761
        /// will be truncated, not rounded.
762
        #[must_use]
763
31
        pub fn now() -> Self {
764
31
            Self::try_from(SystemTime::now()).expect("now should always be representable")
765
31
        }
766

            
767
        /// Returns the [`Duration`] since January 1, 1970 00:00:00 UTC for this
768
        /// timestamp.
769
15
        pub fn duration_since_unix_epoch(&self) -> Result<Duration, TimeError> {
770
15
            let relative_offset = Resolution::repr_to_duration(self.0.step)?;
771
15
            match relative_offset {
772
8
                SignedDuration::Positive(offset) => Epoch::epoch_offset()
773
8
                    .checked_add(offset)
774
8
                    .ok_or(TimeError::DeltaNotRepresentable),
775
7
                SignedDuration::Negative(offset) => Epoch::epoch_offset()
776
7
                    .checked_sub(offset)
777
7
                    .ok_or(TimeError::DeltaNotRepresentable),
778
            }
779
15
        }
780

            
781
        /// Converts this value to a a decimal string containing the number of
782
        /// seconds since the unix epoch (January 1, 1970 00:00:00 UTC).
783
        ///
784
        /// The resulting string can be parsed as well.
785
        ///
786
        /// ```rust
787
        /// use bonsaidb_core::key::time::limited::{
788
        ///     BonsaiEpoch, LimitedResolutionTimestamp, Milliseconds,
789
        /// };
790
        ///
791
        /// let now = LimitedResolutionTimestamp::<Milliseconds, BonsaiEpoch>::now();
792
        /// let timestamp = now.to_timestamp_string().unwrap();
793
        /// let parsed = timestamp.parse().unwrap();
794
        /// assert_eq!(now, parsed);
795
        /// ```
796
        ///
797
        /// The difference between this function and `to_string()`] is that
798
        /// `to_string()` will revert to using the underlying
799
        /// [`LimitedResolutionDuration`]'s `to_string()` if a value is unable
800
        /// to be converted to a value relative to the unix epoch.
801
        pub fn to_timestamp_string(&self) -> Result<String, TimeError> {
802
            let mut string = String::new();
803
            self.display(&mut string).map(|_| string)
804
        }
805

            
806
15
        fn display(&self, f: &mut impl Write) -> Result<(), TimeError> {
807
15
            let since_epoch = self.duration_since_unix_epoch()?;
808
15
            write!(f, "{}", since_epoch.as_secs()).map_err(|_| TimeError::InvalidValue)?;
809
15
            if since_epoch.subsec_nanos() > 0 {
810
3
                if since_epoch.subsec_nanos() % 1_000_000 == 0 {
811
                    // Rendering any precision beyond milliseconds will yield 0s
812
1
                    write!(f, ".{:03}", since_epoch.subsec_millis())
813
1
                        .map_err(|_| TimeError::InvalidValue)
814
2
                } else if since_epoch.subsec_nanos() % 1_000 == 0 {
815
1
                    write!(f, ".{:06}", since_epoch.subsec_micros())
816
1
                        .map_err(|_| TimeError::InvalidValue)
817
                } else {
818
1
                    write!(f, ".{:09}", since_epoch.subsec_nanos())
819
1
                        .map_err(|_| TimeError::InvalidValue)
820
                }
821
            } else {
822
                // No subsecond
823
12
                Ok(())
824
            }
825
15
        }
826
    }
827

            
828
    impl<Resolution, Epoch> Debug for LimitedResolutionTimestamp<Resolution, Epoch>
829
    where
830
        Resolution: TimeResolution,
831
        Epoch: TimeEpoch,
832
    {
833
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
834
            write!(f, "LimitedResolutionTimestamp({self})")
835
        }
836
    }
837

            
838
    impl<Resolution, Epoch> Display for LimitedResolutionTimestamp<Resolution, Epoch>
839
    where
840
        Resolution: TimeResolution,
841
        Epoch: TimeEpoch,
842
    {
843
15
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
844
15
            self.display(f).or_else(|_| Display::fmt(&self.0, f))
845
15
        }
846
    }
847

            
848
    impl<Resolution, Epoch> FromStr for LimitedResolutionTimestamp<Resolution, Epoch>
849
    where
850
        Resolution: TimeResolution,
851
        Epoch: TimeEpoch,
852
    {
853
        type Err = TimeError;
854

            
855
7
        fn from_str(s: &str) -> Result<Self, Self::Err> {
856
7
            let mut parts = s.split('.');
857
7
            let seconds = parts.next().ok_or(TimeError::InvalidValue)?;
858
7
            let seconds = seconds
859
7
                .parse::<u64>()
860
7
                .map_err(|_| TimeError::InvalidValue)?;
861

            
862
7
            let duration = if let Some(subseconds_str) = parts.next() {
863
3
                if subseconds_str.len() > 9 || parts.next().is_some() {
864
                    return Err(TimeError::InvalidValue);
865
3
                }
866
3
                let subseconds = subseconds_str
867
3
                    .parse::<u32>()
868
3
                    .map_err(|_| TimeError::InvalidValue)?;
869

            
870
3
                let nanos =
871
3
                    subseconds * 10_u32.pow(u32::try_from(9 - subseconds_str.len()).unwrap());
872
3
                Duration::new(seconds, nanos)
873
            } else {
874
4
                Duration::from_secs(seconds)
875
            };
876

            
877
7
            let epoch = Epoch::epoch_offset();
878
7
            let duration = if duration < epoch {
879
7
                SignedDuration::Negative(epoch - duration)
880
            } else {
881
                SignedDuration::Positive(duration - epoch)
882
            };
883
            Ok(Self::from(
884
7
                LimitedResolutionDuration::<Resolution>::try_from(duration)?,
885
            ))
886
7
        }
887
    }
888

            
889
1
    #[test]
890
1
    fn timestamp_parse_tests() {
891
7
        fn test_roundtrip_parsing<Resolution: TimeResolution>() {
892
7
            let original = LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::now();
893
7
            let unix_timestamp = original.to_string();
894
7
            let parsed = unix_timestamp.parse().unwrap();
895
7
            assert_eq!(
896
1
                original, parsed,
897
1
                "{original} produced {unix_timestamp}, but parsed {parsed}"
898
1
            );
899
7
        }
900
1

            
901
1
        test_roundtrip_parsing::<Weeks>();
902
1
        test_roundtrip_parsing::<Days>();
903
1
        test_roundtrip_parsing::<Minutes>();
904
1
        test_roundtrip_parsing::<Seconds>();
905
1
        test_roundtrip_parsing::<Milliseconds>();
906
1
        test_roundtrip_parsing::<Microseconds>();
907
1
        test_roundtrip_parsing::<Nanoseconds>();
908
1
    }
909

            
910
    impl<'a, Resolution, Epoch> Key<'a> for LimitedResolutionTimestamp<Resolution, Epoch>
911
    where
912
        Resolution: TimeResolution,
913
        Epoch: TimeEpoch,
914
    {
915
        fn from_ord_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
916
            let duration = LimitedResolutionDuration::<Resolution>::from_ord_bytes(bytes)?;
917
            Ok(Self::from(duration))
918
        }
919
    }
920

            
921
    impl<'a, Resolution, Epoch> KeyEncoding<'a, Self> for LimitedResolutionTimestamp<Resolution, Epoch>
922
    where
923
        Resolution: TimeResolution,
924
        Epoch: TimeEpoch,
925
    {
926
        type Error = TimeError;
927

            
928
        const LENGTH: Option<usize> = None;
929

            
930
        fn as_ord_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
931
            self.0.as_ord_bytes()
932
        }
933
    }
934

            
935
    impl<Resolution, Epoch> From<LimitedResolutionDuration<Resolution>>
936
        for LimitedResolutionTimestamp<Resolution, Epoch>
937
    where
938
        Resolution: TimeResolution,
939
        Epoch: TimeEpoch,
940
    {
941
86
        fn from(duration: LimitedResolutionDuration<Resolution>) -> Self {
942
86
            Self(duration, PhantomData)
943
86
        }
944
    }
945

            
946
    impl<Resolution, Epoch> From<LimitedResolutionTimestamp<Resolution, Epoch>>
947
        for LimitedResolutionDuration<Resolution>
948
    where
949
        Resolution: TimeResolution,
950
        Epoch: TimeEpoch,
951
    {
952
        fn from(time: LimitedResolutionTimestamp<Resolution, Epoch>) -> Self {
953
            time.0
954
        }
955
    }
956

            
957
    impl<Resolution, Epoch> TryFrom<SystemTime> for LimitedResolutionTimestamp<Resolution, Epoch>
958
    where
959
        Resolution: TimeResolution,
960
        Epoch: TimeEpoch,
961
    {
962
        type Error = TimeError;
963
79
        fn try_from(time: SystemTime) -> Result<Self, TimeError> {
964
79
            let epoch = UNIX_EPOCH
965
79
                .checked_add(Epoch::epoch_offset())
966
79
                .ok_or(TimeError::DeltaNotRepresentable)?;
967
79
            match time.duration_since(epoch) {
968
34
                Ok(duration) => {
969
34
                    LimitedResolutionDuration::try_from(SignedDuration::Positive(duration))
970
34
                        .map(Self::from)
971
                }
972
45
                Err(_) => match epoch.duration_since(time) {
973
45
                    Ok(duration) => {
974
45
                        LimitedResolutionDuration::try_from(SignedDuration::Negative(duration))
975
45
                            .map(Self::from)
976
                    }
977
                    Err(_) => Err(TimeError::DeltaNotRepresentable),
978
                },
979
            }
980
79
        }
981
    }
982

            
983
    impl<Resolution, Epoch> TryFrom<LimitedResolutionTimestamp<Resolution, Epoch>> for SystemTime
984
    where
985
        Resolution: TimeResolution,
986
        Epoch: TimeEpoch,
987
    {
988
        type Error = TimeError;
989

            
990
32
        fn try_from(
991
32
            time: LimitedResolutionTimestamp<Resolution, Epoch>,
992
32
        ) -> Result<Self, TimeError> {
993
32
            let since_epoch = SignedDuration::try_from(time.0)?;
994
32
            let epoch = UNIX_EPOCH
995
32
                .checked_add(Epoch::epoch_offset())
996
32
                .ok_or(TimeError::DeltaNotRepresentable)?;
997
32
            let time = match since_epoch {
998
17
                SignedDuration::Positive(since_epoch) => epoch.checked_add(since_epoch),
999
15
                SignedDuration::Negative(since_epoch) => epoch.checked_sub(since_epoch),
            };

            
32
            time.ok_or(TimeError::DeltaNotRepresentable)
32
        }
    }

            
    /// A [`TimeEpoch`] implementation that allows storing
    /// [`LimitedResolutionTimestamp`] relative to the "unix epoch": January 1,
    /// 1970 00:00:00 UTC.
    pub struct UnixEpoch;

            
    impl TimeEpoch for UnixEpoch {
24
        fn epoch_offset() -> Duration {
24
            Duration::ZERO
24
        }
    }

            
    /// A [`TimeEpoch`] implementation that allows storing
    /// [`LimitedResolutionTimestamp`] relative to the 10-year anniversary of
    /// BonsaiDb: March 20, 2031 04:31:47 UTC.
    ///
    /// ## Why use [`BonsaiEpoch`] instead of [`UnixEpoch`]?
    ///
    /// [`LimitedResolutionTimestamp`] uses [`ordered-varint::Variable`] to
    /// implement [`Key`], which encodes the underlying value in as few bytes as
    /// possible while still preserving the ordering required by [`Key`].
    ///
    /// Many applications are not storing timestamps that predate the
    /// application being developed. When there is a likelihood that timestamps
    /// are closer to "now" than they are to the unix timestamp (January 1, 1970
    /// 00:00:00 UTC), the [`BonsaiEpoch`] will consistently encode the
    /// underlying representation in fewer bytes than when using [`UnixEpoch`].
    ///
    /// We hope BonsaiDb is a viable database option for many years. By setting
    /// this epoch 10 years from the start of BonsaiDb, it allows the internal
    /// representation of timestamps to slowly decrease in size until the
    /// 10-year anniversary. Over the following 10 years, the size will grow
    /// back to the same size it was at its conception, and then slowly grow as
    /// needed from that point on.
    pub struct BonsaiEpoch;

            
    impl BonsaiEpoch {
        const EPOCH: Duration = Duration::new(1_931_747_507, 0);
    }

            
    impl TimeEpoch for BonsaiEpoch {
109
        fn epoch_offset() -> Duration {
109
            Self::EPOCH
109
        }
    }

            
1
    #[test]
1
    fn limited_resolution_timestamp_tests() {
8
        fn test_resolution<Resolution: TimeResolution>(resolution: Duration) {
8
            let now_in_seconds = LimitedResolutionTimestamp::<Resolution, UnixEpoch>::now();
8
            let as_system = SystemTime::try_from(now_in_seconds).unwrap();
8
            let as_limited =
8
                LimitedResolutionTimestamp::<Resolution, UnixEpoch>::try_from(as_system).unwrap();
8
            assert_eq!(as_limited, now_in_seconds);
1

            
8
            let now_in_seconds = LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::now();
8
            let as_system = SystemTime::try_from(now_in_seconds).unwrap();
8
            let as_limited =
8
                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(as_system).unwrap();
8
            assert_eq!(as_limited, now_in_seconds);
1

            
8
            let slightly_before_epoch = UNIX_EPOCH + BonsaiEpoch::EPOCH
8
                - Duration::from_nanos(u64::try_from(resolution.as_nanos() / 2).unwrap());
8
            let unix_epoch_in_recent =
8
                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(
8
                    slightly_before_epoch,
8
                )
8
                .unwrap();
8
            let as_system = SystemTime::try_from(unix_epoch_in_recent).unwrap();
8
            let as_limited =
8
                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(as_system).unwrap();
8
            assert!(
8
                slightly_before_epoch
8
                    .duration_since(as_system)
8
                    .expect("timestamp should have been trunctated towards MIN")
8
                    < resolution
8
            );
8
            assert_eq!(as_limited, unix_epoch_in_recent);
1

            
8
            let slightly_after_epoch = UNIX_EPOCH
8
                + BonsaiEpoch::EPOCH
8
                + Duration::from_nanos(u64::try_from(resolution.as_nanos() / 2).unwrap());
8
            let unix_epoch_in_recent =
8
                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(
8
                    slightly_after_epoch,
8
                )
8
                .unwrap();
8
            let as_system = SystemTime::try_from(unix_epoch_in_recent).unwrap();
8
            println!("{slightly_after_epoch:?} converted to {unix_epoch_in_recent} and back as {as_system:?}");
8
            let as_limited =
8
                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(as_system).unwrap();
8
            assert!(
8
                slightly_after_epoch
8
                    .duration_since(as_system)
8
                    .expect("timestamp should have been truncated towards 0")
8
                    < resolution
8
            );
8
            assert_eq!(as_limited, unix_epoch_in_recent);
8
        }
1

            
1
        test_resolution::<Weeks>(Duration::from_secs(7 * 24 * 60 * 60));
1
        test_resolution::<Days>(Duration::from_secs(24 * 60 * 60));
1
        test_resolution::<Hours>(Duration::from_secs(60 * 60));
1
        test_resolution::<Minutes>(Duration::from_secs(60));
1
        test_resolution::<Seconds>(Duration::from_secs(1));
1
        test_resolution::<Milliseconds>(Duration::from_millis(1));
1
        test_resolution::<Microseconds>(Duration::from_micros(1));
1
        test_resolution::<Nanoseconds>(Duration::from_nanos(1));
1
    }

            
1
    #[test]
1
    fn serialization_tests() {
8
        fn test_serialization<Resolution: TimeResolution>() {
8
            let original = LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::now();
8
            let serialized = pot::to_vec(&original).unwrap();
8
            let deserialized = pot::from_slice(&serialized).unwrap();
8
            assert_eq!(original, deserialized);
8
        }
1

            
1
        test_serialization::<Weeks>();
1
        test_serialization::<Days>();
1
        test_serialization::<Hours>();
1
        test_serialization::<Minutes>();
1
        test_serialization::<Seconds>();
1
        test_serialization::<Milliseconds>();
1
        test_serialization::<Microseconds>();
1
        test_serialization::<Nanoseconds>();
1
    }
}

            
/// A signed duration of time represented in weeks (604,800 seconds).
/// Internally, the number of weeks is represented as an `i32`,
/// allowing a range of +/- ~41.18 million years.
pub type Weeks = limited::LimitedResolutionDuration<limited::Weeks>;

            
/// A signed duration of time represented in days (86,400 seconds). Internally,
/// the number of days is represented as an `i32`, allowing a range of +/- ~5.88
/// million years.
pub type Days = limited::LimitedResolutionDuration<limited::Days>;

            
/// A signed duration of time represented in hours (3,600 seconds). Internally,
/// the number of hours is represented as an `i32`, allowing a range of +/-
/// ~245,147 years.
pub type Hours = limited::LimitedResolutionDuration<limited::Hours>;

            
/// A signed duration of time represented in minutes (60 seconds). Internally,
/// the number of minutes is represented as an `i32`,
/// allowing a range of +/- ~4,086 years.
pub type Minutes = limited::LimitedResolutionDuration<limited::Minutes>;

            
/// A signed duration of time represented in seconds (with no partial
/// subseconds). Internally, the number of seconds is represented as an `i64`,
/// allowing a range of +/- ~21 times the age of the universe.
pub type Seconds = limited::LimitedResolutionDuration<limited::Seconds>;

            
/// A signed duration of time represented in milliseconds (1/1,000th of a
/// second). Internally, the number of milliseconds is represented as an `i64`,
/// allowing a range of +/- ~292.5 million years.
pub type Milliseconds = limited::LimitedResolutionDuration<limited::Milliseconds>;

            
/// A signed duration of time represented in microseconds (1/1,000,000th of a
/// second). Internally, the number of microseconds is represented as an `i64`,
/// allowing a range of +/- ~292,471 years.
pub type Microseconds = limited::LimitedResolutionDuration<limited::Microseconds>;

            
/// A signed duration of time represented in nanoseconds (1/1,000,000,000th of a
/// second). Internally, the number of microseconds is represented as an `i64`,
/// allowing a range of +/- ~292.5 years.
pub type Nanoseconds = limited::LimitedResolutionDuration<limited::Nanoseconds>;

            
/// A timestamp stored as the number of weeks (604,800 seconds) relative to
/// [`UnixEpoch`]. Internally, the number of weeks is represented as an `i32`,
/// allowing a range of +/- ~41.18 million years.
pub type WeeksSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Weeks, UnixEpoch>;

            
/// A timestamp stored as the number of days (86,400 seconds) relative to
/// [`UnixEpoch`]. Internally, the number of days is represented as an `i32`,
/// allowing a range of +/- ~5.88 million years.
pub type DaysSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Days, UnixEpoch>;

            
/// A timestamp stored as the number of hours (3,600 seconds) relative to
/// [`UnixEpoch`]. Internally, the number of hours is represented as an `i32`,
/// allowing a range of +/- ~245,147 years.
pub type HoursSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Hours, UnixEpoch>;

            
/// A timestamp stored as the number of minutes (60 seconds) relative to
/// [`UnixEpoch`]. Internally, the number of minutes is represented as an `i32`,
/// allowing a range of +/- ~4,086 years.
pub type MinutesSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Minutes, UnixEpoch>;

            
/// A timestamp stored as the number of seconds (with no partial subseconds)
/// relative to [`UnixEpoch`]. Internally, the number of seconds is represented
/// as an `i64`, allowing a range of +/- ~21 times the age of the universe.
pub type SecondsSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Seconds, UnixEpoch>;

            
/// A timestamp stored as the number of milliseconds (1/1,000th of a second)
/// relative to [`UnixEpoch`]. Internally, the number of milliseconds is
/// represented as an `i64`, allowing a range of +/- ~292.5 million years.
pub type MillisecondsSinceUnixEpoch =
    limited::LimitedResolutionTimestamp<limited::Milliseconds, UnixEpoch>;

            
/// A timestamp stored as the number of microseconds (1/1,000,000th of a second)
/// relative to [`UnixEpoch`]. Internally, the number of microseconds is
/// represented as an `i64`, allowing a range of +/- ~292,471 years.
pub type MicrosecondsSinceUnixEpoch =
    limited::LimitedResolutionTimestamp<limited::Microseconds, UnixEpoch>;

            
/// A timestamp stored as the number of nanoseconds (1/1,000,000,000th of a
/// second) relative to [`UnixEpoch`]. Internally, the number of microseconds is
/// represented as an `i64`, allowing a range of +/- ~292.5 years.
pub type NanosecondsSinceUnixEpoch =
    limited::LimitedResolutionTimestamp<limited::Nanoseconds, UnixEpoch>;

            
/// A timestamp stored as the number of weeks (604,800 seconds) relative to
/// [`BonsaiEpoch`]. Internally, the number of weeks is represented as an `i32`,
/// allowing a range of +/- ~41.18 million years.
pub type TimestampAsWeeks = limited::LimitedResolutionTimestamp<limited::Weeks, BonsaiEpoch>;

            
/// A timestamp stored as the number of days (86,400 seconds) relative to
/// [`BonsaiEpoch`]. Internally, the number of days is represented as an `i32`,
/// allowing a range of +/- ~5.88 million years.
pub type TimestampAsDays = limited::LimitedResolutionTimestamp<limited::Days, BonsaiEpoch>;

            
/// A timestamp stored as the number of hours (3,600 seconds) relative to
/// [`BonsaiEpoch`]. Internally, the number of hours is represented as an `i32`,
/// allowing a range of +/- ~245,147 years.
pub type TimestampAsHours = limited::LimitedResolutionTimestamp<limited::Hours, BonsaiEpoch>;

            
/// A timestamp stored as the number of minutes (60 seconds) relative to
/// [`BonsaiEpoch`]. Internally, the number of minutes is represented as an `i32`,
/// allowing a range of +/- ~4,086 years.
pub type TimestampAsMinutes = limited::LimitedResolutionTimestamp<limited::Minutes, BonsaiEpoch>;

            
/// A timestamp stored as the number of seconds (with no partial subseconds)
/// relative to [`BonsaiEpoch`]. Internally, the number of seconds is represented
/// as an `i64`, allowing a range of +/- ~21 times the age of the universe.
pub type TimestampAsSeconds = limited::LimitedResolutionTimestamp<limited::Seconds, BonsaiEpoch>;

            
/// A timestamp stored as the number of milliseconds (1/1,000th of a second)
/// relative to [`BonsaiEpoch`]. Internally, the number of milliseconds is
/// represented as an `i64`, allowing a range of +/- ~292.5 million years.
pub type TimestampAsMilliseconds =
    limited::LimitedResolutionTimestamp<limited::Milliseconds, BonsaiEpoch>;

            
/// A timestamp stored as the number of microseconds (1/1,000,000th of a second)
/// relative to [`BonsaiEpoch`]. Internally, the number of microseconds is
/// represented as an `i64`, allowing a range of +/- ~292,471 years.
pub type TimestampAsMicroseconds =
    limited::LimitedResolutionTimestamp<limited::Microseconds, BonsaiEpoch>;

            
/// A timestamp stored as the number of nanoseconds (1/1,000,000,000th of a
/// second) relative to [`BonsaiEpoch`]. Internally, the number of microseconds is
/// represented as an `i64`, allowing a range of +/- ~292.5 years.
pub type TimestampAsNanoseconds =
    limited::LimitedResolutionTimestamp<limited::Nanoseconds, BonsaiEpoch>;