kine_core/
time.rs

1use core::{
2    fmt::{self, Debug},
3    ops::{Add, AddAssign, Sub, SubAssign},
4};
5
6use crate::{Calendar, CalendarTime, Duration, TimeResult, WrittenTime};
7
8const NANOS_IN_SECS: i128 = 1_000_000_000;
9
10/// One instant in real-life
11///
12/// It can hold any time between roughly 10^22 years before and after posix epoch.
13#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
14pub struct Time {
15    /// Offset with the POSIX epoch
16    ///
17    /// This is basically TAI - 10.
18    nanos: i128,
19}
20
21impl Time {
22    /// The posix epoch (1970-01-01T00:00:00 Gregorian UTC)
23    pub const POSIX_EPOCH: Time = Time { nanos: 0 };
24
25    pub(crate) const fn from_tai_nanos(nanos: i128) -> Option<Time> {
26        Time::from_nanos_since_posix_epoch(nanos).checked_sub(&Duration::from_secs(10))
27    }
28
29    pub(crate) const fn as_tai_nanos(&self) -> Option<i128> {
30        self.nanos.checked_add(10 * NANOS_IN_SECS)
31    }
32
33    pub(crate) const fn from_nanos_since_posix_epoch(nanos: i128) -> Time {
34        Time { nanos }
35    }
36
37    /// Return the current time as indicated by the system timezone
38    ///
39    /// Note that this will panic in no-std environments if an alternative way of getting
40    /// the time is not known for the platform.
41    pub fn try_now() -> crate::Result<TimeResult> {
42        // TODO: Introduce hacks to really always be monotonic, eg. based on std::Instant
43        // after the first time a duration was acquired?
44        crate::tz::System::now().read()
45    }
46
47    /// Return the current time
48    ///
49    /// If the system clock (as returned by Rust's `SystemTime::now()`) is in a non-UTC
50    /// timezone mode, then this will return any one of the possible `Time`s corresponding
51    /// to the time returned by the system clock. This should not happen anyway as Rust's
52    /// `SystemTime` should return POSIX timestamps (so, UTC) even when the system clock
53    /// is set to local time, so there should be no issue in relying on this function.
54    ///
55    /// Note that this will panic in no-std environments if an alternative way of getting
56    /// the time is not known for the platform.
57    pub fn now() -> Time {
58        Self::try_now()
59            .expect("System time out of range")
60            .any_approximate()
61    }
62
63    /// Read the current time from a given written time
64    pub fn read<Tim: CalendarTime>(t: Tim) -> crate::Result<TimeResult> {
65        t.read()
66    }
67
68    /// Write the current time in a given calendar
69    pub fn write<Cal: Calendar>(&self, cal: Cal) -> crate::Result<WrittenTime<Cal>> {
70        cal.write(self)
71    }
72
73    /// Offset by a duration, returning `None` on (however unlikely) overflow
74    pub const fn checked_add(&self, rhs: &Duration) -> Option<Time> {
75        // TODO: replace with .map() once const_option_ext is stable
76        match self.nanos.checked_add(rhs.nanos()) {
77            Some(n) => Some(Time::from_nanos_since_posix_epoch(n)),
78            None => None,
79        }
80    }
81
82    /// Offset by a duration, returning `None` on (however unlikely) overflow
83    pub const fn checked_sub(&self, rhs: &Duration) -> Option<Time> {
84        // TODO: replace with .map() once const_option_ext is stable
85        match self.nanos.checked_sub(rhs.nanos()) {
86            Some(n) => Some(Time::from_nanos_since_posix_epoch(n)),
87            None => None,
88        }
89    }
90
91    /// Return the duration elapsed since the other point in time
92    pub fn duration_since(&self, rhs: &Time) -> Duration {
93        self.checked_duration_since(rhs)
94            .expect("overflow computing duration since another time")
95    }
96
97    /// Return the duration elapsed since the other point in time
98    ///
99    /// Returns `None` on the (however unlikely) overflow
100    pub fn checked_duration_since(&self, rhs: &Time) -> Option<Duration> {
101        self.nanos.checked_sub(rhs.nanos).map(Duration::from_nanos)
102    }
103}
104
105impl Add<Duration> for Time {
106    type Output = Time;
107
108    fn add(self, rhs: Duration) -> Self::Output {
109        self.checked_add(&rhs)
110            .expect("overflow adding duration to a time")
111    }
112}
113
114impl AddAssign<Duration> for Time {
115    fn add_assign(&mut self, rhs: Duration) {
116        *self = *self + rhs;
117    }
118}
119
120impl Sub<Duration> for Time {
121    type Output = Time;
122
123    fn sub(self, rhs: Duration) -> Self::Output {
124        self.checked_sub(&rhs)
125            .expect("overflow subtracting duration from a time")
126    }
127}
128
129impl SubAssign<Duration> for Time {
130    fn sub_assign(&mut self, rhs: Duration) {
131        *self = *self - rhs;
132    }
133}
134
135impl Sub<Time> for Time {
136    type Output = Duration;
137
138    fn sub(self, rhs: Time) -> Self::Output {
139        self.duration_since(&rhs)
140    }
141}
142
143// TODO: also introduce all the & variants
144
145impl Debug for Time {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(
148            f,
149            "{}.{}",
150            self.nanos / NANOS_IN_SECS,
151            (self.nanos % NANOS_IN_SECS).abs(),
152        )
153    }
154}