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