dbs_utils/
time.rs

1// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::fmt;
5
6/// Constant to convert seconds to nanoseconds.
7pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
8/// Constant to convert milliseconds to nanoseconds.
9pub const NANOS_PER_MILLISECOND: u64 = 1_000_000;
10
11/// Wrapper over `libc::clockid_t` to specify Linux Kernel clock source.
12pub enum ClockType {
13    /// Equivalent to `libc::CLOCK_MONOTONIC`.
14    Monotonic,
15    /// Equivalent to `libc::CLOCK_REALTIME`.
16    Real,
17    /// Equivalent to `libc::CLOCK_PROCESS_CPUTIME_ID`.
18    ProcessCpu,
19    /// Equivalent to `libc::CLOCK_THREAD_CPUTIME_ID`.
20    ThreadCpu,
21}
22
23impl From<ClockType> for libc::clockid_t {
24    fn from(clock_type: ClockType) -> Self {
25        match clock_type {
26            ClockType::Monotonic => libc::CLOCK_MONOTONIC,
27            ClockType::Real => libc::CLOCK_REALTIME,
28            ClockType::ProcessCpu => libc::CLOCK_PROCESS_CPUTIME_ID,
29            ClockType::ThreadCpu => libc::CLOCK_THREAD_CPUTIME_ID,
30        }
31    }
32}
33
34/// Structure representing the date in local time with nanosecond precision.
35pub struct LocalTime {
36    /// Seconds in current minute.
37    sec: i32,
38    /// Minutes in current hour.
39    min: i32,
40    /// Hours in current day, 24H format.
41    hour: i32,
42    /// Days in current month.
43    mday: i32,
44    /// Months in current year.
45    mon: i32,
46    /// Years passed since 1900 BC.
47    year: i32,
48    /// Nanoseconds in current second.
49    nsec: i64,
50}
51
52impl LocalTime {
53    /// Returns the [LocalTime](struct.LocalTime.html) structure for the calling moment.
54    pub fn now() -> LocalTime {
55        let mut timespec = libc::timespec {
56            tv_sec: 0,
57            tv_nsec: 0,
58        };
59        let mut tm: libc::tm = libc::tm {
60            tm_sec: 0,
61            tm_min: 0,
62            tm_hour: 0,
63            tm_mday: 0,
64            tm_mon: 0,
65            tm_year: 0,
66            tm_wday: 0,
67            tm_yday: 0,
68            tm_isdst: 0,
69            tm_gmtoff: 0,
70            tm_zone: std::ptr::null(),
71        };
72
73        // Safe because the parameters are valid.
74        unsafe {
75            libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
76            libc::localtime_r(&timespec.tv_sec, &mut tm);
77        }
78
79        LocalTime {
80            sec: tm.tm_sec,
81            min: tm.tm_min,
82            hour: tm.tm_hour,
83            mday: tm.tm_mday,
84            mon: tm.tm_mon,
85            year: tm.tm_year,
86            nsec: timespec.tv_nsec,
87        }
88    }
89}
90
91impl fmt::Display for LocalTime {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        write!(
94            f,
95            "{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}",
96            self.year + 1900,
97            self.mon + 1,
98            self.mday,
99            self.hour,
100            self.min,
101            self.sec,
102            self.nsec
103        )
104    }
105}
106
107/// Holds a micro-second resolution timestamp with both the real time and cpu time.
108#[derive(Clone)]
109pub struct TimestampUs {
110    /// Real time in microseconds.
111    pub time_us: u64,
112    /// Cpu time in microseconds.
113    pub cputime_us: u64,
114}
115
116impl Default for TimestampUs {
117    fn default() -> TimestampUs {
118        TimestampUs {
119            time_us: get_time_us(ClockType::Monotonic),
120            cputime_us: get_time_us(ClockType::ProcessCpu),
121        }
122    }
123}
124
125/// Get process CPU time in us.
126pub fn now_cputime_us() -> u64 {
127    get_time_us(ClockType::ProcessCpu)
128}
129
130/// Returns a timestamp in nanoseconds from a monotonic clock.
131///
132/// Uses `_rdstc` on `x86_64` and [`get_time`](fn.get_time.html) on other architectures.
133pub fn timestamp_cycles() -> u64 {
134    #[cfg(target_arch = "x86_64")]
135    // Safe because there's nothing that can go wrong with this call.
136    unsafe {
137        std::arch::x86_64::_rdtsc() as u64
138    }
139    #[cfg(not(target_arch = "x86_64"))]
140    {
141        get_time_ns(ClockType::Monotonic)
142    }
143}
144
145/// Returns a timestamp in nanoseconds based on the provided clock type.
146///
147/// # Arguments
148///
149/// * `clock_type` - Identifier of the Linux Kernel clock on which to act.
150pub fn get_time_ns(clock_type: ClockType) -> u64 {
151    let mut time_struct = libc::timespec {
152        tv_sec: 0,
153        tv_nsec: 0,
154    };
155    // Safe because the parameters are valid.
156    unsafe { libc::clock_gettime(clock_type.into(), &mut time_struct) };
157    seconds_to_nanoseconds(time_struct.tv_sec).expect("Time conversion overflow") as u64
158        + (time_struct.tv_nsec as u64)
159}
160
161/// Returns a timestamp in microseconds based on the provided clock type.
162///
163/// # Arguments
164///
165/// * `clock_type` - Identifier of the Linux Kernel clock on which to act.
166pub fn get_time_us(clock_type: ClockType) -> u64 {
167    get_time_ns(clock_type) / 1000
168}
169
170/// Returns a timestamp in milliseconds based on the provided clock type.
171///
172/// # Arguments
173///
174/// * `clock_type` - Identifier of the Linux Kernel clock on which to act.
175pub fn get_time_ms(clock_type: ClockType) -> u64 {
176    get_time_ns(clock_type) / NANOS_PER_MILLISECOND
177}
178
179/// Converts a timestamp in seconds to an equivalent one in nanoseconds.
180/// Returns `None` if the conversion overflows.
181///
182/// # Arguments
183///
184/// * `value` - Timestamp in seconds.
185pub fn seconds_to_nanoseconds(value: i64) -> Option<i64> {
186    value.checked_mul(NANOS_PER_SECOND as i64)
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192
193    #[test]
194    fn test_get_time() {
195        for _ in 0..1000 {
196            assert!(get_time_ns(ClockType::Monotonic) <= get_time_ns(ClockType::Monotonic));
197        }
198
199        for _ in 0..1000 {
200            assert!(get_time_ns(ClockType::ProcessCpu) <= get_time_ns(ClockType::ProcessCpu));
201        }
202
203        for _ in 0..1000 {
204            assert!(get_time_ns(ClockType::ThreadCpu) <= get_time_ns(ClockType::ThreadCpu));
205        }
206
207        assert_ne!(get_time_ns(ClockType::Real), 0);
208        assert_ne!(get_time_us(ClockType::Real), 0);
209        assert!(get_time_ns(ClockType::Real) / 1000 <= get_time_us(ClockType::Real));
210        assert!(
211            get_time_ns(ClockType::Real) / NANOS_PER_MILLISECOND <= get_time_ms(ClockType::Real)
212        );
213    }
214
215    #[test]
216    fn test_local_time_display() {
217        let local_time = LocalTime {
218            sec: 30,
219            min: 15,
220            hour: 10,
221            mday: 4,
222            mon: 6,
223            year: 119,
224            nsec: 123_456_789,
225        };
226        assert_eq!(
227            String::from("2019-07-04T10:15:30.123456789"),
228            local_time.to_string()
229        );
230
231        let local_time = LocalTime {
232            sec: 5,
233            min: 5,
234            hour: 5,
235            mday: 23,
236            mon: 7,
237            year: 44,
238            nsec: 123,
239        };
240        assert_eq!(
241            String::from("1944-08-23T05:05:05.000000123"),
242            local_time.to_string()
243        );
244
245        let local_time = LocalTime::now();
246        assert!(local_time.mon >= 0 && local_time.mon <= 11);
247    }
248
249    #[test]
250    fn test_seconds_to_nanoseconds() {
251        assert_eq!(
252            seconds_to_nanoseconds(100).unwrap() as u64,
253            100 * NANOS_PER_SECOND
254        );
255
256        assert!(seconds_to_nanoseconds(9_223_372_037).is_none());
257    }
258}