1use core::num::niche_types::Nanoseconds;
2
3use crate::sys_common::AsInner;
4use crate::time::Duration;
5use crate::{fmt, io};
6
7const NSEC_PER_SEC: u64 = 1_000_000_000;
8pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
9#[allow(dead_code)] pub const TIMESPEC_MAX: libc::timespec =
11 libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
12
13#[cfg(target_os = "nto")]
16pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec {
17 tv_sec: (u64::MAX / NSEC_PER_SEC) as i64,
18 tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64,
19};
20
21#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct SystemTime {
23 pub(crate) t: Timespec,
24}
25
26#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub(crate) struct Timespec {
28 tv_sec: i64,
29 tv_nsec: Nanoseconds,
30}
31
32impl SystemTime {
33 #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))]
34 pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
35 Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? })
36 }
37
38 pub fn now() -> SystemTime {
39 SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
40 }
41
42 pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
43 self.t.sub_timespec(&other.t)
44 }
45
46 pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
47 Some(SystemTime { t: self.t.checked_add_duration(other)? })
48 }
49
50 pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
51 Some(SystemTime { t: self.t.checked_sub_duration(other)? })
52 }
53}
54
55impl fmt::Debug for SystemTime {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.debug_struct("SystemTime")
58 .field("tv_sec", &self.t.tv_sec)
59 .field("tv_nsec", &self.t.tv_nsec)
60 .finish()
61 }
62}
63
64impl Timespec {
65 const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
66 Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
67 }
68
69 pub const fn zero() -> Timespec {
70 unsafe { Self::new_unchecked(0, 0) }
71 }
72
73 const fn new(tv_sec: i64, tv_nsec: i64) -> Result<Timespec, io::Error> {
74 #[cfg(target_vendor = "apple")]
87 let (tv_sec, tv_nsec) =
88 if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) {
89 (tv_sec - 1, tv_nsec + 1_000_000_000)
90 } else {
91 (tv_sec, tv_nsec)
92 };
93 if tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64 {
94 Ok(unsafe { Self::new_unchecked(tv_sec, tv_nsec) })
95 } else {
96 Err(io::const_error!(io::ErrorKind::InvalidData, "invalid timestamp"))
97 }
98 }
99
100 pub fn now(clock: libc::clockid_t) -> Timespec {
101 use crate::mem::MaybeUninit;
102 use crate::sys::cvt;
103
104 #[cfg(all(
106 target_os = "linux",
107 target_env = "gnu",
108 target_pointer_width = "32",
109 not(target_arch = "riscv32")
110 ))]
111 {
112 use crate::sys::weak::weak;
113
114 weak!(
117 fn __clock_gettime64(
118 clockid: libc::clockid_t,
119 tp: *mut __timespec64,
120 ) -> libc::c_int;
121 );
122
123 if let Some(clock_gettime64) = __clock_gettime64.get() {
124 let mut t = MaybeUninit::uninit();
125 cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
126 let t = unsafe { t.assume_init() };
127 return Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap();
128 }
129 }
130
131 let mut t = MaybeUninit::uninit();
132 cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
133 let t = unsafe { t.assume_init() };
134 Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap()
135 }
136
137 pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
138 fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 {
140 debug_assert!(a >= b);
141 a.wrapping_sub(b).cast_unsigned()
142 }
143
144 if self >= other {
145 let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() {
146 (
147 sub_ge_to_unsigned(self.tv_sec, other.tv_sec),
148 self.tv_nsec.as_inner() - other.tv_nsec.as_inner(),
149 )
150 } else {
151 debug_assert!(self.tv_nsec < other.tv_nsec);
153 debug_assert!(self.tv_sec > other.tv_sec);
154 debug_assert!(self.tv_sec > i64::MIN);
155 (
156 sub_ge_to_unsigned(self.tv_sec - 1, other.tv_sec),
157 self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(),
158 )
159 };
160
161 Ok(Duration::new(secs, nsec))
162 } else {
163 match other.sub_timespec(self) {
164 Ok(d) => Err(d),
165 Err(d) => Ok(d),
166 }
167 }
168 }
169
170 pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
171 let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
172
173 let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner();
176 if nsec >= NSEC_PER_SEC as u32 {
177 nsec -= NSEC_PER_SEC as u32;
178 secs = secs.checked_add(1)?;
179 }
180 Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) })
181 }
182
183 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
184 let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
185
186 let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32;
188 if nsec < 0 {
189 nsec += NSEC_PER_SEC as i32;
190 secs = secs.checked_sub(1)?;
191 }
192 Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) })
193 }
194
195 #[allow(dead_code)]
196 pub fn to_timespec(&self) -> Option<libc::timespec> {
197 Some(libc::timespec {
198 tv_sec: self.tv_sec.try_into().ok()?,
199 tv_nsec: self.tv_nsec.as_inner().try_into().ok()?,
200 })
201 }
202
203 #[cfg(target_os = "nto")]
206 pub(in crate::sys) fn to_timespec_capped(&self) -> Option<libc::timespec> {
207 if (self.tv_nsec.as_inner() as u64)
209 .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?)
210 .is_none()
211 {
212 return None;
213 }
214 self.to_timespec()
215 }
216
217 #[cfg(all(
218 target_os = "linux",
219 target_env = "gnu",
220 target_pointer_width = "32",
221 not(target_arch = "riscv32")
222 ))]
223 pub fn to_timespec64(&self) -> __timespec64 {
224 __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _)
225 }
226}
227
228#[cfg(all(
229 target_os = "linux",
230 target_env = "gnu",
231 target_pointer_width = "32",
232 not(target_arch = "riscv32")
233))]
234#[repr(C)]
235pub(crate) struct __timespec64 {
236 pub(crate) tv_sec: i64,
237 #[cfg(target_endian = "big")]
238 _padding: i32,
239 pub(crate) tv_nsec: i32,
240 #[cfg(target_endian = "little")]
241 _padding: i32,
242}
243
244#[cfg(all(
245 target_os = "linux",
246 target_env = "gnu",
247 target_pointer_width = "32",
248 not(target_arch = "riscv32")
249))]
250impl __timespec64 {
251 pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self {
252 Self { tv_sec, tv_nsec, _padding: 0 }
253 }
254}
255
256#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
257pub struct Instant {
258 t: Timespec,
259}
260
261impl Instant {
262 #[cfg(target_vendor = "apple")]
263 pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
264 #[cfg(not(target_vendor = "apple"))]
265 pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC;
266 pub fn now() -> Instant {
267 Instant { t: Timespec::now(Self::CLOCK_ID) }
279 }
280
281 pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
282 self.t.sub_timespec(&other.t).ok()
283 }
284
285 pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
286 Some(Instant { t: self.t.checked_add_duration(other)? })
287 }
288
289 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
290 Some(Instant { t: self.t.checked_sub_duration(other)? })
291 }
292
293 #[cfg_attr(
294 not(target_os = "linux"),
295 allow(unused, reason = "needed by the `sleep_until` on some unix platforms")
296 )]
297 pub(crate) fn into_timespec(self) -> Timespec {
298 self.t
299 }
300}
301
302impl AsInner<Timespec> for Instant {
303 fn as_inner(&self) -> &Timespec {
304 &self.t
305 }
306}
307
308impl fmt::Debug for Instant {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 f.debug_struct("Instant")
311 .field("tv_sec", &self.t.tv_sec)
312 .field("tv_nsec", &self.t.tv_nsec)
313 .finish()
314 }
315}