rust_automata/
timestamp.rs

1//! Provide timestamp and timestamp delta types.
2//!
3//! Useful for internal representation of time, and exposes methods for conversion to and from `DateTime`.
4use chrono::{DateTime, Local, TimeDelta, TimeZone, Utc};
5use core::fmt;
6use std::fmt::Display;
7use std::num::ParseIntError;
8use std::ops::{Add, Sub};
9use std::str::FromStr;
10
11/// A timestamp in nanoseconds in the UTC timezone.
12///
13/// Use this type for internal timestamps and for nice date formatting
14/// use [`DateTime<Local>`].
15///
16/// The dates that can be represented as nanoseconds are between
17/// 1677-09-21T00:12:43.145224192 and 2262-04-11T23:47:16.854775807.
18#[derive(
19    Debug,
20    Clone,
21    Copy,
22    PartialEq,
23    Eq,
24    PartialOrd,
25    Ord,
26    Default,
27    serde::Serialize,
28    serde::Deserialize,
29)]
30pub struct Timestamp(i64);
31
32/// A timestamp delta (duration) in nanoseconds.
33///
34/// Any time you subtract two timestamps, you get a `TimestampDelta`.
35#[derive(
36    Debug,
37    Clone,
38    Copy,
39    PartialEq,
40    Eq,
41    PartialOrd,
42    Ord,
43    Default,
44    serde::Serialize,
45    serde::Deserialize,
46)]
47pub struct TimestampDelta(i64);
48
49impl Timestamp {
50    pub const fn zero() -> Self {
51        Self(0)
52    }
53    pub const fn as_secs(&self) -> i64 {
54        self.0 / 1_000_000_000
55    }
56    pub const fn as_millis(&self) -> i64 {
57        self.0 / 1_000_000
58    }
59    pub const fn as_micros(&self) -> i64 {
60        self.0 / 1_000
61    }
62    pub const fn as_nanos(&self) -> i64 {
63        self.0
64    }
65    pub fn local(&self) -> DateTime<Local> {
66        DateTime::<Local>::from(*self)
67    }
68    pub fn utc(&self) -> DateTime<Utc> {
69        DateTime::<Utc>::from(*self)
70    }
71
72    // TODO: bounds check
73    pub const fn from_hours(hours: i64) -> Self {
74        Self(hours * 60 * 60 * 1_000_000_000)
75    }
76    pub const fn from_minutes(minutes: i64) -> Self {
77        Self(minutes * 60 * 1_000_000_000)
78    }
79    pub const fn from_secs(secs: i64) -> Self {
80        Self(secs * 1_000_000_000)
81    }
82    pub const fn from_millis(millis: i64) -> Self {
83        Self(millis * 1_000_000)
84    }
85    pub const fn from_micros(micros: i64) -> Self {
86        Self(micros * 1_000)
87    }
88    pub const fn from_nanos(nanos: i64) -> Self {
89        Self(nanos)
90    }
91}
92
93impl TimestampDelta {
94    pub const fn zero() -> Self {
95        Self(0)
96    }
97    pub const fn as_secs(&self) -> i64 {
98        self.0 / 1_000_000_000
99    }
100    pub const fn as_millis(&self) -> i64 {
101        self.0 / 1_000_000
102    }
103    pub const fn as_micros(&self) -> i64 {
104        self.0 / 1_000
105    }
106    pub const fn as_nanos(&self) -> i64 {
107        self.0
108    }
109
110    // TODO: bounds check
111    pub const fn from_hours(hours: i64) -> Self {
112        Self(hours * 60 * 60 * 1_000_000_000)
113    }
114    pub const fn from_minutes(minutes: i64) -> Self {
115        Self(minutes * 60 * 1_000_000_000)
116    }
117    pub const fn from_secs(secs: i64) -> Self {
118        Self(secs * 1_000_000_000)
119    }
120    pub const fn from_millis(millis: i64) -> Self {
121        Self(millis * 1_000_000)
122    }
123    pub const fn from_micros(micros: i64) -> Self {
124        Self(micros * 1_000)
125    }
126    pub const fn from_nanos(nanos: i64) -> Self {
127        Self(nanos)
128    }
129}
130
131impl Display for Timestamp {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        write!(f, "{}", self.0)
134    }
135}
136
137impl From<i64> for Timestamp {
138    fn from(nanos: i64) -> Self {
139        Self(nanos)
140    }
141}
142
143impl Add<TimeDelta> for Timestamp {
144    type Output = Timestamp;
145
146    fn add(self, rhs: TimeDelta) -> Self::Output {
147        Timestamp::from(self.0 + rhs.num_nanoseconds().unwrap())
148    }
149}
150impl Add<Timestamp> for Timestamp {
151    type Output = Timestamp;
152
153    fn add(self, rhs: Timestamp) -> Self::Output {
154        Timestamp::from(self.0 + rhs.0)
155    }
156}
157impl Add<TimestampDelta> for Timestamp {
158    type Output = Timestamp;
159
160    fn add(self, rhs: TimestampDelta) -> Self::Output {
161        Timestamp::from(self.0 + rhs.0)
162    }
163}
164
165impl Sub<TimeDelta> for Timestamp {
166    type Output = TimestampDelta;
167
168    fn sub(self, rhs: TimeDelta) -> Self::Output {
169        TimestampDelta::from(self.0 - rhs.num_nanoseconds().unwrap())
170    }
171}
172impl Sub<Timestamp> for Timestamp {
173    type Output = TimestampDelta;
174
175    fn sub(self, rhs: Timestamp) -> Self::Output {
176        TimestampDelta::from(self.0 - rhs.0)
177    }
178}
179
180impl FromStr for Timestamp {
181    type Err = ParseIntError;
182
183    fn from_str(s: &str) -> Result<Self, Self::Err> {
184        let nanos = i64::from_str(s)?;
185        Ok(Timestamp::from(nanos))
186    }
187}
188
189impl From<DateTime<Utc>> for Timestamp {
190    fn from(dt: DateTime<Utc>) -> Self {
191        Self(dt.timestamp_nanos_opt().unwrap())
192    }
193}
194
195impl From<DateTime<Local>> for Timestamp {
196    fn from(dt: DateTime<Local>) -> Self {
197        Self(dt.with_timezone(&Utc).timestamp_nanos_opt().unwrap())
198    }
199}
200
201impl From<Timestamp> for DateTime<Utc> {
202    fn from(ts: Timestamp) -> Self {
203        Utc.timestamp_nanos(ts.0)
204    }
205}
206
207impl From<Timestamp> for DateTime<Local> {
208    fn from(ts: Timestamp) -> Self {
209        let utc: DateTime<Utc> = ts.into();
210        utc.with_timezone(&Local)
211    }
212}
213
214impl From<Timestamp> for TimeDelta {
215    fn from(ts: Timestamp) -> Self {
216        TimeDelta::nanoseconds(ts.0)
217    }
218}
219
220impl Display for TimestampDelta {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        write!(f, "{}", self.0)
223    }
224}
225
226impl From<i64> for TimestampDelta {
227    fn from(nanos: i64) -> Self {
228        Self(nanos)
229    }
230}
231
232impl Add<TimeDelta> for TimestampDelta {
233    type Output = TimestampDelta;
234
235    fn add(self, rhs: TimeDelta) -> Self::Output {
236        TimestampDelta::from(self.0 + rhs.num_nanoseconds().unwrap())
237    }
238}
239impl Add<TimestampDelta> for TimestampDelta {
240    type Output = TimestampDelta;
241
242    fn add(self, rhs: TimestampDelta) -> Self::Output {
243        TimestampDelta::from(self.0 + rhs.0)
244    }
245}
246
247impl Sub<TimeDelta> for TimestampDelta {
248    type Output = TimestampDelta;
249
250    fn sub(self, rhs: TimeDelta) -> Self::Output {
251        TimestampDelta::from(self.0 - rhs.num_nanoseconds().unwrap())
252    }
253}
254impl Sub<TimestampDelta> for TimestampDelta {
255    type Output = TimestampDelta;
256
257    fn sub(self, rhs: TimestampDelta) -> Self::Output {
258        TimestampDelta::from(self.0 - rhs.0)
259    }
260}
261
262impl From<TimeDelta> for TimestampDelta {
263    fn from(delta: TimeDelta) -> Self {
264        TimestampDelta::from(delta.num_nanoseconds().unwrap())
265    }
266}
267
268impl From<TimestampDelta> for TimeDelta {
269    fn from(delta: TimestampDelta) -> Self {
270        TimeDelta::nanoseconds(delta.0)
271    }
272}