transforms/time/timestamp/
mod.rs1use core::{
2 cmp::Ordering,
3 ops::{Add, Sub},
4 time::Duration,
5};
6
7use crate::time::{TimeError, TimePoint};
8
9#[cfg(feature = "std")]
10use std::time::{SystemTime, UNIX_EPOCH};
11
12pub mod error;
13#[allow(deprecated)]
14pub use error::TimestampError;
15
16#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
23pub struct Timestamp {
24 pub t: u128,
25}
26
27impl Timestamp {
28 #[cfg(feature = "std")]
29 #[must_use = "this returns the result of the operation"]
30 pub fn now() -> Self {
46 let duration_since_epoch = SystemTime::now()
47 .duration_since(UNIX_EPOCH)
48 .expect("Time went backwards");
49
50 Timestamp {
51 t: duration_since_epoch.as_nanos(),
52 }
53 }
54
55 #[must_use = "this returns the result of the operation"]
56 pub fn zero() -> Self {
68 Timestamp { t: 0 }
69 }
70
71 pub fn as_seconds(&self) -> Result<f64, TimeError> {
96 const NANOSECONDS_PER_SECOND: f64 = 1_000_000_000.0;
97 #[allow(clippy::cast_precision_loss)]
98 let seconds = self.t as f64 / NANOSECONDS_PER_SECOND;
99
100 #[allow(clippy::cast_possible_truncation)]
101 #[allow(clippy::cast_sign_loss)]
102 if (seconds * NANOSECONDS_PER_SECOND) as u128 == self.t {
103 Ok(seconds)
104 } else {
105 Err(TimeError::AccuracyLoss)
106 }
107 }
108
109 #[must_use = "this returns the result of the operation"]
110 #[allow(clippy::cast_precision_loss)]
124 pub fn as_seconds_unchecked(&self) -> f64 {
125 const NANOSECONDS_PER_SECOND: f64 = 1_000_000_000.0;
126 self.t as f64 / NANOSECONDS_PER_SECOND
127 }
128}
129
130impl Sub<Timestamp> for Timestamp {
131 type Output = Result<Duration, TimeError>;
132
133 fn sub(
134 self,
135 other: Timestamp,
136 ) -> Self::Output {
137 match self.t.cmp(&other.t) {
138 Ordering::Less => Err(TimeError::DurationUnderflow),
139 Ordering::Equal => Ok(Duration::from_secs(0)),
140 Ordering::Greater => {
141 let diff = self.t - other.t;
142 let seconds = diff / 1_000_000_000;
143 let nanos = (diff % 1_000_000_000) as u32;
144
145 if seconds > u128::from(u64::MAX) {
146 return Err(TimeError::DurationOverflow);
147 }
148
149 #[allow(clippy::cast_possible_truncation)]
150 Ok(Duration::new(seconds as u64, nanos))
151 }
152 }
153 }
154}
155
156impl Add<Duration> for Timestamp {
157 type Output = Result<Timestamp, TimeError>;
158
159 fn add(
160 self,
161 rhs: Duration,
162 ) -> Self::Output {
163 (u128::from(rhs.as_secs()))
164 .checked_mul(1_000_000_000)
165 .and_then(|seconds| seconds.checked_add(u128::from(rhs.subsec_nanos())))
166 .and_then(|total_duration_nanos| self.t.checked_add(total_duration_nanos))
167 .map(|final_nanos| Timestamp { t: final_nanos })
168 .ok_or(TimeError::DurationOverflow)
169 }
170}
171
172impl Sub<Duration> for Timestamp {
173 type Output = Result<Timestamp, TimeError>;
174
175 fn sub(
176 self,
177 rhs: Duration,
178 ) -> Self::Output {
179 u128::from(rhs.as_secs())
180 .checked_mul(1_000_000_000)
181 .and_then(|seconds| seconds.checked_add(u128::from(rhs.subsec_nanos())))
182 .and_then(|total_duration_nanos| self.t.checked_sub(total_duration_nanos))
183 .map(|final_nanos| Timestamp { t: final_nanos })
184 .ok_or(TimeError::DurationUnderflow)
185 }
186}
187
188impl TimePoint for Timestamp {
189 fn static_timestamp() -> Self {
190 Timestamp::zero()
191 }
192
193 fn duration_since(
194 self,
195 earlier: Self,
196 ) -> Result<Duration, TimeError> {
197 self - earlier
198 }
199
200 fn checked_add(
201 self,
202 rhs: Duration,
203 ) -> Result<Self, TimeError> {
204 self + rhs
205 }
206
207 fn checked_sub(
208 self,
209 rhs: Duration,
210 ) -> Result<Self, TimeError> {
211 self - rhs
212 }
213
214 fn as_seconds(self) -> Result<f64, TimeError> {
215 Timestamp::as_seconds(&self)
216 }
217}
218
219#[cfg(test)]
220mod tests;