nix/sys/
time.rs

1use std::{cmp, fmt, ops};
2use std::time::Duration;
3use std::convert::From;
4use libc::{timespec, timeval};
5#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
6pub use libc::{time_t, suseconds_t};
7
8pub(crate) const TIMESPEC_ZERO: libc::timespec = unsafe {
9    std::mem::transmute([0u8; std::mem::size_of::<libc::timespec>()])
10};
11
12pub trait TimeValLike: Sized {
13    #[inline]
14    fn zero() -> Self {
15        Self::seconds(0)
16    }
17
18    #[inline]
19    fn hours(hours: i64) -> Self {
20        let secs = hours.checked_mul(SECS_PER_HOUR)
21            .expect("TimeValLike::hours ouf of bounds");
22        Self::seconds(secs)
23    }
24
25    #[inline]
26    fn minutes(minutes: i64) -> Self {
27        let secs = minutes.checked_mul(SECS_PER_MINUTE)
28            .expect("TimeValLike::minutes out of bounds");
29        Self::seconds(secs)
30    }
31
32    fn seconds(seconds: i64) -> Self;
33    fn milliseconds(milliseconds: i64) -> Self;
34    fn microseconds(microseconds: i64) -> Self;
35    fn nanoseconds(nanoseconds: i64) -> Self;
36
37    #[inline]
38    fn num_hours(&self) -> i64 {
39        self.num_seconds() / 3600
40    }
41
42    #[inline]
43    fn num_minutes(&self) -> i64 {
44        self.num_seconds() / 60
45    }
46
47    fn num_seconds(&self) -> i64;
48    fn num_milliseconds(&self) -> i64;
49    fn num_microseconds(&self) -> i64;
50    fn num_nanoseconds(&self) -> i64;
51}
52
53#[repr(C)]
54#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55pub struct TimeSpec(timespec);
56
57const NANOS_PER_SEC: i64 = 1_000_000_000;
58const SECS_PER_MINUTE: i64 = 60;
59const SECS_PER_HOUR: i64 = 3600;
60
61#[cfg(target_pointer_width = "64")]
62const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1;
63
64#[cfg(target_pointer_width = "32")]
65const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
66
67const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
68
69// x32 compatibility
70// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
71#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
72type timespec_tv_nsec_t = i64;
73#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
74type timespec_tv_nsec_t = libc::c_long;
75
76impl From<timespec> for TimeSpec {
77    fn from(ts: timespec) -> Self {
78        Self(ts)
79    }
80}
81
82impl From<Duration> for TimeSpec {
83    fn from(duration: Duration) -> Self {
84        Self::from_duration(duration)
85    }
86}
87
88impl From<TimeSpec> for Duration {
89    fn from(timespec: TimeSpec) -> Self {
90        Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
91    }
92}
93
94impl AsRef<timespec> for TimeSpec {
95    fn as_ref(&self) -> &timespec {
96        &self.0
97    }
98}
99
100impl AsMut<timespec> for TimeSpec {
101    fn as_mut(&mut self) -> &mut timespec {
102        &mut self.0
103    }
104}
105
106impl Ord for TimeSpec {
107    // The implementation of cmp is simplified by assuming that the struct is
108    // normalized.  That is, tv_nsec must always be within [0, 1_000_000_000)
109    fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
110        if self.tv_sec() == other.tv_sec() {
111            self.tv_nsec().cmp(&other.tv_nsec())
112        } else {
113            self.tv_sec().cmp(&other.tv_sec())
114        }
115    }
116}
117
118impl PartialOrd for TimeSpec {
119    fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
120        Some(self.cmp(other))
121    }
122}
123
124impl TimeValLike for TimeSpec {
125    #[inline]
126    #[cfg_attr(target_env = "musl", allow(deprecated))]
127    // https://github.com/rust-lang/libc/issues/1848
128    fn seconds(seconds: i64) -> TimeSpec {
129        assert!((TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
130                "TimeSpec out of bounds; seconds={}", seconds);
131        let mut ts = TIMESPEC_ZERO;
132        ts.tv_sec = seconds as time_t;
133        ts.tv_nsec = 0;
134        TimeSpec(ts)
135    }
136
137    #[inline]
138    fn milliseconds(milliseconds: i64) -> TimeSpec {
139        let nanoseconds = milliseconds.checked_mul(1_000_000)
140            .expect("TimeSpec::milliseconds out of bounds");
141
142        TimeSpec::nanoseconds(nanoseconds)
143    }
144
145    /// Makes a new `TimeSpec` with given number of microseconds.
146    #[inline]
147    fn microseconds(microseconds: i64) -> TimeSpec {
148        let nanoseconds = microseconds.checked_mul(1_000)
149            .expect("TimeSpec::milliseconds out of bounds");
150
151        TimeSpec::nanoseconds(nanoseconds)
152    }
153
154    /// Makes a new `TimeSpec` with given number of nanoseconds.
155    #[inline]
156    #[cfg_attr(target_env = "musl", allow(deprecated))]
157    // https://github.com/rust-lang/libc/issues/1848
158    fn nanoseconds(nanoseconds: i64) -> TimeSpec {
159        let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
160        assert!((TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
161                "TimeSpec out of bounds");
162        let mut ts = TIMESPEC_ZERO;
163        ts.tv_sec = secs as time_t;
164        ts.tv_nsec = nanos as timespec_tv_nsec_t;
165        TimeSpec(ts)
166    }
167
168    // The cast is not unnecessary on all platforms.
169    #[allow(clippy::unnecessary_cast)]
170    fn num_seconds(&self) -> i64 {
171        if self.tv_sec() < 0 && self.tv_nsec() > 0 {
172            (self.tv_sec() + 1) as i64
173        } else {
174            self.tv_sec() as i64
175        }
176    }
177
178    fn num_milliseconds(&self) -> i64 {
179        self.num_nanoseconds() / 1_000_000
180    }
181
182    fn num_microseconds(&self) -> i64 {
183        self.num_nanoseconds() / 1_000
184    }
185
186    // The cast is not unnecessary on all platforms.
187    #[allow(clippy::unnecessary_cast)]
188    fn num_nanoseconds(&self) -> i64 {
189        let secs = self.num_seconds() * 1_000_000_000;
190        let nsec = self.nanos_mod_sec();
191        secs + nsec as i64
192    }
193}
194
195impl TimeSpec {
196    fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
197        if self.tv_sec() < 0 && self.tv_nsec() > 0 {
198            self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
199        } else {
200            self.tv_nsec()
201        }
202    }
203
204    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
205    pub const fn tv_sec(&self) -> time_t {
206        self.0.tv_sec
207    }
208
209    pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
210        self.0.tv_nsec
211    }
212
213    #[cfg_attr(target_env = "musl", allow(deprecated))]
214    // https://github.com/rust-lang/libc/issues/1848
215    pub const fn from_duration(duration: Duration) -> Self {
216        let mut ts = TIMESPEC_ZERO;
217        ts.tv_sec = duration.as_secs() as time_t;
218        ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
219        TimeSpec(ts)
220    }
221
222    pub const fn from_timespec(timespec: timespec) -> Self {
223        Self(timespec)
224    }
225}
226
227impl ops::Neg for TimeSpec {
228    type Output = TimeSpec;
229
230    fn neg(self) -> TimeSpec {
231        TimeSpec::nanoseconds(-self.num_nanoseconds())
232    }
233}
234
235impl ops::Add for TimeSpec {
236    type Output = TimeSpec;
237
238    fn add(self, rhs: TimeSpec) -> TimeSpec {
239        TimeSpec::nanoseconds(
240            self.num_nanoseconds() + rhs.num_nanoseconds())
241    }
242}
243
244impl ops::Sub for TimeSpec {
245    type Output = TimeSpec;
246
247    fn sub(self, rhs: TimeSpec) -> TimeSpec {
248        TimeSpec::nanoseconds(
249            self.num_nanoseconds() - rhs.num_nanoseconds())
250    }
251}
252
253impl ops::Mul<i32> for TimeSpec {
254    type Output = TimeSpec;
255
256    fn mul(self, rhs: i32) -> TimeSpec {
257        let usec = self.num_nanoseconds().checked_mul(i64::from(rhs))
258            .expect("TimeSpec multiply out of bounds");
259
260        TimeSpec::nanoseconds(usec)
261    }
262}
263
264impl ops::Div<i32> for TimeSpec {
265    type Output = TimeSpec;
266
267    fn div(self, rhs: i32) -> TimeSpec {
268        let usec = self.num_nanoseconds() / i64::from(rhs);
269        TimeSpec::nanoseconds(usec)
270    }
271}
272
273impl fmt::Display for TimeSpec {
274    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275        let (abs, sign) = if self.tv_sec() < 0 {
276            (-*self, "-")
277        } else {
278            (*self, "")
279        };
280
281        let sec = abs.tv_sec();
282
283        write!(f, "{}", sign)?;
284
285        if abs.tv_nsec() == 0 {
286            if abs.tv_sec() == 1 {
287                write!(f, "{} second", sec)?;
288            } else {
289                write!(f, "{} seconds", sec)?;
290            }
291        } else if abs.tv_nsec() % 1_000_000 == 0 {
292            write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?;
293        } else if abs.tv_nsec() % 1_000 == 0 {
294            write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?;
295        } else {
296            write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?;
297        }
298
299        Ok(())
300    }
301}
302
303
304
305#[repr(transparent)]
306#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
307pub struct TimeVal(timeval);
308
309const MICROS_PER_SEC: i64 = 1_000_000;
310
311#[cfg(target_pointer_width = "64")]
312const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1;
313
314#[cfg(target_pointer_width = "32")]
315const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
316
317const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
318
319impl AsRef<timeval> for TimeVal {
320    fn as_ref(&self) -> &timeval {
321        &self.0
322    }
323}
324
325impl AsMut<timeval> for TimeVal {
326    fn as_mut(&mut self) -> &mut timeval {
327        &mut self.0
328    }
329}
330
331impl Ord for TimeVal {
332    // The implementation of cmp is simplified by assuming that the struct is
333    // normalized.  That is, tv_usec must always be within [0, 1_000_000)
334    fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
335        if self.tv_sec() == other.tv_sec() {
336            self.tv_usec().cmp(&other.tv_usec())
337        } else {
338            self.tv_sec().cmp(&other.tv_sec())
339        }
340    }
341}
342
343impl PartialOrd for TimeVal {
344    fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
345        Some(self.cmp(other))
346    }
347}
348
349impl TimeValLike for TimeVal {
350    #[inline]
351    fn seconds(seconds: i64) -> TimeVal {
352        assert!((TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
353                "TimeVal out of bounds; seconds={}", seconds);
354        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
355        TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 })
356    }
357
358    #[inline]
359    fn milliseconds(milliseconds: i64) -> TimeVal {
360        let microseconds = milliseconds.checked_mul(1_000)
361            .expect("TimeVal::milliseconds out of bounds");
362
363        TimeVal::microseconds(microseconds)
364    }
365
366    /// Makes a new `TimeVal` with given number of microseconds.
367    #[inline]
368    fn microseconds(microseconds: i64) -> TimeVal {
369        let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
370        assert!((TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
371                "TimeVal out of bounds");
372        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
373        TimeVal(timeval {tv_sec: secs as time_t,
374                           tv_usec: micros as suseconds_t })
375    }
376
377    /// Makes a new `TimeVal` with given number of nanoseconds.  Some precision
378    /// will be lost
379    #[inline]
380    fn nanoseconds(nanoseconds: i64) -> TimeVal {
381        let microseconds = nanoseconds / 1000;
382        let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
383        assert!((TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
384                "TimeVal out of bounds");
385        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
386        TimeVal(timeval {tv_sec: secs as time_t,
387                           tv_usec: micros as suseconds_t })
388    }
389
390    // The cast is not unnecessary on all platforms.
391    #[allow(clippy::unnecessary_cast)]
392    fn num_seconds(&self) -> i64 {
393        if self.tv_sec() < 0 && self.tv_usec() > 0 {
394            (self.tv_sec() + 1) as i64
395        } else {
396            self.tv_sec() as i64
397        }
398    }
399
400    fn num_milliseconds(&self) -> i64 {
401        self.num_microseconds() / 1_000
402    }
403
404    // The cast is not unnecessary on all platforms.
405    #[allow(clippy::unnecessary_cast)]
406    fn num_microseconds(&self) -> i64 {
407        let secs = self.num_seconds() * 1_000_000;
408        let usec = self.micros_mod_sec();
409        secs + usec as i64
410    }
411
412    fn num_nanoseconds(&self) -> i64 {
413        self.num_microseconds() * 1_000
414    }
415}
416
417impl TimeVal {
418    fn micros_mod_sec(&self) -> suseconds_t {
419        if self.tv_sec() < 0 && self.tv_usec() > 0 {
420            self.tv_usec() - MICROS_PER_SEC as suseconds_t
421        } else {
422            self.tv_usec()
423        }
424    }
425
426    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
427    pub const fn tv_sec(&self) -> time_t {
428        self.0.tv_sec
429    }
430
431    pub const fn tv_usec(&self) -> suseconds_t {
432        self.0.tv_usec
433    }
434}
435
436impl ops::Neg for TimeVal {
437    type Output = TimeVal;
438
439    fn neg(self) -> TimeVal {
440        TimeVal::microseconds(-self.num_microseconds())
441    }
442}
443
444impl ops::Add for TimeVal {
445    type Output = TimeVal;
446
447    fn add(self, rhs: TimeVal) -> TimeVal {
448        TimeVal::microseconds(
449            self.num_microseconds() + rhs.num_microseconds())
450    }
451}
452
453impl ops::Sub for TimeVal {
454    type Output = TimeVal;
455
456    fn sub(self, rhs: TimeVal) -> TimeVal {
457        TimeVal::microseconds(
458            self.num_microseconds() - rhs.num_microseconds())
459    }
460}
461
462impl ops::Mul<i32> for TimeVal {
463    type Output = TimeVal;
464
465    fn mul(self, rhs: i32) -> TimeVal {
466        let usec = self.num_microseconds().checked_mul(i64::from(rhs))
467            .expect("TimeVal multiply out of bounds");
468
469        TimeVal::microseconds(usec)
470    }
471}
472
473impl ops::Div<i32> for TimeVal {
474    type Output = TimeVal;
475
476    fn div(self, rhs: i32) -> TimeVal {
477        let usec = self.num_microseconds() / i64::from(rhs);
478        TimeVal::microseconds(usec)
479    }
480}
481
482impl fmt::Display for TimeVal {
483    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
484        let (abs, sign) = if self.tv_sec() < 0 {
485            (-*self, "-")
486        } else {
487            (*self, "")
488        };
489
490        let sec = abs.tv_sec();
491
492        write!(f, "{}", sign)?;
493
494        if abs.tv_usec() == 0 {
495            if abs.tv_sec() == 1 {
496                write!(f, "{} second", sec)?;
497            } else {
498                write!(f, "{} seconds", sec)?;
499            }
500        } else if abs.tv_usec() % 1000 == 0 {
501            write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?;
502        } else {
503            write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?;
504        }
505
506        Ok(())
507    }
508}
509
510impl From<timeval> for TimeVal {
511    fn from(tv: timeval) -> Self {
512        TimeVal(tv)
513    }
514}
515
516#[inline]
517fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
518    (div_floor_64(this, other), mod_floor_64(this, other))
519}
520
521#[inline]
522fn div_floor_64(this: i64, other: i64) -> i64 {
523    match div_rem_64(this, other) {
524        (d, r) if (r > 0 && other < 0)
525               || (r < 0 && other > 0) => d - 1,
526        (d, _)                         => d,
527    }
528}
529
530#[inline]
531fn mod_floor_64(this: i64, other: i64) -> i64 {
532    match this % other {
533        r if (r > 0 && other < 0)
534          || (r < 0 && other > 0) => r + other,
535        r                         => r,
536    }
537}
538
539#[inline]
540fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
541    (this / other, this % other)
542}
543
544#[cfg(test)]
545mod test {
546    use super::{TimeSpec, TimeVal, TimeValLike};
547    use std::time::Duration;
548
549    #[test]
550    pub fn test_timespec() {
551        assert!(TimeSpec::seconds(1) != TimeSpec::zero());
552        assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2),
553                   TimeSpec::seconds(3));
554        assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2),
555                   TimeSpec::seconds(182));
556    }
557
558    #[test]
559    pub fn test_timespec_from() {
560        let duration = Duration::new(123, 123_456_789);
561        let timespec = TimeSpec::nanoseconds(123_123_456_789);
562
563        assert_eq!(TimeSpec::from(duration), timespec);
564        assert_eq!(Duration::from(timespec), duration);
565    }
566
567    #[test]
568    pub fn test_timespec_neg() {
569        let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
570        let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
571
572        assert_eq!(a, -b);
573    }
574
575    #[test]
576    pub fn test_timespec_ord() {
577        assert!(TimeSpec::seconds(1) == TimeSpec::nanoseconds(1_000_000_000));
578        assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
579        assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
580        assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
581        assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
582    }
583
584    #[test]
585    pub fn test_timespec_fmt() {
586        assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
587        assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
588        assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
589        assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
590        assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds");
591        assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
592    }
593
594    #[test]
595    pub fn test_timeval() {
596        assert!(TimeVal::seconds(1) != TimeVal::zero());
597        assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2),
598                   TimeVal::seconds(3));
599        assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2),
600                   TimeVal::seconds(182));
601    }
602
603    #[test]
604    pub fn test_timeval_ord() {
605        assert!(TimeVal::seconds(1) == TimeVal::microseconds(1_000_000));
606        assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
607        assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
608        assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
609        assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
610    }
611
612    #[test]
613    pub fn test_timeval_neg() {
614        let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
615        let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
616
617        assert_eq!(a, -b);
618    }
619
620    #[test]
621    pub fn test_timeval_fmt() {
622        assert_eq!(TimeVal::zero().to_string(), "0 seconds");
623        assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
624        assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
625        assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
626        assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
627        assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
628    }
629}