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