bonsaidb_core/key/
time.rs

1use std::borrow::Cow;
2use std::fmt::Debug;
3use std::time::{Duration, SystemTime, UNIX_EPOCH};
4
5use ordered_varint::Variable;
6use serde::{Deserialize, Serialize};
7
8use crate::key::time::limited::{BonsaiEpoch, UnixEpoch};
9use crate::key::{ByteSource, CompositeKind, Key, KeyEncoding, KeyKind, KeyVisitor};
10
11impl<'k> Key<'k> for Duration {
12    const CAN_OWN_BYTES: bool = false;
13
14    fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
15        let merged = u128::decode_variable(bytes.as_ref()).map_err(|_| TimeError::InvalidValue)?;
16        let seconds = u64::try_from(merged >> 30).map_err(|_| TimeError::DeltaNotRepresentable)?;
17        let nanos = u32::try_from(merged & (2_u128.pow(30) - 1)).unwrap();
18        Ok(Self::new(seconds, nanos))
19    }
20}
21
22impl KeyEncoding<Self> for Duration {
23    type Error = TimeError;
24
25    const LENGTH: Option<usize> = None;
26
27    fn describe<Visitor>(visitor: &mut Visitor)
28    where
29        Visitor: KeyVisitor,
30    {
31        visitor.visit_composite(
32            CompositeKind::Struct(Cow::Borrowed("std::time::Duration")),
33            1,
34        );
35        visitor.visit_type(KeyKind::Unsigned);
36    }
37
38    fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
39        let merged = u128::from(self.as_secs()) << 30 | u128::from(self.subsec_nanos());
40        // It's safe to unwrap here, because under the hood ordered-varint can
41        // only raise an error if the top bits are set. Since we only ever add
42        // 94 bits, the top bits will not have any data set.
43        Ok(Cow::Owned(merged.to_variable_vec().unwrap()))
44    }
45}
46
47#[test]
48fn duration_key_tests() {
49    assert_eq!(
50        Duration::ZERO,
51        Duration::from_ord_bytes(ByteSource::Borrowed(
52            &Duration::ZERO.as_ord_bytes().unwrap()
53        ))
54        .unwrap()
55    );
56    assert_eq!(
57        Duration::MAX,
58        Duration::from_ord_bytes(ByteSource::Borrowed(&Duration::MAX.as_ord_bytes().unwrap()))
59            .unwrap()
60    );
61}
62
63impl<'k> Key<'k> for SystemTime {
64    const CAN_OWN_BYTES: bool = false;
65
66    fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
67        let since_epoch = Duration::from_ord_bytes(bytes)?;
68        UNIX_EPOCH
69            .checked_add(since_epoch)
70            .ok_or(TimeError::DeltaNotRepresentable)
71    }
72}
73
74impl KeyEncoding<Self> for SystemTime {
75    type Error = TimeError;
76
77    const LENGTH: Option<usize> = None;
78
79    fn describe<Visitor>(visitor: &mut Visitor)
80    where
81        Visitor: KeyVisitor,
82    {
83        visitor.visit_composite(
84            CompositeKind::Struct(Cow::Borrowed("std::time::SystemTime")),
85            1,
86        );
87        visitor.visit_type(KeyKind::Unsigned);
88    }
89
90    fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
91        let since_epoch = self.duration_since(UNIX_EPOCH).unwrap();
92        match since_epoch.as_ord_bytes()? {
93            Cow::Owned(bytes) => Ok(Cow::Owned(bytes)),
94            Cow::Borrowed(_) => unreachable!(),
95        }
96    }
97}
98
99#[test]
100fn system_time_tests() {
101    assert_eq!(
102        UNIX_EPOCH,
103        SystemTime::from_ord_bytes(ByteSource::Borrowed(&UNIX_EPOCH.as_ord_bytes().unwrap()))
104            .unwrap()
105    );
106    let now = SystemTime::now();
107    assert_eq!(
108        now,
109        SystemTime::from_ord_bytes(ByteSource::Borrowed(&now.as_ord_bytes().unwrap())).unwrap()
110    );
111}
112
113/// An error that indicates that the stored timestamp is unable to be converted
114/// to the destination type without losing data.
115#[derive(thiserror::Error, Debug)]
116#[error("the stored timestamp is outside the allowed range")]
117pub struct DeltaNotRepresentable;
118
119/// Errors that can arise from parsing times serialized with [`Key`].
120#[derive(thiserror::Error, Debug, Clone, Serialize, Deserialize)]
121pub enum TimeError {
122    /// An error that indicates that the stored timestamp is unable to be converted
123    /// to the destination type without losing data.
124    #[error("the stored timestamp is outside the allowed range")]
125    DeltaNotRepresentable,
126    /// The value stored was not encoded correctly.
127    #[error("invalid value")]
128    InvalidValue,
129}
130
131impl From<DeltaNotRepresentable> for TimeError {
132    fn from(_: DeltaNotRepresentable) -> Self {
133        Self::DeltaNotRepresentable
134    }
135}
136
137impl From<std::io::Error> for TimeError {
138    fn from(_: std::io::Error) -> Self {
139        Self::InvalidValue
140    }
141}
142
143/// Types for storing limited-precision Durations.
144pub mod limited {
145    use std::borrow::Cow;
146    use std::fmt::{self, Debug, Display, Write};
147    use std::hash::Hash;
148    use std::iter;
149    use std::marker::PhantomData;
150    use std::str::FromStr;
151    use std::time::{Duration, SystemTime, UNIX_EPOCH};
152
153    use derive_where::derive_where;
154    use ordered_varint::Variable;
155    use serde::{Deserialize, Serialize};
156
157    use crate::key::time::TimeError;
158    use crate::key::{ByteSource, CompositeKind, Key, KeyEncoding, KeyVisitor};
159
160    /// A [`Duration`] of time stored with a limited `Resolution`. This type may be
161    /// preferred to [`std::time::Duration`] because `Duration` takes a full 12
162    /// bytes to achieve its nanosecond resolution.
163    ///
164    /// Converting from [`Duration`] truncates the duration and performs no rounding.
165    ///
166    /// The `Resolution` type controls the storage size. The resolutions
167    /// provided by BonsaiDb:
168    ///
169    /// - [`Weeks`]
170    /// - [`Days`]
171    /// - [`Hours`]
172    /// - [`Minutes`]
173    /// - [`Seconds`]
174    /// - [`Milliseconds`]
175    /// - [`Microseconds`]
176    /// - [`Nanoseconds`]
177    ///
178    /// Other resolutions can be used by implementing [`TimeResolution`].
179    #[derive_where(Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
180    pub struct LimitedResolutionDuration<Resolution: TimeResolution> {
181        representation: Resolution::Representation,
182        _resolution: PhantomData<Resolution>,
183    }
184
185    /// A resolution of a time measurement.
186    pub trait TimeResolution: Debug + Send + Sync {
187        /// The in-memory and serialized representation for this resolution.
188        type Representation: Variable
189            + Serialize
190            + for<'de> Deserialize<'de>
191            + for<'k> Key<'k>
192            + Display
193            + Hash
194            + Eq
195            + PartialEq
196            + Ord
197            + PartialOrd
198            + Clone
199            + Copy
200            + Send
201            + Sync
202            + Debug
203            + Default;
204
205        /// The label used when formatting times with this resolution.
206        const FORMAT_SUFFIX: &'static str;
207
208        /// Converts a [`Self::Representation`] to [`Duration`].
209        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError>;
210
211        /// Converts a [`Duration`] to [`Self::Representation`].
212        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError>;
213    }
214
215    /// A [`Duration`] that can be either negative or positive.
216    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
217    pub enum SignedDuration {
218        /// A duration representing a positive measurement of time.
219        Positive(Duration),
220        /// A duration representing a negative measurement of time.
221        Negative(Duration),
222    }
223
224    impl SignedDuration {
225        /// Adds the two durations, honoring the signs, and returns the result
226        /// if the duration is representable.
227        #[must_use]
228        pub fn checked_add(self, other: Self) -> Option<Self> {
229            match (self, other) {
230                (SignedDuration::Positive(a), SignedDuration::Positive(b)) => {
231                    a.checked_add(b).map(SignedDuration::Positive)
232                }
233                (SignedDuration::Negative(a), SignedDuration::Negative(b)) => {
234                    a.checked_add(b).map(SignedDuration::Negative)
235                }
236                (SignedDuration::Positive(a), SignedDuration::Negative(b)) => {
237                    if let Some(result) = a.checked_sub(b) {
238                        Some(SignedDuration::Positive(result))
239                    } else {
240                        Some(SignedDuration::Negative(b - a))
241                    }
242                }
243                (SignedDuration::Negative(a), SignedDuration::Positive(b)) => {
244                    if let Some(result) = a.checked_sub(b) {
245                        Some(SignedDuration::Negative(result))
246                    } else {
247                        Some(SignedDuration::Positive(b - a))
248                    }
249                }
250            }
251        }
252    }
253
254    impl<Resolution> LimitedResolutionDuration<Resolution>
255    where
256        Resolution: TimeResolution,
257    {
258        /// Returns a new instance with the `representation` provided, which
259        /// conceptually is a unit of `Resolution`.
260        pub const fn new(representation: Resolution::Representation) -> Self {
261            Self {
262                representation,
263                _resolution: PhantomData,
264            }
265        }
266
267        /// Returns the internal representation of this duration.
268        pub const fn representation(&self) -> Resolution::Representation {
269            self.representation
270        }
271    }
272
273    impl<Resolution: TimeResolution> Debug for LimitedResolutionDuration<Resolution> {
274        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275            write!(f, "{:?}{}", self.representation, Resolution::FORMAT_SUFFIX)
276        }
277    }
278
279    impl<Resolution: TimeResolution> Display for LimitedResolutionDuration<Resolution> {
280        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281            write!(f, "{}{}", self.representation, Resolution::FORMAT_SUFFIX)
282        }
283    }
284
285    impl iter::Sum<SignedDuration> for Option<SignedDuration> {
286        fn sum<I: Iterator<Item = SignedDuration>>(mut iter: I) -> Self {
287            let first = iter.next();
288            iter.fold(first, |sum, duration| {
289                sum.and_then(|sum| sum.checked_add(duration))
290            })
291        }
292    }
293
294    impl iter::Sum<Self> for SignedDuration {
295        fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
296            iter.sum::<Option<Self>>().expect("operation overflowed")
297        }
298    }
299
300    impl<'k, Resolution> Key<'k> for LimitedResolutionDuration<Resolution>
301    where
302        Resolution: TimeResolution,
303    {
304        const CAN_OWN_BYTES: bool = false;
305
306        fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
307            let representation =
308                <Resolution::Representation as Variable>::decode_variable(bytes.as_ref())
309                    .map_err(|_| TimeError::InvalidValue)?;
310
311            Ok(Self {
312                representation,
313                _resolution: PhantomData,
314            })
315        }
316    }
317
318    impl<Resolution> KeyEncoding<Self> for LimitedResolutionDuration<Resolution>
319    where
320        Resolution: TimeResolution,
321    {
322        type Error = TimeError;
323
324        const LENGTH: Option<usize> = None;
325
326        fn describe<Visitor>(visitor: &mut Visitor)
327        where
328            Visitor: KeyVisitor,
329        {
330            visitor.visit_composite(
331                CompositeKind::Struct(Cow::Borrowed(
332                    "bonsaidb::core::key::time::LimitedResolutionDuration",
333                )),
334                1,
335            );
336            <Resolution::Representation as KeyEncoding>::describe(visitor);
337        }
338
339        fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
340            self.representation
341                .to_variable_vec()
342                .map(Cow::Owned)
343                .map_err(|_| TimeError::InvalidValue)
344        }
345    }
346
347    impl<Resolution> Default for LimitedResolutionDuration<Resolution>
348    where
349        Resolution: TimeResolution,
350    {
351        fn default() -> Self {
352            Self {
353                representation: <Resolution::Representation as Default>::default(),
354                _resolution: PhantomData,
355            }
356        }
357    }
358
359    impl<Resolution> Serialize for LimitedResolutionDuration<Resolution>
360    where
361        Resolution: TimeResolution,
362    {
363        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
364        where
365            S: serde::Serializer,
366        {
367            self.representation.serialize(serializer)
368        }
369    }
370
371    impl<'de, Resolution> Deserialize<'de> for LimitedResolutionDuration<Resolution>
372    where
373        Resolution: TimeResolution,
374    {
375        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
376        where
377            D: serde::Deserializer<'de>,
378        {
379            <Resolution::Representation as Deserialize<'de>>::deserialize(deserializer)
380                .map(Self::new)
381        }
382    }
383
384    impl<Resolution> TryFrom<SignedDuration> for LimitedResolutionDuration<Resolution>
385    where
386        Resolution: TimeResolution,
387    {
388        type Error = TimeError;
389
390        fn try_from(duration: SignedDuration) -> Result<Self, Self::Error> {
391            Resolution::duration_to_repr(duration).map(|representation| Self {
392                representation,
393                _resolution: PhantomData,
394            })
395        }
396    }
397
398    impl<Resolution> TryFrom<LimitedResolutionDuration<Resolution>> for SignedDuration
399    where
400        Resolution: TimeResolution,
401    {
402        type Error = TimeError;
403
404        fn try_from(value: LimitedResolutionDuration<Resolution>) -> Result<Self, Self::Error> {
405            Resolution::repr_to_duration(value.representation)
406        }
407    }
408
409    impl<Resolution> TryFrom<Duration> for LimitedResolutionDuration<Resolution>
410    where
411        Resolution: TimeResolution,
412    {
413        type Error = TimeError;
414
415        fn try_from(duration: Duration) -> Result<Self, Self::Error> {
416            Self::try_from(SignedDuration::Positive(duration))
417        }
418    }
419
420    impl<Resolution> TryFrom<LimitedResolutionDuration<Resolution>> for Duration
421    where
422        Resolution: TimeResolution,
423    {
424        type Error = TimeError;
425
426        fn try_from(value: LimitedResolutionDuration<Resolution>) -> Result<Self, Self::Error> {
427            match SignedDuration::try_from(value) {
428                Ok(SignedDuration::Positive(value)) => Ok(value),
429                _ => Err(TimeError::DeltaNotRepresentable),
430            }
431        }
432    }
433
434    impl<Resolution> iter::Sum<LimitedResolutionDuration<Resolution>>
435        for Option<LimitedResolutionDuration<Resolution>>
436    where
437        Resolution: TimeResolution,
438    {
439        fn sum<I: Iterator<Item = LimitedResolutionDuration<Resolution>>>(mut iter: I) -> Self {
440            let first = iter
441                .next()
442                .and_then(|dur| Resolution::repr_to_duration(dur.representation).ok());
443            let duration = iter.fold(first, |sum, dur| {
444                sum.and_then(|sum| {
445                    Resolution::repr_to_duration(dur.representation)
446                        .ok()
447                        .and_then(|dur| sum.checked_add(dur))
448                })
449            });
450            duration.and_then(|dur| {
451                Resolution::duration_to_repr(dur)
452                    .ok()
453                    .map(LimitedResolutionDuration::new)
454            })
455        }
456    }
457
458    impl<Resolution> iter::Sum<Self> for LimitedResolutionDuration<Resolution>
459    where
460        Resolution: TimeResolution,
461    {
462        fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
463            iter.sum::<Option<Self>>().expect("operation overflowed")
464        }
465    }
466
467    #[test]
468    fn limited_resolution_duration_sum() {
469        use super::Nanoseconds;
470        assert_eq!(
471            [
472                Nanoseconds::new(1),
473                Nanoseconds::new(2),
474                Nanoseconds::new(3),
475            ]
476            .into_iter()
477            .sum::<Nanoseconds>(),
478            Nanoseconds::new(6)
479        );
480        assert_eq!(
481            [
482                Nanoseconds::new(1),
483                Nanoseconds::new(2),
484                Nanoseconds::new(3),
485            ]
486            .into_iter()
487            .sum::<Option<Nanoseconds>>(),
488            Some(Nanoseconds::new(6))
489        );
490
491        assert_eq!(
492            [Nanoseconds::new(1), Nanoseconds::new(i64::MAX)]
493                .into_iter()
494                .sum::<Option<Nanoseconds>>(),
495            None
496        );
497
498        assert_eq!(
499            [Nanoseconds::new(i64::MAX), Nanoseconds::new(1)]
500                .into_iter()
501                .sum::<Option<Nanoseconds>>(),
502            None
503        );
504
505        assert_eq!(
506            [Nanoseconds::new(i64::MIN), Nanoseconds::new(-11)]
507                .into_iter()
508                .sum::<Option<Nanoseconds>>(),
509            None
510        );
511
512        assert_eq!(
513            [Nanoseconds::new(1), Nanoseconds::new(i64::MIN)]
514                .into_iter()
515                .sum::<Option<Nanoseconds>>(),
516            Some(Nanoseconds::new(i64::MIN + 1))
517        );
518        assert_eq!(
519            [Nanoseconds::new(i64::MIN), Nanoseconds::new(1)]
520                .into_iter()
521                .sum::<Option<Nanoseconds>>(),
522            Some(Nanoseconds::new(i64::MIN + 1))
523        );
524    }
525
526    /// A [`TimeResolution`] implementation that preserves nanosecond
527    /// resolution. Internally, the number of microseconds is represented as an
528    /// `i64`, allowing a range of +/- ~292.5 years.
529    #[derive(Debug)]
530    pub enum Nanoseconds {}
531
532    const I64_MIN_ABS_AS_U64: u64 = 9_223_372_036_854_775_808;
533
534    impl TimeResolution for Nanoseconds {
535        type Representation = i64;
536
537        const FORMAT_SUFFIX: &'static str = "ns";
538
539        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
540            if let Ok(unsigned) = u64::try_from(value) {
541                Ok(SignedDuration::Positive(Duration::from_nanos(unsigned)))
542            } else {
543                let positive = value
544                    .checked_abs()
545                    .and_then(|value| u64::try_from(value).ok())
546                    .unwrap_or(I64_MIN_ABS_AS_U64);
547                Ok(SignedDuration::Negative(Duration::from_nanos(positive)))
548            }
549        }
550
551        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
552            match duration {
553                SignedDuration::Positive(duration) => {
554                    i64::try_from(duration.as_nanos()).map_err(|_| TimeError::DeltaNotRepresentable)
555                }
556                SignedDuration::Negative(duration) => {
557                    let nanos = duration.as_nanos();
558                    if let Ok(nanos) = i64::try_from(nanos) {
559                        Ok(-nanos)
560                    } else if nanos == u128::from(I64_MIN_ABS_AS_U64) {
561                        Ok(i64::MIN)
562                    } else {
563                        Err(TimeError::DeltaNotRepresentable)
564                    }
565                }
566            }
567        }
568    }
569
570    /// A [`TimeResolution`] implementation that truncates time measurements to
571    /// microseconds. Internally, the number of microseconds is represented as
572    /// an `i64`, allowing a range of +/- ~292,471 years.
573    #[derive(Debug)]
574    pub enum Microseconds {}
575
576    impl TimeResolution for Microseconds {
577        type Representation = i64;
578
579        const FORMAT_SUFFIX: &'static str = "us";
580
581        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
582            if let Ok(unsigned) = u64::try_from(value) {
583                Ok(SignedDuration::Positive(Duration::from_micros(unsigned)))
584            } else {
585                let positive = value
586                    .checked_abs()
587                    .and_then(|value| u64::try_from(value).ok())
588                    .unwrap_or(u64::MAX);
589                Ok(SignedDuration::Negative(Duration::from_micros(positive)))
590            }
591        }
592
593        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
594            match duration {
595                SignedDuration::Positive(duration) => i64::try_from(duration.as_micros())
596                    .map_err(|_| TimeError::DeltaNotRepresentable),
597                SignedDuration::Negative(duration) => {
598                    let rounded_up = duration
599                        .checked_add(Duration::from_nanos(999))
600                        .ok_or(TimeError::DeltaNotRepresentable)?;
601                    i64::try_from(rounded_up.as_micros())
602                        .map(|repr| -repr)
603                        .map_err(|_| TimeError::DeltaNotRepresentable)
604                }
605            }
606        }
607    }
608
609    /// A [`TimeResolution`] implementation that truncates time measurements to
610    /// milliseconds. Internally, the number of milliseconds is represented as
611    /// an `i64`, allowing a range of +/- ~292.5 million years.
612    #[derive(Debug)]
613    pub enum Milliseconds {}
614
615    impl TimeResolution for Milliseconds {
616        type Representation = i64;
617
618        const FORMAT_SUFFIX: &'static str = "ms";
619
620        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
621            if let Ok(unsigned) = u64::try_from(value) {
622                Ok(SignedDuration::Positive(Duration::from_millis(unsigned)))
623            } else {
624                let positive = value
625                    .checked_abs()
626                    .and_then(|value| u64::try_from(value).ok())
627                    .unwrap_or(u64::MAX);
628                Ok(SignedDuration::Negative(Duration::from_millis(positive)))
629            }
630        }
631
632        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
633            match duration {
634                SignedDuration::Positive(duration) => i64::try_from(duration.as_millis())
635                    .map_err(|_| TimeError::DeltaNotRepresentable),
636                SignedDuration::Negative(duration) => {
637                    let rounded_up = duration
638                        .checked_add(Duration::from_nanos(999_999))
639                        .ok_or(TimeError::DeltaNotRepresentable)?;
640                    i64::try_from(rounded_up.as_millis())
641                        .map(|repr| -repr)
642                        .map_err(|_| TimeError::DeltaNotRepresentable)
643                }
644            }
645        }
646    }
647
648    /// A [`TimeResolution`] implementation that truncates time measurements to
649    /// seconds. Internally, the number of seconds is represented as an `i64`,
650    /// allowing a range of +/- ~21 times the age of the universe.
651    #[derive(Debug)]
652    pub enum Seconds {}
653
654    impl TimeResolution for Seconds {
655        type Representation = i64;
656
657        const FORMAT_SUFFIX: &'static str = "s";
658
659        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
660            if let Ok(unsigned) = u64::try_from(value) {
661                Ok(SignedDuration::Positive(Duration::from_secs(unsigned)))
662            } else {
663                let positive = value
664                    .checked_abs()
665                    .and_then(|value| u64::try_from(value).ok())
666                    .unwrap_or(u64::MAX);
667                Ok(SignedDuration::Negative(Duration::from_secs(positive)))
668            }
669        }
670
671        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
672            match duration {
673                SignedDuration::Positive(duration) => {
674                    i64::try_from(duration.as_secs()).map_err(|_| TimeError::DeltaNotRepresentable)
675                }
676                SignedDuration::Negative(duration) => {
677                    let rounded_up = duration
678                        .checked_add(Duration::from_nanos(999_999_999))
679                        .ok_or(TimeError::DeltaNotRepresentable)?;
680                    i64::try_from(rounded_up.as_secs())
681                        .map(|repr| -repr)
682                        .map_err(|_| TimeError::DeltaNotRepresentable)
683                }
684            }
685        }
686    }
687
688    /// A [`TimeResolution`] implementation that truncates time measurements to
689    /// minutes. Internally, the number of minutes is represented as an `i32`,
690    /// allowing a range of +/- ~4,086 years.
691    #[derive(Debug)]
692    pub enum Minutes {}
693
694    impl TimeResolution for Minutes {
695        type Representation = i32;
696
697        const FORMAT_SUFFIX: &'static str = "m";
698
699        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
700            if let Ok(unsigned) = u64::try_from(value) {
701                Ok(SignedDuration::Positive(Duration::from_secs(unsigned * 60)))
702            } else {
703                let positive = u64::try_from(i64::from(value).abs()).unwrap();
704                Ok(SignedDuration::Negative(Duration::from_secs(positive * 60)))
705            }
706        }
707
708        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
709            match duration {
710                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / 60)
711                    .map_err(|_| TimeError::DeltaNotRepresentable),
712                SignedDuration::Negative(duration) => i32::try_from((duration.as_secs() + 59) / 60)
713                    .map(|repr| -repr)
714                    .map_err(|_| TimeError::DeltaNotRepresentable),
715            }
716        }
717    }
718
719    /// A [`TimeResolution`] implementation that truncates time measurements to
720    /// hours. Internally, the number of hours is represented as an `i32`,
721    /// allowing a range of +/- ~245,147 years.
722    #[derive(Debug)]
723    pub enum Hours {}
724
725    impl TimeResolution for Hours {
726        type Representation = i32;
727
728        const FORMAT_SUFFIX: &'static str = "h";
729
730        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
731            if let Ok(unsigned) = u64::try_from(value) {
732                Ok(SignedDuration::Positive(Duration::from_secs(
733                    unsigned * 60 * 60,
734                )))
735            } else {
736                let positive = u64::try_from(i64::from(value).abs()).unwrap();
737                Ok(SignedDuration::Negative(Duration::from_secs(
738                    positive * 60 * 60,
739                )))
740            }
741        }
742
743        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
744            const FACTOR: u64 = 60 * 60;
745            match duration {
746                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / FACTOR)
747                    .map_err(|_| TimeError::DeltaNotRepresentable),
748                SignedDuration::Negative(duration) => {
749                    i32::try_from((duration.as_secs() + FACTOR - 1) / FACTOR)
750                        .map(|repr| -repr)
751                        .map_err(|_| TimeError::DeltaNotRepresentable)
752                }
753            }
754        }
755    }
756
757    /// A [`TimeResolution`] implementation that truncates time measurements to
758    /// days. Internally, the number of days is represented as an `i32`,
759    /// allowing a range of +/- ~5.88 million years.
760    #[derive(Debug)]
761    pub enum Days {}
762
763    impl TimeResolution for Days {
764        type Representation = i32;
765
766        const FORMAT_SUFFIX: &'static str = "d";
767
768        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
769            if let Ok(unsigned) = u64::try_from(value) {
770                Ok(SignedDuration::Positive(Duration::from_secs(
771                    unsigned * 24 * 60 * 60,
772                )))
773            } else {
774                let positive = u64::try_from(i64::from(value).abs()).unwrap();
775                Ok(SignedDuration::Negative(Duration::from_secs(
776                    positive * 24 * 60 * 60,
777                )))
778            }
779        }
780
781        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
782            const FACTOR: u64 = 24 * 60 * 60;
783            match duration {
784                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / FACTOR)
785                    .map_err(|_| TimeError::DeltaNotRepresentable),
786                SignedDuration::Negative(duration) => {
787                    Ok(i32::try_from((duration.as_secs() + FACTOR - 1) / FACTOR)
788                        .map_or(i32::MIN, |repr| -repr))
789                }
790            }
791        }
792    }
793
794    /// A [`TimeResolution`] implementation that truncates time measurements to
795    /// weeks. Internally, the number of weeks is represented as an `i32`,
796    /// allowing a range of +/- ~41.18 million years.
797    #[derive(Debug)]
798    pub enum Weeks {}
799
800    impl TimeResolution for Weeks {
801        type Representation = i32;
802
803        const FORMAT_SUFFIX: &'static str = "w";
804
805        fn repr_to_duration(value: Self::Representation) -> Result<SignedDuration, TimeError> {
806            if let Ok(unsigned) = u64::try_from(value) {
807                Ok(SignedDuration::Positive(Duration::from_secs(
808                    unsigned * 7 * 24 * 60 * 60,
809                )))
810            } else {
811                let positive = u64::try_from(i64::from(value).abs()).unwrap();
812                Ok(SignedDuration::Negative(Duration::from_secs(
813                    positive * 7 * 24 * 60 * 60,
814                )))
815            }
816        }
817
818        fn duration_to_repr(duration: SignedDuration) -> Result<Self::Representation, TimeError> {
819            const FACTOR: u64 = 7 * 24 * 60 * 60;
820            match duration {
821                SignedDuration::Positive(duration) => i32::try_from(duration.as_secs() / FACTOR)
822                    .map_err(|_| TimeError::DeltaNotRepresentable),
823                SignedDuration::Negative(duration) => {
824                    i32::try_from((duration.as_secs() + FACTOR - 1) / FACTOR)
825                        .map(|repr| -repr)
826                        .map_err(|_| TimeError::DeltaNotRepresentable)
827                }
828            }
829        }
830    }
831
832    #[test]
833    fn limited_resolution_duration_tests() {
834        fn test_limited<Resolution: TimeResolution>(
835            duration: Duration,
836            expected_step: Resolution::Representation,
837        ) {
838            let limited = LimitedResolutionDuration::<Resolution>::try_from(duration).unwrap();
839            assert_eq!(limited.representation, expected_step);
840            let encoded = limited.as_ord_bytes().unwrap();
841            println!("Encoded {limited:?} to {} bytes", encoded.len());
842            let decoded =
843                LimitedResolutionDuration::from_ord_bytes(ByteSource::Borrowed(&encoded)).unwrap();
844            assert_eq!(limited, decoded);
845        }
846
847        fn test_eq_limited<Resolution: TimeResolution>(
848            duration: Duration,
849            expected_step: Resolution::Representation,
850        ) {
851            test_limited::<Resolution>(duration, expected_step);
852            let limited = LimitedResolutionDuration::<Resolution>::try_from(duration).unwrap();
853            assert_eq!(duration, Duration::try_from(limited).unwrap());
854        }
855
856        let truncating_seconds = 7 * 24 * 60 * 60 + 24 * 60 * 60 + 60 * 60 + 60 + 1;
857        let truncating = Duration::new(u64::try_from(truncating_seconds).unwrap(), 987_654_321);
858        test_limited::<Weeks>(truncating, 1);
859        test_limited::<Days>(truncating, 8);
860        test_limited::<Hours>(truncating, 8 * 24 + 1);
861        test_limited::<Minutes>(truncating, 8 * 24 * 60 + 60 + 1);
862        test_limited::<Seconds>(truncating, 8 * 24 * 60 * 60 + 60 * 60 + 60 + 1);
863        test_limited::<Milliseconds>(truncating, truncating_seconds * 1_000 + 987);
864        test_limited::<Microseconds>(truncating, truncating_seconds * 1_000_000 + 987_654);
865
866        let forty_two_days = Duration::from_secs(42 * 24 * 60 * 60);
867        test_eq_limited::<Weeks>(forty_two_days, 6);
868        test_eq_limited::<Days>(forty_two_days, 42);
869        test_eq_limited::<Hours>(forty_two_days, 42 * 24);
870        test_eq_limited::<Minutes>(forty_two_days, 42 * 24 * 60);
871        test_eq_limited::<Seconds>(forty_two_days, 42 * 24 * 60 * 60);
872        test_eq_limited::<Milliseconds>(forty_two_days, 42 * 24 * 60 * 60 * 1_000);
873        test_eq_limited::<Microseconds>(forty_two_days, 42 * 24 * 60 * 60 * 1_000_000);
874    }
875
876    /// A timestamp (moment in time) stored with a limited `Resolution`. This
877    /// type may be preferred to [`std::time::SystemTime`] because `SystemTime`
878    /// serializes with nanosecond resolution. Often this level of precision is
879    /// not needed and less storage and memory can be used.
880    ///
881    /// This type stores the representation of the timestamp as a
882    /// [`LimitedResolutionDuration`] relative to `Epoch`.
883    ///
884    /// The `Resolution` type controls the storage size. The resolutions
885    /// provided by BonsaiDb:
886    ///
887    /// - [`Weeks`]
888    /// - [`Days`]
889    /// - [`Hours`]
890    /// - [`Minutes`]
891    /// - [`Seconds`]
892    /// - [`Milliseconds`]
893    /// - [`Microseconds`]
894    /// - [`Nanoseconds`]
895    ///
896    /// Other resolutions can be used by implementing [`TimeResolution`].
897    /// BonsaiDb provides two [`TimeEpoch`] implementations:
898    ///
899    /// - [`UnixEpoch`]
900    /// - [`BonsaiEpoch`]
901    #[derive_where(Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
902    pub struct LimitedResolutionTimestamp<Resolution: TimeResolution, Epoch: TimeEpoch>(
903        LimitedResolutionDuration<Resolution>,
904        PhantomData<Epoch>,
905    );
906
907    impl<Resolution, Epoch> Default for LimitedResolutionTimestamp<Resolution, Epoch>
908    where
909        Resolution: TimeResolution,
910        Epoch: TimeEpoch,
911    {
912        fn default() -> Self {
913            Self(LimitedResolutionDuration::default(), PhantomData)
914        }
915    }
916
917    impl<Resolution, Epoch> Serialize for LimitedResolutionTimestamp<Resolution, Epoch>
918    where
919        Resolution: TimeResolution,
920        Epoch: TimeEpoch,
921    {
922        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
923        where
924            S: serde::Serializer,
925        {
926            self.0.serialize(serializer)
927        }
928    }
929
930    impl<'de, Resolution, Epoch> Deserialize<'de> for LimitedResolutionTimestamp<Resolution, Epoch>
931    where
932        Resolution: TimeResolution,
933        Epoch: TimeEpoch,
934    {
935        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
936        where
937            D: serde::Deserializer<'de>,
938        {
939            LimitedResolutionDuration::deserialize(deserializer)
940                .map(|duration| Self(duration, PhantomData))
941        }
942    }
943
944    /// An epoch for [`LimitedResolutionTimestamp`].
945    pub trait TimeEpoch: Sized + Send + Sync {
946        /// The name of this epoch, used in [`KeyEncoding::describe`] to
947        /// disambiguate timestamps with different epochs.
948        fn name() -> &'static str;
949
950        /// The offset from [`UNIX_EPOCH`] for this epoch.
951        fn epoch_offset() -> Duration;
952    }
953
954    impl<Resolution, Epoch> LimitedResolutionTimestamp<Resolution, Epoch>
955    where
956        Resolution: TimeResolution,
957        Epoch: TimeEpoch,
958    {
959        /// Returns [`SystemTime::now()`] limited to `Resolution`. The timestamp
960        /// will be truncated, not rounded.
961        ///
962        /// # Panics
963        ///
964        /// This function will panic [`SystemTime::now()`] is unable to be
965        /// represented by `Resolution` and `Epoch`.
966        #[must_use]
967        pub fn now() -> Self {
968            Self::try_from(SystemTime::now()).expect("now should always be representable")
969        }
970
971        /// Returns the duration since another timestamp. This returns None if
972        /// `other` is before `self`,
973        pub fn duration_since(
974            &self,
975            other: &impl AnyTimestamp,
976        ) -> Result<Option<Duration>, TimeError> {
977            let self_delta = self.duration_since_unix_epoch()?;
978            let other_delta = other.duration_since_unix_epoch()?;
979            Ok(self_delta.checked_sub(other_delta))
980        }
981
982        /// Returns the absolute duration between `self` and `other`.
983        pub fn duration_between(&self, other: &impl AnyTimestamp) -> Result<Duration, TimeError> {
984            let self_delta = self.duration_since_unix_epoch()?;
985            let other_delta = other.duration_since_unix_epoch()?;
986            if self_delta < other_delta {
987                Ok(other_delta - self_delta)
988            } else {
989                Ok(self_delta - other_delta)
990            }
991        }
992
993        /// Returns the internal representation of this timestamp, which is a
994        /// unit of `Resolution`.
995        pub const fn representation(&self) -> Resolution::Representation {
996            self.0.representation()
997        }
998
999        /// Returns a new timestamp using the `representation` provided.
1000        pub fn from_representation(representation: Resolution::Representation) -> Self {
1001            Self::from(LimitedResolutionDuration::new(representation))
1002        }
1003
1004        /// Converts this value to a a decimal string containing the number of
1005        /// seconds since the unix epoch (January 1, 1970 00:00:00 UTC).
1006        ///
1007        /// The resulting string can be parsed as well.
1008        ///
1009        /// ```rust
1010        /// use bonsaidb_core::key::time::limited::{
1011        ///     BonsaiEpoch, LimitedResolutionTimestamp, Milliseconds,
1012        /// };
1013        ///
1014        /// let now = LimitedResolutionTimestamp::<Milliseconds, BonsaiEpoch>::now();
1015        /// let timestamp = now.to_timestamp_string().unwrap();
1016        /// let parsed = timestamp.parse().unwrap();
1017        /// assert_eq!(now, parsed);
1018        /// ```
1019        ///
1020        /// The difference between this function and `to_string()`] is that
1021        /// `to_string()` will revert to using the underlying
1022        /// [`LimitedResolutionDuration`]'s `to_string()` if a value is unable
1023        /// to be converted to a value relative to the unix epoch.
1024        pub fn to_timestamp_string(&self) -> Result<String, TimeError> {
1025            let mut string = String::new();
1026            self.display(&mut string).map(|_| string)
1027        }
1028
1029        fn display(&self, f: &mut impl Write) -> Result<(), TimeError> {
1030            let since_epoch = self.duration_since_unix_epoch()?;
1031            write!(f, "{}", since_epoch.as_secs()).map_err(|_| TimeError::InvalidValue)?;
1032            if since_epoch.subsec_nanos() > 0 {
1033                if since_epoch.subsec_nanos() % 1_000_000 == 0 {
1034                    // Rendering any precision beyond milliseconds will yield 0s
1035                    write!(f, ".{:03}", since_epoch.subsec_millis())
1036                        .map_err(|_| TimeError::InvalidValue)
1037                } else if since_epoch.subsec_nanos() % 1_000 == 0 {
1038                    write!(f, ".{:06}", since_epoch.subsec_micros())
1039                        .map_err(|_| TimeError::InvalidValue)
1040                } else {
1041                    write!(f, ".{:09}", since_epoch.subsec_nanos())
1042                        .map_err(|_| TimeError::InvalidValue)
1043                }
1044            } else {
1045                // No subsecond
1046                Ok(())
1047            }
1048        }
1049    }
1050
1051    /// A timestamp that can report it sduration since the Unix Epoch.
1052    pub trait AnyTimestamp {
1053        /// Returns the [`Duration`] since January 1, 1970 00:00:00 UTC for this
1054        /// timestamp.
1055        fn duration_since_unix_epoch(&self) -> Result<Duration, TimeError>;
1056    }
1057
1058    impl AnyTimestamp for SystemTime {
1059        fn duration_since_unix_epoch(&self) -> Result<Duration, TimeError> {
1060            Ok(self.duration_since(UNIX_EPOCH).unwrap())
1061        }
1062    }
1063
1064    impl<Resolution, Epoch> AnyTimestamp for LimitedResolutionTimestamp<Resolution, Epoch>
1065    where
1066        Resolution: TimeResolution,
1067        Epoch: TimeEpoch,
1068    {
1069        fn duration_since_unix_epoch(&self) -> Result<Duration, TimeError> {
1070            let relative_offset = Resolution::repr_to_duration(self.0.representation)?;
1071            match relative_offset {
1072                SignedDuration::Positive(offset) => Epoch::epoch_offset()
1073                    .checked_add(offset)
1074                    .ok_or(TimeError::DeltaNotRepresentable),
1075                SignedDuration::Negative(offset) => Epoch::epoch_offset()
1076                    .checked_sub(offset)
1077                    .ok_or(TimeError::DeltaNotRepresentable),
1078            }
1079        }
1080    }
1081
1082    impl<Resolution, Epoch> Debug for LimitedResolutionTimestamp<Resolution, Epoch>
1083    where
1084        Resolution: TimeResolution,
1085        Epoch: TimeEpoch,
1086    {
1087        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1088            write!(f, "LimitedResolutionTimestamp({self})")
1089        }
1090    }
1091
1092    impl<Resolution, Epoch> Display for LimitedResolutionTimestamp<Resolution, Epoch>
1093    where
1094        Resolution: TimeResolution,
1095        Epoch: TimeEpoch,
1096    {
1097        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1098            self.display(f).or_else(|_| Display::fmt(&self.0, f))
1099        }
1100    }
1101
1102    impl<Resolution, Epoch> FromStr for LimitedResolutionTimestamp<Resolution, Epoch>
1103    where
1104        Resolution: TimeResolution,
1105        Epoch: TimeEpoch,
1106    {
1107        type Err = TimeError;
1108
1109        fn from_str(s: &str) -> Result<Self, Self::Err> {
1110            let mut parts = s.split('.');
1111            let seconds = parts.next().ok_or(TimeError::InvalidValue)?;
1112            let seconds = seconds
1113                .parse::<u64>()
1114                .map_err(|_| TimeError::InvalidValue)?;
1115
1116            let duration = if let Some(subseconds_str) = parts.next() {
1117                if subseconds_str.len() > 9 || parts.next().is_some() {
1118                    return Err(TimeError::InvalidValue);
1119                }
1120                let subseconds = subseconds_str
1121                    .parse::<u32>()
1122                    .map_err(|_| TimeError::InvalidValue)?;
1123
1124                let nanos =
1125                    subseconds * 10_u32.pow(u32::try_from(9 - subseconds_str.len()).unwrap());
1126                Duration::new(seconds, nanos)
1127            } else {
1128                Duration::from_secs(seconds)
1129            };
1130
1131            let epoch = Epoch::epoch_offset();
1132            let duration = if duration < epoch {
1133                SignedDuration::Negative(epoch - duration)
1134            } else {
1135                SignedDuration::Positive(duration - epoch)
1136            };
1137            Ok(Self::from(
1138                LimitedResolutionDuration::<Resolution>::try_from(duration)?,
1139            ))
1140        }
1141    }
1142
1143    #[test]
1144    fn timestamp_parse_tests() {
1145        fn test_roundtrip_parsing<Resolution: TimeResolution>() {
1146            let original = LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::now();
1147            let unix_timestamp = original.to_string();
1148            let parsed = unix_timestamp.parse().unwrap();
1149            assert_eq!(
1150                original, parsed,
1151                "{original} produced {unix_timestamp}, but parsed {parsed}"
1152            );
1153        }
1154
1155        test_roundtrip_parsing::<Weeks>();
1156        test_roundtrip_parsing::<Days>();
1157        test_roundtrip_parsing::<Minutes>();
1158        test_roundtrip_parsing::<Seconds>();
1159        test_roundtrip_parsing::<Milliseconds>();
1160        test_roundtrip_parsing::<Microseconds>();
1161        test_roundtrip_parsing::<Nanoseconds>();
1162    }
1163
1164    impl<'k, Resolution, Epoch> Key<'k> for LimitedResolutionTimestamp<Resolution, Epoch>
1165    where
1166        Resolution: TimeResolution,
1167        Epoch: TimeEpoch,
1168    {
1169        const CAN_OWN_BYTES: bool = false;
1170
1171        fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
1172            let duration = LimitedResolutionDuration::<Resolution>::from_ord_bytes(bytes)?;
1173            Ok(Self::from(duration))
1174        }
1175    }
1176
1177    impl<Resolution, Epoch> KeyEncoding<Self> for LimitedResolutionTimestamp<Resolution, Epoch>
1178    where
1179        Resolution: TimeResolution,
1180        Epoch: TimeEpoch,
1181    {
1182        type Error = TimeError;
1183
1184        const LENGTH: Option<usize> = None;
1185
1186        fn describe<Visitor>(visitor: &mut Visitor)
1187        where
1188            Visitor: KeyVisitor,
1189        {
1190            visitor.visit_composite(
1191                CompositeKind::Struct(Cow::Borrowed(
1192                    "bonsaidb::core::key::time::LimitedResolutionTimestamp",
1193                )),
1194                1,
1195            );
1196            visitor.visit_composite_attribute("epoch", Epoch::epoch_offset().as_nanos());
1197            <Resolution::Representation as KeyEncoding>::describe(visitor);
1198        }
1199
1200        fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
1201            self.0.as_ord_bytes()
1202        }
1203    }
1204
1205    impl<Resolution, Epoch> From<LimitedResolutionDuration<Resolution>>
1206        for LimitedResolutionTimestamp<Resolution, Epoch>
1207    where
1208        Resolution: TimeResolution,
1209        Epoch: TimeEpoch,
1210    {
1211        fn from(duration: LimitedResolutionDuration<Resolution>) -> Self {
1212            Self(duration, PhantomData)
1213        }
1214    }
1215
1216    impl<Resolution, Epoch> From<LimitedResolutionTimestamp<Resolution, Epoch>>
1217        for LimitedResolutionDuration<Resolution>
1218    where
1219        Resolution: TimeResolution,
1220        Epoch: TimeEpoch,
1221    {
1222        fn from(time: LimitedResolutionTimestamp<Resolution, Epoch>) -> Self {
1223            time.0
1224        }
1225    }
1226
1227    impl<Resolution, Epoch> TryFrom<SystemTime> for LimitedResolutionTimestamp<Resolution, Epoch>
1228    where
1229        Resolution: TimeResolution,
1230        Epoch: TimeEpoch,
1231    {
1232        type Error = TimeError;
1233
1234        fn try_from(time: SystemTime) -> Result<Self, TimeError> {
1235            let epoch = UNIX_EPOCH
1236                .checked_add(Epoch::epoch_offset())
1237                .ok_or(TimeError::DeltaNotRepresentable)?;
1238            match time.duration_since(epoch) {
1239                Ok(duration) => {
1240                    LimitedResolutionDuration::try_from(SignedDuration::Positive(duration))
1241                        .map(Self::from)
1242                }
1243                Err(_) => match epoch.duration_since(time) {
1244                    Ok(duration) => {
1245                        LimitedResolutionDuration::try_from(SignedDuration::Negative(duration))
1246                            .map(Self::from)
1247                    }
1248                    Err(_) => Err(TimeError::DeltaNotRepresentable),
1249                },
1250            }
1251        }
1252    }
1253
1254    impl<Resolution, Epoch> TryFrom<LimitedResolutionTimestamp<Resolution, Epoch>> for SystemTime
1255    where
1256        Resolution: TimeResolution,
1257        Epoch: TimeEpoch,
1258    {
1259        type Error = TimeError;
1260
1261        fn try_from(
1262            time: LimitedResolutionTimestamp<Resolution, Epoch>,
1263        ) -> Result<Self, TimeError> {
1264            let since_epoch = SignedDuration::try_from(time.0)?;
1265            let epoch = UNIX_EPOCH
1266                .checked_add(Epoch::epoch_offset())
1267                .ok_or(TimeError::DeltaNotRepresentable)?;
1268            let time = match since_epoch {
1269                SignedDuration::Positive(since_epoch) => epoch.checked_add(since_epoch),
1270                SignedDuration::Negative(since_epoch) => epoch.checked_sub(since_epoch),
1271            };
1272
1273            time.ok_or(TimeError::DeltaNotRepresentable)
1274        }
1275    }
1276
1277    /// A [`TimeEpoch`] implementation that allows storing
1278    /// [`LimitedResolutionTimestamp`] relative to the "unix epoch": January 1,
1279    /// 1970 00:00:00 UTC.
1280    pub struct UnixEpoch;
1281
1282    impl TimeEpoch for UnixEpoch {
1283        fn name() -> &'static str {
1284            "Unix"
1285        }
1286
1287        fn epoch_offset() -> Duration {
1288            Duration::ZERO
1289        }
1290    }
1291
1292    /// A [`TimeEpoch`] implementation that allows storing
1293    /// [`LimitedResolutionTimestamp`] relative to the 10-year anniversary of
1294    /// BonsaiDb: March 20, 2031 04:31:47 UTC.
1295    ///
1296    /// ## Why use [`BonsaiEpoch`] instead of [`UnixEpoch`]?
1297    ///
1298    /// [`LimitedResolutionTimestamp`] uses [`ordered-varint::Variable`] to
1299    /// implement [`Key`], which encodes the underlying value in as few bytes as
1300    /// possible while still preserving the ordering required by [`Key`].
1301    ///
1302    /// Many applications are not storing timestamps that predate the
1303    /// application being developed. When there is a likelihood that timestamps
1304    /// are closer to "now" than they are to the unix timestamp (January 1, 1970
1305    /// 00:00:00 UTC), the [`BonsaiEpoch`] will consistently encode the
1306    /// underlying representation in fewer bytes than when using [`UnixEpoch`].
1307    ///
1308    /// We hope BonsaiDb is a viable database option for many years. By setting
1309    /// this epoch 10 years from the start of BonsaiDb, it allows the internal
1310    /// representation of timestamps to slowly decrease in size until the
1311    /// 10-year anniversary. Over the following 10 years, the size will grow
1312    /// back to the same size it was at its conception, and then slowly grow as
1313    /// needed from that point on.
1314    pub struct BonsaiEpoch;
1315
1316    impl BonsaiEpoch {
1317        const EPOCH: Duration = Duration::new(1_931_747_507, 0);
1318    }
1319
1320    impl TimeEpoch for BonsaiEpoch {
1321        fn name() -> &'static str {
1322            "BonsaiDb"
1323        }
1324
1325        fn epoch_offset() -> Duration {
1326            Self::EPOCH
1327        }
1328    }
1329
1330    #[test]
1331    fn limited_resolution_timestamp_tests() {
1332        fn test_resolution<Resolution: TimeResolution>(resolution: Duration) {
1333            let now_in_seconds = LimitedResolutionTimestamp::<Resolution, UnixEpoch>::now();
1334            let as_system = SystemTime::try_from(now_in_seconds).unwrap();
1335            let as_limited =
1336                LimitedResolutionTimestamp::<Resolution, UnixEpoch>::try_from(as_system).unwrap();
1337            assert_eq!(as_limited, now_in_seconds);
1338
1339            let now_in_seconds = LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::now();
1340            let as_system = SystemTime::try_from(now_in_seconds).unwrap();
1341            let as_limited =
1342                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(as_system).unwrap();
1343            assert_eq!(as_limited, now_in_seconds);
1344
1345            let slightly_before_epoch = UNIX_EPOCH + BonsaiEpoch::EPOCH
1346                - Duration::from_nanos(u64::try_from(resolution.as_nanos() / 2).unwrap());
1347            let unix_epoch_in_recent =
1348                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(
1349                    slightly_before_epoch,
1350                )
1351                .unwrap();
1352            let as_system = SystemTime::try_from(unix_epoch_in_recent).unwrap();
1353            let as_limited =
1354                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(as_system).unwrap();
1355            assert!(
1356                slightly_before_epoch
1357                    .duration_since(as_system)
1358                    .expect("timestamp should have been trunctated towards MIN")
1359                    < resolution
1360            );
1361            assert_eq!(as_limited, unix_epoch_in_recent);
1362
1363            let slightly_after_epoch = UNIX_EPOCH
1364                + BonsaiEpoch::EPOCH
1365                + Duration::from_nanos(u64::try_from(resolution.as_nanos() / 2).unwrap());
1366            let unix_epoch_in_recent =
1367                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(
1368                    slightly_after_epoch,
1369                )
1370                .unwrap();
1371            let as_system = SystemTime::try_from(unix_epoch_in_recent).unwrap();
1372            println!("{slightly_after_epoch:?} converted to {unix_epoch_in_recent} and back as {as_system:?}");
1373            let as_limited =
1374                LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::try_from(as_system).unwrap();
1375            assert!(
1376                slightly_after_epoch
1377                    .duration_since(as_system)
1378                    .expect("timestamp should have been truncated towards 0")
1379                    < resolution
1380            );
1381            assert_eq!(as_limited, unix_epoch_in_recent);
1382        }
1383
1384        test_resolution::<Weeks>(Duration::from_secs(7 * 24 * 60 * 60));
1385        test_resolution::<Days>(Duration::from_secs(24 * 60 * 60));
1386        test_resolution::<Hours>(Duration::from_secs(60 * 60));
1387        test_resolution::<Minutes>(Duration::from_secs(60));
1388        test_resolution::<Seconds>(Duration::from_secs(1));
1389        test_resolution::<Milliseconds>(Duration::from_millis(1));
1390        test_resolution::<Microseconds>(Duration::from_micros(1));
1391        test_resolution::<Nanoseconds>(Duration::from_nanos(1));
1392    }
1393
1394    #[test]
1395    fn serialization_tests() {
1396        fn test_serialization<Resolution: TimeResolution>() {
1397            let original = LimitedResolutionTimestamp::<Resolution, BonsaiEpoch>::now();
1398            let serialized = pot::to_vec(&original).unwrap();
1399            let deserialized = pot::from_slice(&serialized).unwrap();
1400            assert_eq!(original, deserialized);
1401        }
1402
1403        test_serialization::<Weeks>();
1404        test_serialization::<Days>();
1405        test_serialization::<Hours>();
1406        test_serialization::<Minutes>();
1407        test_serialization::<Seconds>();
1408        test_serialization::<Milliseconds>();
1409        test_serialization::<Microseconds>();
1410        test_serialization::<Nanoseconds>();
1411    }
1412}
1413
1414/// A signed duration of time represented in weeks (604,800 seconds).
1415/// Internally, the number of weeks is represented as an `i32`,
1416/// allowing a range of +/- ~41.18 million years.
1417pub type Weeks = limited::LimitedResolutionDuration<limited::Weeks>;
1418
1419/// A signed duration of time represented in days (86,400 seconds). Internally,
1420/// the number of days is represented as an `i32`, allowing a range of +/- ~5.88
1421/// million years.
1422pub type Days = limited::LimitedResolutionDuration<limited::Days>;
1423
1424/// A signed duration of time represented in hours (3,600 seconds). Internally,
1425/// the number of hours is represented as an `i32`, allowing a range of +/-
1426/// ~245,147 years.
1427pub type Hours = limited::LimitedResolutionDuration<limited::Hours>;
1428
1429/// A signed duration of time represented in minutes (60 seconds). Internally,
1430/// the number of minutes is represented as an `i32`,
1431/// allowing a range of +/- ~4,086 years.
1432pub type Minutes = limited::LimitedResolutionDuration<limited::Minutes>;
1433
1434/// A signed duration of time represented in seconds (with no partial
1435/// subseconds). Internally, the number of seconds is represented as an `i64`,
1436/// allowing a range of +/- ~21 times the age of the universe.
1437pub type Seconds = limited::LimitedResolutionDuration<limited::Seconds>;
1438
1439/// A signed duration of time represented in milliseconds (1/1,000th of a
1440/// second). Internally, the number of milliseconds is represented as an `i64`,
1441/// allowing a range of +/- ~292.5 million years.
1442pub type Milliseconds = limited::LimitedResolutionDuration<limited::Milliseconds>;
1443
1444/// A signed duration of time represented in microseconds (1/1,000,000th of a
1445/// second). Internally, the number of microseconds is represented as an `i64`,
1446/// allowing a range of +/- ~292,471 years.
1447pub type Microseconds = limited::LimitedResolutionDuration<limited::Microseconds>;
1448
1449/// A signed duration of time represented in nanoseconds (1/1,000,000,000th of a
1450/// second). Internally, the number of microseconds is represented as an `i64`,
1451/// allowing a range of +/- ~292.5 years.
1452pub type Nanoseconds = limited::LimitedResolutionDuration<limited::Nanoseconds>;
1453
1454/// A timestamp stored as the number of weeks (604,800 seconds) relative to
1455/// [`UnixEpoch`]. Internally, the number of weeks is represented as an `i32`,
1456/// allowing a range of +/- ~41.18 million years.
1457pub type WeeksSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Weeks, UnixEpoch>;
1458
1459/// A timestamp stored as the number of days (86,400 seconds) relative to
1460/// [`UnixEpoch`]. Internally, the number of days is represented as an `i32`,
1461/// allowing a range of +/- ~5.88 million years.
1462pub type DaysSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Days, UnixEpoch>;
1463
1464/// A timestamp stored as the number of hours (3,600 seconds) relative to
1465/// [`UnixEpoch`]. Internally, the number of hours is represented as an `i32`,
1466/// allowing a range of +/- ~245,147 years.
1467pub type HoursSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Hours, UnixEpoch>;
1468
1469/// A timestamp stored as the number of minutes (60 seconds) relative to
1470/// [`UnixEpoch`]. Internally, the number of minutes is represented as an `i32`,
1471/// allowing a range of +/- ~4,086 years.
1472pub type MinutesSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Minutes, UnixEpoch>;
1473
1474/// A timestamp stored as the number of seconds (with no partial subseconds)
1475/// relative to [`UnixEpoch`]. Internally, the number of seconds is represented
1476/// as an `i64`, allowing a range of +/- ~21 times the age of the universe.
1477pub type SecondsSinceUnixEpoch = limited::LimitedResolutionTimestamp<limited::Seconds, UnixEpoch>;
1478
1479/// A timestamp stored as the number of milliseconds (1/1,000th of a second)
1480/// relative to [`UnixEpoch`]. Internally, the number of milliseconds is
1481/// represented as an `i64`, allowing a range of +/- ~292.5 million years.
1482pub type MillisecondsSinceUnixEpoch =
1483    limited::LimitedResolutionTimestamp<limited::Milliseconds, UnixEpoch>;
1484
1485/// A timestamp stored as the number of microseconds (1/1,000,000th of a second)
1486/// relative to [`UnixEpoch`]. Internally, the number of microseconds is
1487/// represented as an `i64`, allowing a range of +/- ~292,471 years.
1488pub type MicrosecondsSinceUnixEpoch =
1489    limited::LimitedResolutionTimestamp<limited::Microseconds, UnixEpoch>;
1490
1491/// A timestamp stored as the number of nanoseconds (1/1,000,000,000th of a
1492/// second) relative to [`UnixEpoch`]. Internally, the number of microseconds is
1493/// represented as an `i64`, allowing a range of +/- ~292.5 years.
1494pub type NanosecondsSinceUnixEpoch =
1495    limited::LimitedResolutionTimestamp<limited::Nanoseconds, UnixEpoch>;
1496
1497/// A timestamp stored as the number of weeks (604,800 seconds) relative to
1498/// [`BonsaiEpoch`]. Internally, the number of weeks is represented as an `i32`,
1499/// allowing a range of +/- ~41.18 million years.
1500pub type TimestampAsWeeks = limited::LimitedResolutionTimestamp<limited::Weeks, BonsaiEpoch>;
1501
1502/// A timestamp stored as the number of days (86,400 seconds) relative to
1503/// [`BonsaiEpoch`]. Internally, the number of days is represented as an `i32`,
1504/// allowing a range of +/- ~5.88 million years.
1505pub type TimestampAsDays = limited::LimitedResolutionTimestamp<limited::Days, BonsaiEpoch>;
1506
1507/// A timestamp stored as the number of hours (3,600 seconds) relative to
1508/// [`BonsaiEpoch`]. Internally, the number of hours is represented as an `i32`,
1509/// allowing a range of +/- ~245,147 years.
1510pub type TimestampAsHours = limited::LimitedResolutionTimestamp<limited::Hours, BonsaiEpoch>;
1511
1512/// A timestamp stored as the number of minutes (60 seconds) relative to
1513/// [`BonsaiEpoch`]. Internally, the number of minutes is represented as an `i32`,
1514/// allowing a range of +/- ~4,086 years.
1515pub type TimestampAsMinutes = limited::LimitedResolutionTimestamp<limited::Minutes, BonsaiEpoch>;
1516
1517/// A timestamp stored as the number of seconds (with no partial subseconds)
1518/// relative to [`BonsaiEpoch`]. Internally, the number of seconds is represented
1519/// as an `i64`, allowing a range of +/- ~21 times the age of the universe.
1520pub type TimestampAsSeconds = limited::LimitedResolutionTimestamp<limited::Seconds, BonsaiEpoch>;
1521
1522/// A timestamp stored as the number of milliseconds (1/1,000th of a second)
1523/// relative to [`BonsaiEpoch`]. Internally, the number of milliseconds is
1524/// represented as an `i64`, allowing a range of +/- ~292.5 million years.
1525pub type TimestampAsMilliseconds =
1526    limited::LimitedResolutionTimestamp<limited::Milliseconds, BonsaiEpoch>;
1527
1528/// A timestamp stored as the number of microseconds (1/1,000,000th of a second)
1529/// relative to [`BonsaiEpoch`]. Internally, the number of microseconds is
1530/// represented as an `i64`, allowing a range of +/- ~292,471 years.
1531pub type TimestampAsMicroseconds =
1532    limited::LimitedResolutionTimestamp<limited::Microseconds, BonsaiEpoch>;
1533
1534/// A timestamp stored as the number of nanoseconds (1/1,000,000,000th of a
1535/// second) relative to [`BonsaiEpoch`]. Internally, the number of microseconds is
1536/// represented as an `i64`, allowing a range of +/- ~292.5 years.
1537pub type TimestampAsNanoseconds =
1538    limited::LimitedResolutionTimestamp<limited::Nanoseconds, BonsaiEpoch>;