nix/sys/
time.rs

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