Skip to main content

broker_ntp/
unix_time.rs

1use protocol;
2use std::{self, time};
3
4/// The number of seconds from 1st January 1900 UTC to the start of the Unix epoch.
5pub const EPOCH_DELTA: i64 = 2_208_988_800;
6
7// The NTP fractional scale.
8const NTP_SCALE: f64 = std::u32::MAX as f64;
9
10/// Describes an instant relative to the `UNIX_EPOCH` - 00:00:00 Coordinated Universal Time (UTC),
11/// Thursay, 1 January 1970 in seconds with the fractional part in nanoseconds.
12///
13/// If the **Instant** describes some moment prior to `UNIX_EPOCH`, both the `secs` and
14/// `subsec_nanos` components will be negative.
15///
16/// The sole purpose of this type is for retrieving the "current" time using the `std::time` module
17/// and for converting between the ntp timestamp formats. If you are interested in converting from
18/// unix time to some other more human readable format, perhaps see the [chrono
19/// crate](https://crates.io/crates/chrono).
20///
21/// ## Example
22///
23/// Here is a demonstration of displaying the **Instant** in local time using the chrono crate:
24///
25/// ```
26/// extern crate chrono;
27/// extern crate broker_ntp;
28///
29/// use chrono::TimeZone;
30///
31/// fn main() {
32///     let unix_time = broker_ntp::unix_time::Instant::now();
33///     let local_time = chrono::Local.timestamp(unix_time.secs(), unix_time.subsec_nanos() as _);
34///     println!("{}", local_time);
35/// }
36/// ```
37#[derive(Copy, Clone, Debug)]
38pub struct Instant {
39    secs: i64,
40    subsec_nanos: i32,
41}
42
43impl Instant {
44    /// Create a new **Instant** given its `secs` and `subsec_nanos` components.
45    ///
46    /// To indicate a time following `UNIX_EPOCH`, both `secs` and `subsec_nanos` must be positive.
47    /// To indicate a time prior to `UNIX_EPOCH`, both `secs` and `subsec_nanos` must be negative.
48    /// Violating these invariants will result in a **panic!**.
49    pub fn new(secs: i64, subsec_nanos: i32) -> Instant {
50        if secs > 0 && subsec_nanos < 0 {
51            panic!("invalid instant: secs was positive but subsec_nanos was negative");
52        }
53        if secs < 0 && subsec_nanos > 0 {
54            panic!("invalid instant: secs was negative but subsec_nanos was positive");
55        }
56        Instant { secs, subsec_nanos }
57    }
58
59    /// Uses `std::time::SystemTime::now` and `std::time::UNIX_EPOCH` to determine the current
60    /// **Instant**.
61    ///
62    /// ## Example
63    ///
64    /// ```
65    /// extern crate broker_ntp;
66    ///
67    /// fn main() {
68    ///     println!("{:?}", broker_ntp::unix_time::Instant::now());
69    /// }
70    /// ```
71    pub fn now() -> Self {
72        match time::SystemTime::now().duration_since(time::UNIX_EPOCH) {
73            Ok(duration) => {
74                let secs = duration.as_secs() as i64;
75                let subsec_nanos = duration.subsec_nanos() as i32;
76                Instant::new(secs, subsec_nanos)
77            }
78            Err(sys_time_err) => {
79                let duration_pre_unix_epoch = sys_time_err.duration();
80                let secs = -(duration_pre_unix_epoch.as_secs() as i64);
81                let subsec_nanos = -(duration_pre_unix_epoch.subsec_nanos() as i32);
82                Instant::new(secs, subsec_nanos)
83            }
84        }
85    }
86
87    /// The "seconds" component of the **Instant**.
88    pub fn secs(&self) -> i64 {
89        self.secs
90    }
91
92    /// The fractional component of the **Instant** in nanoseconds.
93    pub fn subsec_nanos(&self) -> i32 {
94        self.subsec_nanos
95    }
96}
97
98// Conversion implementations.
99
100impl From<protocol::ShortFormat> for Instant {
101    fn from(t: protocol::ShortFormat) -> Self {
102        let secs = t.seconds as i64 - EPOCH_DELTA;
103        let subsec_nanos = (t.fraction as f64 / NTP_SCALE * 1e9) as i32;
104        Instant::new(secs, subsec_nanos)
105    }
106}
107
108impl From<protocol::TimestampFormat> for Instant {
109    fn from(t: protocol::TimestampFormat) -> Self {
110        let secs = t.seconds as i64 - EPOCH_DELTA;
111        let subsec_nanos = (t.fraction as f64 / NTP_SCALE * 1e9) as i32;
112        Instant::new(secs, subsec_nanos)
113    }
114}
115
116impl From<Instant> for protocol::ShortFormat {
117    fn from(t: Instant) -> Self {
118        let sec = t.secs() + EPOCH_DELTA;
119        let frac = t.subsec_nanos() as f64 * NTP_SCALE / 1e10;
120        protocol::ShortFormat {
121            seconds: sec as u16,
122            fraction: frac as u16,
123        }
124    }
125}
126
127impl From<Instant> for protocol::TimestampFormat {
128    fn from(t: Instant) -> Self {
129        let sec = t.secs() + EPOCH_DELTA;
130        let frac = t.subsec_nanos() as f64 * NTP_SCALE / 1e10;
131        protocol::TimestampFormat {
132            seconds: sec as u32,
133            fraction: frac as u32,
134        }
135    }
136}