nix/sys/
time.rs

1#[cfg_attr(target_env = "musl", allow(deprecated))]
2// https://github.com/rust-lang/libc/issues/1848
3pub use libc::{suseconds_t, time_t};
4use libc::{timespec, timeval};
5use std::convert::From;
6use std::time::Duration;
7use std::{cmp, fmt, ops};
8
9const fn zero_init_timespec() -> timespec {
10    // `std::mem::MaybeUninit::zeroed()` is not yet a const fn
11    // (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of
12    // the appropriate size to zero and then transmute it to a timespec value.
13    unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
14}
15
16#[cfg(any(
17    all(feature = "time", any(target_os = "android", target_os = "linux")),
18    all(
19        any(
20            target_os = "freebsd",
21            target_os = "illumos",
22            target_os = "linux",
23            target_os = "netbsd"
24        ),
25        feature = "time",
26        feature = "signal"
27    )
28))]
29pub(crate) mod timer {
30    use crate::sys::time::{zero_init_timespec, TimeSpec};
31    use bitflags::bitflags;
32
33    #[derive(Debug, Clone, Copy)]
34    pub(crate) struct TimerSpec(libc::itimerspec);
35
36    impl TimerSpec {
37        pub const fn none() -> Self {
38            Self(libc::itimerspec {
39                it_interval: zero_init_timespec(),
40                it_value: zero_init_timespec(),
41            })
42        }
43    }
44
45    impl AsMut<libc::itimerspec> for TimerSpec {
46        fn as_mut(&mut self) -> &mut libc::itimerspec {
47            &mut self.0
48        }
49    }
50
51    impl AsRef<libc::itimerspec> for TimerSpec {
52        fn as_ref(&self) -> &libc::itimerspec {
53            &self.0
54        }
55    }
56
57    impl From<Expiration> for TimerSpec {
58        fn from(expiration: Expiration) -> TimerSpec {
59            match expiration {
60                Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
61                    it_interval: zero_init_timespec(),
62                    it_value: *t.as_ref(),
63                }),
64                Expiration::IntervalDelayed(start, interval) => {
65                    TimerSpec(libc::itimerspec {
66                        it_interval: *interval.as_ref(),
67                        it_value: *start.as_ref(),
68                    })
69                }
70                Expiration::Interval(t) => TimerSpec(libc::itimerspec {
71                    it_interval: *t.as_ref(),
72                    it_value: *t.as_ref(),
73                }),
74            }
75        }
76    }
77
78    /// An enumeration allowing the definition of the expiration time of an alarm,
79    /// recurring or not.
80    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
81    pub enum Expiration {
82        /// Alarm will trigger once after the time given in `TimeSpec`
83        OneShot(TimeSpec),
84        /// Alarm will trigger after a specified delay and then every interval of
85        /// time.
86        IntervalDelayed(TimeSpec, TimeSpec),
87        /// Alarm will trigger every specified interval of time.
88        Interval(TimeSpec),
89    }
90
91    #[cfg(any(target_os = "android", target_os = "linux"))]
92    bitflags! {
93        /// Flags that are used for arming the timer.
94        pub struct TimerSetTimeFlags: libc::c_int {
95            const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
96        }
97    }
98    #[cfg(any(
99        target_os = "freebsd",
100        target_os = "netbsd",
101        target_os = "dragonfly",
102        target_os = "illumos"
103    ))]
104    bitflags! {
105        /// Flags that are used for arming the timer.
106        pub struct TimerSetTimeFlags: libc::c_int {
107            const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
108        }
109    }
110
111    impl From<TimerSpec> for Expiration {
112        fn from(timerspec: TimerSpec) -> Expiration {
113            match timerspec {
114                TimerSpec(libc::itimerspec {
115                    it_interval:
116                        libc::timespec {
117                            tv_sec: 0,
118                            tv_nsec: 0,
119                            ..
120                        },
121                    it_value: ts,
122                }) => Expiration::OneShot(ts.into()),
123                TimerSpec(libc::itimerspec {
124                    it_interval: int_ts,
125                    it_value: val_ts,
126                }) => {
127                    if (int_ts.tv_sec == val_ts.tv_sec)
128                        && (int_ts.tv_nsec == val_ts.tv_nsec)
129                    {
130                        Expiration::Interval(int_ts.into())
131                    } else {
132                        Expiration::IntervalDelayed(
133                            val_ts.into(),
134                            int_ts.into(),
135                        )
136                    }
137                }
138            }
139        }
140    }
141}
142
143pub trait TimeValLike: Sized {
144    #[inline]
145    fn zero() -> Self {
146        Self::seconds(0)
147    }
148
149    #[inline]
150    fn hours(hours: i64) -> Self {
151        let secs = hours
152            .checked_mul(SECS_PER_HOUR)
153            .expect("TimeValLike::hours ouf of bounds");
154        Self::seconds(secs)
155    }
156
157    #[inline]
158    fn minutes(minutes: i64) -> Self {
159        let secs = minutes
160            .checked_mul(SECS_PER_MINUTE)
161            .expect("TimeValLike::minutes out of bounds");
162        Self::seconds(secs)
163    }
164
165    fn seconds(seconds: i64) -> Self;
166    fn milliseconds(milliseconds: i64) -> Self;
167    fn microseconds(microseconds: i64) -> Self;
168    fn nanoseconds(nanoseconds: i64) -> Self;
169
170    #[inline]
171    fn num_hours(&self) -> i64 {
172        self.num_seconds() / 3600
173    }
174
175    #[inline]
176    fn num_minutes(&self) -> i64 {
177        self.num_seconds() / 60
178    }
179
180    fn num_seconds(&self) -> i64;
181    fn num_milliseconds(&self) -> i64;
182    fn num_microseconds(&self) -> i64;
183    fn num_nanoseconds(&self) -> i64;
184}
185
186#[repr(C)]
187#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
188pub struct TimeSpec(timespec);
189
190const NANOS_PER_SEC: i64 = 1_000_000_000;
191const SECS_PER_MINUTE: i64 = 60;
192const SECS_PER_HOUR: i64 = 3600;
193
194#[cfg(target_pointer_width = "64")]
195const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
196
197#[cfg(target_pointer_width = "32")]
198const TS_MAX_SECONDS: i64 = isize::MAX as i64;
199
200const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
201
202// x32 compatibility
203// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
204#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
205type timespec_tv_nsec_t = i64;
206#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
207type timespec_tv_nsec_t = libc::c_long;
208
209impl From<timespec> for TimeSpec {
210    fn from(ts: timespec) -> Self {
211        Self(ts)
212    }
213}
214
215impl From<Duration> for TimeSpec {
216    fn from(duration: Duration) -> Self {
217        Self::from_duration(duration)
218    }
219}
220
221impl From<TimeSpec> for Duration {
222    fn from(timespec: TimeSpec) -> Self {
223        Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
224    }
225}
226
227impl AsRef<timespec> for TimeSpec {
228    fn as_ref(&self) -> &timespec {
229        &self.0
230    }
231}
232
233impl AsMut<timespec> for TimeSpec {
234    fn as_mut(&mut self) -> &mut timespec {
235        &mut self.0
236    }
237}
238
239impl Ord for TimeSpec {
240    // The implementation of cmp is simplified by assuming that the struct is
241    // normalized.  That is, tv_nsec must always be within [0, 1_000_000_000)
242    fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
243        if self.tv_sec() == other.tv_sec() {
244            self.tv_nsec().cmp(&other.tv_nsec())
245        } else {
246            self.tv_sec().cmp(&other.tv_sec())
247        }
248    }
249}
250
251impl PartialOrd for TimeSpec {
252    fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
253        Some(self.cmp(other))
254    }
255}
256
257impl TimeValLike for TimeSpec {
258    #[inline]
259    #[cfg_attr(target_env = "musl", allow(deprecated))]
260    // https://github.com/rust-lang/libc/issues/1848
261    fn seconds(seconds: i64) -> TimeSpec {
262        assert!(
263            (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
264            "TimeSpec out of bounds; seconds={}",
265            seconds
266        );
267        let mut ts = zero_init_timespec();
268        ts.tv_sec = seconds as time_t;
269        TimeSpec(ts)
270    }
271
272    #[inline]
273    fn milliseconds(milliseconds: i64) -> TimeSpec {
274        let nanoseconds = milliseconds
275            .checked_mul(1_000_000)
276            .expect("TimeSpec::milliseconds out of bounds");
277
278        TimeSpec::nanoseconds(nanoseconds)
279    }
280
281    /// Makes a new `TimeSpec` with given number of microseconds.
282    #[inline]
283    fn microseconds(microseconds: i64) -> TimeSpec {
284        let nanoseconds = microseconds
285            .checked_mul(1_000)
286            .expect("TimeSpec::milliseconds out of bounds");
287
288        TimeSpec::nanoseconds(nanoseconds)
289    }
290
291    /// Makes a new `TimeSpec` with given number of nanoseconds.
292    #[inline]
293    #[cfg_attr(target_env = "musl", allow(deprecated))]
294    // https://github.com/rust-lang/libc/issues/1848
295    fn nanoseconds(nanoseconds: i64) -> TimeSpec {
296        let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
297        assert!(
298            (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
299            "TimeSpec out of bounds"
300        );
301        let mut ts = zero_init_timespec();
302        ts.tv_sec = secs as time_t;
303        ts.tv_nsec = nanos as timespec_tv_nsec_t;
304        TimeSpec(ts)
305    }
306
307    // The cast is not unnecessary on all platforms.
308    #[allow(clippy::unnecessary_cast)]
309    fn num_seconds(&self) -> i64 {
310        if self.tv_sec() < 0 && self.tv_nsec() > 0 {
311            (self.tv_sec() + 1) as i64
312        } else {
313            self.tv_sec() as i64
314        }
315    }
316
317    fn num_milliseconds(&self) -> i64 {
318        self.num_nanoseconds() / 1_000_000
319    }
320
321    fn num_microseconds(&self) -> i64 {
322        self.num_nanoseconds() / 1_000
323    }
324
325    // The cast is not unnecessary on all platforms.
326    #[allow(clippy::unnecessary_cast)]
327    fn num_nanoseconds(&self) -> i64 {
328        let secs = self.num_seconds() * 1_000_000_000;
329        let nsec = self.nanos_mod_sec();
330        secs + nsec as i64
331    }
332}
333
334impl TimeSpec {
335    /// Construct a new `TimeSpec` from its components
336    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
337    pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
338        let mut ts = zero_init_timespec();
339        ts.tv_sec = seconds;
340        ts.tv_nsec = nanoseconds;
341        Self(ts)
342    }
343
344    fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
345        if self.tv_sec() < 0 && self.tv_nsec() > 0 {
346            self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
347        } else {
348            self.tv_nsec()
349        }
350    }
351
352    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
353    pub const fn tv_sec(&self) -> time_t {
354        self.0.tv_sec
355    }
356
357    pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
358        self.0.tv_nsec
359    }
360
361    #[cfg_attr(target_env = "musl", allow(deprecated))]
362    // https://github.com/rust-lang/libc/issues/1848
363    pub const fn from_duration(duration: Duration) -> Self {
364        let mut ts = zero_init_timespec();
365        ts.tv_sec = duration.as_secs() as time_t;
366        ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
367        TimeSpec(ts)
368    }
369
370    pub const fn from_timespec(timespec: timespec) -> Self {
371        Self(timespec)
372    }
373}
374
375impl ops::Neg for TimeSpec {
376    type Output = TimeSpec;
377
378    fn neg(self) -> TimeSpec {
379        TimeSpec::nanoseconds(-self.num_nanoseconds())
380    }
381}
382
383impl ops::Add for TimeSpec {
384    type Output = TimeSpec;
385
386    fn add(self, rhs: TimeSpec) -> TimeSpec {
387        TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
388    }
389}
390
391impl ops::Sub for TimeSpec {
392    type Output = TimeSpec;
393
394    fn sub(self, rhs: TimeSpec) -> TimeSpec {
395        TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
396    }
397}
398
399impl ops::Mul<i32> for TimeSpec {
400    type Output = TimeSpec;
401
402    fn mul(self, rhs: i32) -> TimeSpec {
403        let usec = self
404            .num_nanoseconds()
405            .checked_mul(i64::from(rhs))
406            .expect("TimeSpec multiply out of bounds");
407
408        TimeSpec::nanoseconds(usec)
409    }
410}
411
412impl ops::Div<i32> for TimeSpec {
413    type Output = TimeSpec;
414
415    fn div(self, rhs: i32) -> TimeSpec {
416        let usec = self.num_nanoseconds() / i64::from(rhs);
417        TimeSpec::nanoseconds(usec)
418    }
419}
420
421impl fmt::Display for TimeSpec {
422    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423        let (abs, sign) = if self.tv_sec() < 0 {
424            (-*self, "-")
425        } else {
426            (*self, "")
427        };
428
429        let sec = abs.tv_sec();
430
431        write!(f, "{}", sign)?;
432
433        if abs.tv_nsec() == 0 {
434            if abs.tv_sec() == 1 {
435                write!(f, "{} second", sec)?;
436            } else {
437                write!(f, "{} seconds", sec)?;
438            }
439        } else if abs.tv_nsec() % 1_000_000 == 0 {
440            write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?;
441        } else if abs.tv_nsec() % 1_000 == 0 {
442            write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?;
443        } else {
444            write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?;
445        }
446
447        Ok(())
448    }
449}
450
451#[repr(transparent)]
452#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
453pub struct TimeVal(timeval);
454
455const MICROS_PER_SEC: i64 = 1_000_000;
456
457#[cfg(target_pointer_width = "64")]
458const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
459
460#[cfg(target_pointer_width = "32")]
461const TV_MAX_SECONDS: i64 = isize::MAX as i64;
462
463const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
464
465impl AsRef<timeval> for TimeVal {
466    fn as_ref(&self) -> &timeval {
467        &self.0
468    }
469}
470
471impl AsMut<timeval> for TimeVal {
472    fn as_mut(&mut self) -> &mut timeval {
473        &mut self.0
474    }
475}
476
477impl Ord for TimeVal {
478    // The implementation of cmp is simplified by assuming that the struct is
479    // normalized.  That is, tv_usec must always be within [0, 1_000_000)
480    fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
481        if self.tv_sec() == other.tv_sec() {
482            self.tv_usec().cmp(&other.tv_usec())
483        } else {
484            self.tv_sec().cmp(&other.tv_sec())
485        }
486    }
487}
488
489impl PartialOrd for TimeVal {
490    fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
491        Some(self.cmp(other))
492    }
493}
494
495impl TimeValLike for TimeVal {
496    #[inline]
497    fn seconds(seconds: i64) -> TimeVal {
498        assert!(
499            (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
500            "TimeVal out of bounds; seconds={}",
501            seconds
502        );
503        #[cfg_attr(target_env = "musl", allow(deprecated))]
504        // https://github.com/rust-lang/libc/issues/1848
505        TimeVal(timeval {
506            tv_sec: seconds as time_t,
507            tv_usec: 0,
508        })
509    }
510
511    #[inline]
512    fn milliseconds(milliseconds: i64) -> TimeVal {
513        let microseconds = milliseconds
514            .checked_mul(1_000)
515            .expect("TimeVal::milliseconds out of bounds");
516
517        TimeVal::microseconds(microseconds)
518    }
519
520    /// Makes a new `TimeVal` with given number of microseconds.
521    #[inline]
522    fn microseconds(microseconds: i64) -> TimeVal {
523        let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
524        assert!(
525            (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
526            "TimeVal out of bounds"
527        );
528        #[cfg_attr(target_env = "musl", allow(deprecated))]
529        // https://github.com/rust-lang/libc/issues/1848
530        TimeVal(timeval {
531            tv_sec: secs as time_t,
532            tv_usec: micros as suseconds_t,
533        })
534    }
535
536    /// Makes a new `TimeVal` with given number of nanoseconds.  Some precision
537    /// will be lost
538    #[inline]
539    fn nanoseconds(nanoseconds: i64) -> TimeVal {
540        let microseconds = nanoseconds / 1000;
541        let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
542        assert!(
543            (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
544            "TimeVal out of bounds"
545        );
546        #[cfg_attr(target_env = "musl", allow(deprecated))]
547        // https://github.com/rust-lang/libc/issues/1848
548        TimeVal(timeval {
549            tv_sec: secs as time_t,
550            tv_usec: micros as suseconds_t,
551        })
552    }
553
554    // The cast is not unnecessary on all platforms.
555    #[allow(clippy::unnecessary_cast)]
556    fn num_seconds(&self) -> i64 {
557        if self.tv_sec() < 0 && self.tv_usec() > 0 {
558            (self.tv_sec() + 1) as i64
559        } else {
560            self.tv_sec() as i64
561        }
562    }
563
564    fn num_milliseconds(&self) -> i64 {
565        self.num_microseconds() / 1_000
566    }
567
568    // The cast is not unnecessary on all platforms.
569    #[allow(clippy::unnecessary_cast)]
570    fn num_microseconds(&self) -> i64 {
571        let secs = self.num_seconds() * 1_000_000;
572        let usec = self.micros_mod_sec();
573        secs + usec as i64
574    }
575
576    fn num_nanoseconds(&self) -> i64 {
577        self.num_microseconds() * 1_000
578    }
579}
580
581impl TimeVal {
582    /// Construct a new `TimeVal` from its components
583    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
584    pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
585        Self(timeval {
586            tv_sec: seconds,
587            tv_usec: microseconds,
588        })
589    }
590
591    fn micros_mod_sec(&self) -> suseconds_t {
592        if self.tv_sec() < 0 && self.tv_usec() > 0 {
593            self.tv_usec() - MICROS_PER_SEC as suseconds_t
594        } else {
595            self.tv_usec()
596        }
597    }
598
599    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
600    pub const fn tv_sec(&self) -> time_t {
601        self.0.tv_sec
602    }
603
604    pub const fn tv_usec(&self) -> suseconds_t {
605        self.0.tv_usec
606    }
607}
608
609impl ops::Neg for TimeVal {
610    type Output = TimeVal;
611
612    fn neg(self) -> TimeVal {
613        TimeVal::microseconds(-self.num_microseconds())
614    }
615}
616
617impl ops::Add for TimeVal {
618    type Output = TimeVal;
619
620    fn add(self, rhs: TimeVal) -> TimeVal {
621        TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
622    }
623}
624
625impl ops::Sub for TimeVal {
626    type Output = TimeVal;
627
628    fn sub(self, rhs: TimeVal) -> TimeVal {
629        TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
630    }
631}
632
633impl ops::Mul<i32> for TimeVal {
634    type Output = TimeVal;
635
636    fn mul(self, rhs: i32) -> TimeVal {
637        let usec = self
638            .num_microseconds()
639            .checked_mul(i64::from(rhs))
640            .expect("TimeVal multiply out of bounds");
641
642        TimeVal::microseconds(usec)
643    }
644}
645
646impl ops::Div<i32> for TimeVal {
647    type Output = TimeVal;
648
649    fn div(self, rhs: i32) -> TimeVal {
650        let usec = self.num_microseconds() / i64::from(rhs);
651        TimeVal::microseconds(usec)
652    }
653}
654
655impl fmt::Display for TimeVal {
656    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
657        let (abs, sign) = if self.tv_sec() < 0 {
658            (-*self, "-")
659        } else {
660            (*self, "")
661        };
662
663        let sec = abs.tv_sec();
664
665        write!(f, "{}", sign)?;
666
667        if abs.tv_usec() == 0 {
668            if abs.tv_sec() == 1 {
669                write!(f, "{} second", sec)?;
670            } else {
671                write!(f, "{} seconds", sec)?;
672            }
673        } else if abs.tv_usec() % 1000 == 0 {
674            write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?;
675        } else {
676            write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?;
677        }
678
679        Ok(())
680    }
681}
682
683impl From<timeval> for TimeVal {
684    fn from(tv: timeval) -> Self {
685        TimeVal(tv)
686    }
687}
688
689#[inline]
690fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
691    (div_floor_64(this, other), mod_floor_64(this, other))
692}
693
694#[inline]
695fn div_floor_64(this: i64, other: i64) -> i64 {
696    match div_rem_64(this, other) {
697        (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
698        (d, _) => d,
699    }
700}
701
702#[inline]
703fn mod_floor_64(this: i64, other: i64) -> i64 {
704    match this % other {
705        r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
706        r => r,
707    }
708}
709
710#[inline]
711fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
712    (this / other, this % other)
713}
714
715#[cfg(test)]
716mod test {
717    use super::{TimeSpec, TimeVal, TimeValLike};
718    use std::time::Duration;
719
720    #[test]
721    pub fn test_timespec() {
722        assert_ne!(TimeSpec::seconds(1), TimeSpec::zero());
723        assert_eq!(
724            TimeSpec::seconds(1) + TimeSpec::seconds(2),
725            TimeSpec::seconds(3)
726        );
727        assert_eq!(
728            TimeSpec::minutes(3) + TimeSpec::seconds(2),
729            TimeSpec::seconds(182)
730        );
731    }
732
733    #[test]
734    pub fn test_timespec_from() {
735        let duration = Duration::new(123, 123_456_789);
736        let timespec = TimeSpec::nanoseconds(123_123_456_789);
737
738        assert_eq!(TimeSpec::from(duration), timespec);
739        assert_eq!(Duration::from(timespec), duration);
740    }
741
742    #[test]
743    pub fn test_timespec_neg() {
744        let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
745        let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
746
747        assert_eq!(a, -b);
748    }
749
750    #[test]
751    pub fn test_timespec_ord() {
752        assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000));
753        assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
754        assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
755        assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
756        assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
757    }
758
759    #[test]
760    pub fn test_timespec_fmt() {
761        assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
762        assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
763        assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
764        assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
765        assert_eq!(
766            TimeSpec::nanoseconds(42).to_string(),
767            "0.000000042 seconds"
768        );
769        assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
770    }
771
772    #[test]
773    pub fn test_timeval() {
774        assert_ne!(TimeVal::seconds(1), TimeVal::zero());
775        assert_eq!(
776            TimeVal::seconds(1) + TimeVal::seconds(2),
777            TimeVal::seconds(3)
778        );
779        assert_eq!(
780            TimeVal::minutes(3) + TimeVal::seconds(2),
781            TimeVal::seconds(182)
782        );
783    }
784
785    #[test]
786    pub fn test_timeval_ord() {
787        assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000));
788        assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
789        assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
790        assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
791        assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
792    }
793
794    #[test]
795    pub fn test_timeval_neg() {
796        let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
797        let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
798
799        assert_eq!(a, -b);
800    }
801
802    #[test]
803    pub fn test_timeval_fmt() {
804        assert_eq!(TimeVal::zero().to_string(), "0 seconds");
805        assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
806        assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
807        assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
808        assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
809        assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
810    }
811}