1use core::num::niche_types::Nanoseconds;
2
3use crate::sys::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 pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
34
35 pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
36
37 #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))]
38 pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
39 Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? })
40 }
41
42 pub fn now() -> SystemTime {
43 SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
44 }
45
46 pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
47 self.t.sub_timespec(&other.t)
48 }
49
50 pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
51 Some(SystemTime { t: self.t.checked_add_duration(other)? })
52 }
53
54 pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
55 Some(SystemTime { t: self.t.checked_sub_duration(other)? })
56 }
57}
58
59impl fmt::Debug for SystemTime {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.debug_struct("SystemTime")
62 .field("tv_sec", &self.t.tv_sec)
63 .field("tv_nsec", &self.t.tv_nsec)
64 .finish()
65 }
66}
67
68impl Timespec {
69 const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
70
71 const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
75
76 const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
77 Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
78 }
79
80 pub const fn zero() -> Timespec {
81 unsafe { Self::new_unchecked(0, 0) }
82 }
83
84 const fn new(tv_sec: i64, tv_nsec: i64) -> Result<Timespec, io::Error> {
85 #[cfg(target_vendor = "apple")]
98 let (tv_sec, tv_nsec) =
99 if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) {
100 (tv_sec - 1, tv_nsec + 1_000_000_000)
101 } else {
102 (tv_sec, tv_nsec)
103 };
104 if tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64 {
105 Ok(unsafe { Self::new_unchecked(tv_sec, tv_nsec) })
106 } else {
107 Err(io::const_error!(io::ErrorKind::InvalidData, "invalid timestamp"))
108 }
109 }
110
111 pub fn now(clock: libc::clockid_t) -> Timespec {
112 use crate::mem::MaybeUninit;
113 use crate::sys::cvt;
114
115 #[cfg(all(
117 target_os = "linux",
118 target_env = "gnu",
119 target_pointer_width = "32",
120 not(target_arch = "riscv32")
121 ))]
122 {
123 use crate::sys::weak::weak;
124
125 weak!(
128 fn __clock_gettime64(
129 clockid: libc::clockid_t,
130 tp: *mut __timespec64,
131 ) -> libc::c_int;
132 );
133
134 if let Some(clock_gettime64) = __clock_gettime64.get() {
135 let mut t = MaybeUninit::uninit();
136 cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
137 let t = unsafe { t.assume_init() };
138 return Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap();
139 }
140 }
141
142 let mut t = MaybeUninit::uninit();
143 cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
144 let t = unsafe { t.assume_init() };
145 Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap()
146 }
147
148 pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
149 fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 {
151 debug_assert!(a >= b);
152 a.wrapping_sub(b).cast_unsigned()
153 }
154
155 if self >= other {
156 let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() {
157 (
158 sub_ge_to_unsigned(self.tv_sec, other.tv_sec),
159 self.tv_nsec.as_inner() - other.tv_nsec.as_inner(),
160 )
161 } else {
162 debug_assert!(self.tv_nsec < other.tv_nsec);
164 debug_assert!(self.tv_sec > other.tv_sec);
165 debug_assert!(self.tv_sec > i64::MIN);
166 (
167 sub_ge_to_unsigned(self.tv_sec - 1, other.tv_sec),
168 self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(),
169 )
170 };
171
172 Ok(Duration::new(secs, nsec))
173 } else {
174 match other.sub_timespec(self) {
175 Ok(d) => Err(d),
176 Err(d) => Ok(d),
177 }
178 }
179 }
180
181 pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
182 let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
183
184 let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner();
187 if nsec >= NSEC_PER_SEC as u32 {
188 nsec -= NSEC_PER_SEC as u32;
189 secs = secs.checked_add(1)?;
190 }
191 Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) })
192 }
193
194 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
195 let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
196
197 let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32;
199 if nsec < 0 {
200 nsec += NSEC_PER_SEC as i32;
201 secs = secs.checked_sub(1)?;
202 }
203 Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) })
204 }
205
206 #[allow(dead_code)]
207 pub fn to_timespec(&self) -> Option<libc::timespec> {
208 Some(libc::timespec {
209 tv_sec: self.tv_sec.try_into().ok()?,
210 tv_nsec: self.tv_nsec.as_inner().try_into().ok()?,
211 })
212 }
213
214 #[cfg(target_os = "nto")]
217 pub(in crate::sys) fn to_timespec_capped(&self) -> Option<libc::timespec> {
218 if (self.tv_nsec.as_inner() as u64)
220 .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?)
221 .is_none()
222 {
223 return None;
224 }
225 self.to_timespec()
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 pub fn to_timespec64(&self) -> __timespec64 {
235 __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _)
236 }
237}
238
239#[cfg(all(
240 target_os = "linux",
241 target_env = "gnu",
242 target_pointer_width = "32",
243 not(target_arch = "riscv32")
244))]
245#[repr(C)]
246pub(crate) struct __timespec64 {
247 pub(crate) tv_sec: i64,
248 #[cfg(target_endian = "big")]
249 _padding: i32,
250 pub(crate) tv_nsec: i32,
251 #[cfg(target_endian = "little")]
252 _padding: i32,
253}
254
255#[cfg(all(
256 target_os = "linux",
257 target_env = "gnu",
258 target_pointer_width = "32",
259 not(target_arch = "riscv32")
260))]
261impl __timespec64 {
262 pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self {
263 Self { tv_sec, tv_nsec, _padding: 0 }
264 }
265}
266
267#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
268pub struct Instant {
269 t: Timespec,
270}
271
272impl Instant {
273 #[cfg(target_vendor = "apple")]
274 pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
275 #[cfg(not(target_vendor = "apple"))]
276 pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC;
277 pub fn now() -> Instant {
278 Instant { t: Timespec::now(Self::CLOCK_ID) }
290 }
291
292 pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
293 self.t.sub_timespec(&other.t).ok()
294 }
295
296 pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
297 Some(Instant { t: self.t.checked_add_duration(other)? })
298 }
299
300 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
301 Some(Instant { t: self.t.checked_sub_duration(other)? })
302 }
303
304 #[cfg_attr(
305 not(target_os = "linux"),
306 allow(unused, reason = "needed by the `sleep_until` on some unix platforms")
307 )]
308 pub(crate) fn into_timespec(self) -> Timespec {
309 self.t
310 }
311}
312
313impl AsInner<Timespec> for Instant {
314 fn as_inner(&self) -> &Timespec {
315 &self.t
316 }
317}
318
319impl fmt::Debug for Instant {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 f.debug_struct("Instant")
322 .field("tv_sec", &self.t.tv_sec)
323 .field("tv_nsec", &self.t.tv_nsec)
324 .finish()
325 }
326}