unix_time/lib.rs
1// Copyright 2020 Quentin De Coninck
2//
3// Distributed under MIT license.
4//! A minimal crate to play with Instant based on UNIX epoch.
5//!
6//! The standard library provides Instant and Duration structures to measure
7//! elapsed time. This is fine for most use cases, but the Instant structure
8//! voluntary hides its implementation to keep its semantics. This crate exposes
9//! its time base to the UNIX Epoch (1st January 1970 at 0:00).
10//!
11//! The exposed API tries to mimic as much as possible the `std::time` one for
12//! the related `Instant` structures, such that passing from these to the ones
13//! of this crate would be as seamless as possible (as it actually uses
14//! `std::time` under the hood).
15//!
16//! This crate should only be used to compute local time. It is thus not
17//! appropriate for timezone computations, playing with dates,...
18
19use std::time::SystemTime;
20use std::time::Duration;
21use std::ops::{Add, AddAssign, Sub, SubAssign};
22use std::fmt;
23
24#[cfg(feature = "serde")]
25use serde::{Deserialize, Serialize};
26
27#[cfg(feature = "rkyv")]
28use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
29
30/// An precise instant relative to the UNIX epoch, with nanosecond precision.
31#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[cfg_attr(feature = "rkyv", derive(Archive, RkyvDeserialize, RkyvSerialize))]
34pub struct Instant {
35 secs: u64,
36 nanos: u32,
37}
38
39impl Instant {
40 /// Creates an `Instant` at the specified seconds and nanoseconds after the
41 /// UNIX epoch.
42 ///
43 /// # Examples
44 ///
45 /// ```
46 /// use unix_time::Instant;
47 ///
48 /// let instant = Instant::at(42, 182870024);
49 /// assert_eq!(format!("{:?}", instant), "Instant { secs: 42, nanos: 182870024 }");
50 /// ```
51 pub fn at(secs: u64, nanos: u32) -> Self {
52 Self { secs, nanos }
53 }
54
55 /// Returns an instant corresponding to "now".
56 pub fn now() -> Self {
57 let since_epoch = SystemTime::now()
58 .duration_since(SystemTime::UNIX_EPOCH)
59 .expect("SystemTime before UNIX epoch?!?");
60 Self::at(since_epoch.as_secs(), since_epoch.subsec_nanos())
61 }
62
63 /// Returns the number of _whole_ seconds that spaces `self` from the UNIX
64 /// epoch.
65 ///
66 /// The returned value does not include the fractional (nanosecond) part of
67 /// the duration, which can be obtained using [`subsec_nanos`].
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// use unix_time::Instant;
73 ///
74 /// let duration = Instant::at(5, 730023852);
75 /// assert_eq!(duration.secs(), 5);
76 /// ```
77 ///
78 /// To determine the total number of seconds represented by the `Duration`,
79 /// use `secs` in combination with [`subsec_nanos`]:
80 ///
81 /// ```
82 /// use unix_time::Instant;
83 ///
84 /// let instant = Instant::at(5, 730023852);
85 ///
86 /// assert_eq!(5.730023852,
87 /// instant.secs() as f64
88 /// + instant.subsec_nanos() as f64 * 1e-9);
89 /// ```
90 ///
91 /// [`subsec_nanos`]: Instant::subsec_nanos
92 pub fn secs(&self) -> u64 {
93 self.secs
94 }
95
96 /// Returns the fractional part that spaces `self` from the UNIX epoch in
97 /// nanoseconds.
98 ///
99 /// This method does _not_ return the total duration since the UNIX epoch in
100 /// nanoseconds. The returned number always represents a fractional portion
101 /// of a second (i.e., it is less than one billion).
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// use unix_time::Instant;
107 ///
108 /// let instant = Instant::at(5, 10_000_000);
109 /// assert_eq!(instant.secs(), 5);
110 /// assert_eq!(instant.subsec_nanos(), 10_000_000);
111 /// ```
112 pub fn subsec_nanos(&self) -> u32 {
113 self.nanos
114 }
115
116 /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be
117 /// represented as `Instant` (which means it's inside the bounds of the
118 /// underlying data structure), `None` otherwise.
119 pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
120 let d: Duration = (*self).into();
121 d.checked_add(duration).map(|x| x.into())
122 }
123
124 /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be
125 /// represented as `Instant` (which means it's inside the bounds of the
126 /// underlying data structure), `None` otherwise.
127 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
128 let d: Duration = (*self).into();
129 d.checked_sub(duration).map(|x| x.into())
130 }
131
132 /// Returns the amount of time elapsed from another instant to this one,
133 /// or None if that instant is later than this one.
134 ///
135 /// # Examples
136 ///
137 /// ```no_run
138 /// use unix_time::Instant;
139 /// use std::time::Duration;
140 /// use std::thread::sleep;
141 ///
142 /// let now = Instant::now();
143 /// sleep(Duration::new(1, 0));
144 /// let new_now = Instant::now();
145 /// println!("{:?}", new_now.checked_duration_since(now));
146 /// println!("{:?}", now.checked_duration_since(new_now)); // None
147 /// ```
148 pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
149 let d: Duration = (*self).into();
150 d.checked_sub(earlier.into())
151 }
152
153 /// Returns the amount of time elapsed from another instant to this one.
154 ///
155 /// # Panics
156 ///
157 /// This function will panic if `earlier` is later than `self`.
158 ///
159 /// # Examples
160 ///
161 /// ```no_run
162 /// use std::time::Duration;
163 /// use unix_time::Instant;
164 /// use std::thread::sleep;
165 ///
166 /// let now = Instant::now();
167 /// sleep(Duration::new(1, 0).into());
168 /// let new_now = Instant::now();
169 /// println!("{:?}", new_now.duration_since(now));
170 /// ```
171 pub fn duration_since(&self, earlier: Instant) -> Duration {
172 self.checked_duration_since(earlier).expect("supplied instant is later than self")
173 }
174
175 /// Returns the amount of time elapsed from another instant to this one,
176 /// or zero duration if that instant is later than this one.
177 ///
178 /// # Examples
179 ///
180 /// ```no_run
181 /// use std::time::Duration;
182 /// use unix_time::Instant;
183 /// use std::thread::sleep;
184 ///
185 /// let now = Instant::now();
186 /// sleep(Duration::new(1, 0));
187 /// let new_now = Instant::now();
188 /// println!("{:?}", new_now.saturating_duration_since(now));
189 /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns
190 /// ```
191 pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
192 self.checked_duration_since(earlier).unwrap_or(Duration::new(0, 0))
193 }
194
195 /// Returns the amount of time elapsed since this instant was created.
196 ///
197 /// # Panics
198 ///
199 /// This function may panic if the current time is earlier than this
200 /// instant, which is something that can happen if an `Instant` is
201 /// produced synthetically.
202 ///
203 /// # Examples
204 ///
205 /// ```no_run
206 /// use std::thread::sleep;
207 /// use std::time::Duration;
208 /// use unix_time::Instant;
209 ///
210 /// let instant = Instant::now();
211 /// let three_secs = Duration::from_secs(3);
212 /// sleep(three_secs);
213 /// assert!(instant.elapsed() >= three_secs);
214 /// ```
215 pub fn elapsed(&self) -> Duration {
216 Instant::now() - *self
217 }
218}
219
220impl From<Instant> for Duration {
221 fn from(i: Instant) -> Duration {
222 Duration::new(i.secs, i.nanos)
223 }
224}
225
226impl From<Duration> for Instant {
227 fn from(d: Duration) -> Instant {
228 Instant::at(d.as_secs(), d.subsec_nanos())
229 }
230}
231
232impl Add<Duration> for Instant {
233 type Output = Instant;
234
235 /// # Panics
236 ///
237 /// This function may panic if the resulting point in time cannot be represented by the
238 /// underlying data structure. See [`Instant::checked_add`] for a version without panic.
239 fn add(self, other: Duration) -> Instant {
240 self.checked_add(other).expect("overflow when adding duration to instant")
241 }
242}
243
244impl AddAssign<Duration> for Instant {
245 fn add_assign(&mut self, other: Duration) {
246 *self = *self + other;
247 }
248}
249
250impl Sub<Duration> for Instant {
251 type Output = Instant;
252
253 fn sub(self, other: Duration) -> Instant {
254 self.checked_sub(other).expect("overflow when subtracting duration from instant")
255 }
256}
257
258impl SubAssign<Duration> for Instant {
259 fn sub_assign(&mut self, other: Duration) {
260 *self = *self - other;
261 }
262}
263
264impl Sub<Instant> for Instant {
265 type Output = Duration;
266
267 fn sub(self, other: Instant) -> Duration {
268 self.duration_since(other)
269 }
270}
271
272impl fmt::Debug for Instant {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 write!(f, "Instant {{ secs: {}, nanos: {} }}", self.secs, self.nanos)
275 }
276}