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}