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