1use crate::{
6 c_str::{CStr, CString},
7 error::{Errno, ResultExt},
8 header::{
9 errno::{EFAULT, ENOMEM, EOVERFLOW, ETIMEDOUT},
10 signal::sigevent,
11 stdlib::getenv,
12 unistd::readlink,
13 },
14 out::Out,
15 platform::{
16 self, Pal, Sys,
17 types::{
18 c_char, c_double, c_int, c_long, clock_t, clockid_t, pid_t, size_t, time_t, timer_t,
19 },
20 },
21 raw_cell::RawCell,
22 sync::{Mutex, MutexGuard},
23};
24use alloc::collections::BTreeSet;
25use chrono::{
26 DateTime, Datelike, NaiveDate, NaiveDateTime, Offset, TimeZone, Timelike, Utc,
27 offset::MappedLocalTime,
28};
29use chrono_tz::{OffsetComponents, OffsetName, Tz};
30use core::{cell::OnceCell, convert::TryFrom, mem, ptr};
31
32pub use crate::header::bits_timespec::timespec;
33
34pub use self::constants::*;
35
36pub mod constants;
37
38mod strftime;
39mod strptime;
40pub use strptime::strptime;
41
42const YEARS_PER_ERA: time_t = 400;
44const DAYS_PER_ERA: time_t = 146097;
46const SECS_PER_DAY: time_t = 24 * 60 * 60;
48pub(crate) const NANOSECONDS: c_long = 1_000_000_000;
50const UTC_STR: &core::ffi::CStr = c"UTC";
52
53#[repr(C)]
55#[derive(Clone)]
56#[cfg(target_os = "redox")]
57pub(crate) struct timer_internal_t {
58 pub clockid: clockid_t,
59 pub timerfd: usize,
60 pub eventfd: usize,
61 pub evp: sigevent,
62 pub thread: platform::types::pthread_t,
63 pub next_wake_time: itimerspec,
65 pub next_wake_version: usize,
67 pub process_pid: platform::types::pid_t,
70}
71
72#[cfg(target_os = "redox")]
73impl timer_internal_t {
74 pub unsafe fn from_raw(timerid: timer_t) -> &'static mut Self {
75 unsafe { &mut *(timerid as *mut Self) }
76 }
77}
78
79#[allow(non_camel_case_types)]
81#[repr(C)]
82pub struct tm {
83 pub tm_sec: c_int, pub tm_min: c_int, pub tm_hour: c_int, pub tm_mday: c_int, pub tm_mon: c_int, pub tm_year: c_int, pub tm_wday: c_int, pub tm_yday: c_int, pub tm_isdst: c_int, pub tm_gmtoff: c_long, pub tm_zone: *const c_char, }
95
96unsafe impl Sync for tm {}
97
98static GMTIME_LOCALTIME_RETURN_TM: RawCell<tm> = RawCell::new(blank_tm());
101
102static mut ASCTIME: [c_char; 26] = [0; 26];
105
106#[repr(transparent)]
107pub struct TzName([*mut c_char; 2]);
108
109unsafe impl Sync for TzName {}
110
111static TIMEZONE_NAMES: Mutex<OnceCell<BTreeSet<CString>>> = Mutex::new(OnceCell::new());
114
115static TIMEZONE_LOCK: Mutex<(Option<CString>, Option<CString>)> = Mutex::new((None, None));
120
121#[allow(non_upper_case_globals)]
124#[unsafe(no_mangle)]
125pub static mut daylight: c_int = 0;
126
127#[allow(non_upper_case_globals)]
130#[unsafe(no_mangle)]
131pub static mut timezone: c_long = 0;
132
133#[allow(non_upper_case_globals)]
136#[unsafe(no_mangle)]
137pub static mut tzname: TzName = TzName([ptr::null_mut(); 2]);
138
139#[allow(non_upper_case_globals)]
140#[unsafe(no_mangle)]
141pub static mut getdate_err: c_int = 0;
142
143#[allow(non_camel_case_types)]
145#[repr(C)]
146#[derive(Clone, Default)]
147pub struct itimerspec {
148 pub it_interval: timespec,
149 pub it_value: timespec,
150}
151
152#[deprecated]
158#[unsafe(no_mangle)]
159pub unsafe extern "C" fn asctime(timeptr: *const tm) -> *mut c_char {
160 unsafe {
161 #[allow(deprecated)]
162 asctime_r(timeptr, (&raw mut ASCTIME).cast())
163 }
164}
165
166#[deprecated]
172#[unsafe(no_mangle)]
173pub unsafe extern "C" fn asctime_r(tm: *const tm, buf: *mut c_char) -> *mut c_char {
174 let tm_sec = unsafe { (*tm).tm_sec };
175 let tm_min = unsafe { (*tm).tm_min };
176 let tm_hour = unsafe { (*tm).tm_hour };
177 let tm_mday = unsafe { (*tm).tm_mday };
178 let tm_mon = unsafe { (*tm).tm_mon };
179 let tm_year = unsafe { (*tm).tm_year };
180 let tm_wday = unsafe { (*tm).tm_wday };
181
182 const OUT_OF_RANGE_MESSAGE: &str = "tm member out of range";
211
212 assert!((0..=99).contains(&tm_sec), "{OUT_OF_RANGE_MESSAGE}");
213 assert!((0..=99).contains(&tm_min), "{OUT_OF_RANGE_MESSAGE}");
214 assert!((0..=99).contains(&tm_hour), "{OUT_OF_RANGE_MESSAGE}");
215 assert!((-99..=999).contains(&tm_mday), "{OUT_OF_RANGE_MESSAGE}");
216 assert!((0..=11).contains(&tm_mon), "{OUT_OF_RANGE_MESSAGE}");
217 assert!(
218 (-999 - 1900..=9999 - 1900).contains(&tm_year),
219 "{OUT_OF_RANGE_MESSAGE}"
220 );
221 assert!((0..=6).contains(&tm_wday), "{OUT_OF_RANGE_MESSAGE}");
222
223 let write_result = core::fmt::write(
225 &mut platform::UnsafeStringWriter(buf.cast()),
227 format_args!(
228 "{:.3} {:.3}{:3} {:02}:{:02}:{:02} {}\n",
229 DAY_NAMES[usize::try_from(tm_wday).unwrap()],
230 MON_NAMES[usize::try_from(tm_mon).unwrap()],
231 tm_mday,
232 tm_hour,
233 tm_min,
234 tm_sec,
235 1900 + tm_year
236 ),
237 );
238 match write_result {
239 Ok(()) => buf,
240 Err(_) => {
241 ptr::null_mut()
244 }
245 }
246}
247
248#[unsafe(no_mangle)]
250pub extern "C" fn clock() -> clock_t {
251 let mut ts = mem::MaybeUninit::<timespec>::uninit();
252
253 if unsafe { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts.as_mut_ptr()) } != 0 {
254 return -1;
255 }
256 let ts = unsafe { ts.assume_init() };
257
258 #[expect(clippy::unnecessary_cast, reason = "needed on i586")]
259 let clocks =
260 ts.tv_sec * CLOCKS_PER_SEC as i64 + (ts.tv_nsec / (1_000_000_000 / CLOCKS_PER_SEC)) as i64;
261 clock_t::try_from(clocks).unwrap_or(-1)
262}
263
264pub extern "C" fn clock_getcpuclockid(pid: pid_t, clock_id: *mut clockid_t) -> c_int {
267 unimplemented!();
268}
269
270#[unsafe(no_mangle)]
272pub unsafe extern "C" fn clock_getres(clock_id: clockid_t, res: *mut timespec) -> c_int {
273 Sys::clock_getres(clock_id, unsafe { Out::nullable(res) })
274 .map(|()| 0)
275 .or_minus_one_errno()
276}
277
278#[unsafe(no_mangle)]
280pub unsafe extern "C" fn clock_gettime(clock_id: clockid_t, tp: *mut timespec) -> c_int {
281 Sys::clock_gettime(clock_id, unsafe { Out::nonnull(tp) })
282 .map(|()| 0)
283 .or_minus_one_errno()
284}
285
286pub extern "C" fn clock_nanosleep(
289 clock_id: clockid_t,
290 flags: c_int,
291 rqtp: *const timespec,
292 rmtp: *mut timespec,
293) -> c_int {
294 unimplemented!();
295}
296
297#[unsafe(no_mangle)]
299pub unsafe extern "C" fn clock_settime(clock_id: clockid_t, tp: *const timespec) -> c_int {
300 unsafe { Sys::clock_settime(clock_id, tp) }
301 .map(|()| 0)
302 .or_minus_one_errno()
303}
304
305#[deprecated]
311#[unsafe(no_mangle)]
312pub unsafe extern "C" fn ctime(clock: *const time_t) -> *mut c_char {
313 unsafe {
314 #[allow(deprecated)]
315 asctime(localtime(clock))
316 }
317}
318
319#[deprecated]
325#[unsafe(no_mangle)]
326pub unsafe extern "C" fn ctime_r(clock: *const time_t, buf: *mut c_char) -> *mut c_char {
327 let mut tm1 = blank_tm();
329 unsafe { localtime_r(clock, &raw mut tm1) };
330 unsafe {
331 #[allow(deprecated)]
332 asctime_r(&raw const tm1, buf)
333 }
334}
335
336#[unsafe(no_mangle)]
338pub unsafe extern "C" fn difftime(time1: time_t, time0: time_t) -> c_double {
339 (time1 - time0) as _
340}
341
342pub unsafe extern "C" fn getdate(string: *const c_char) -> *const tm {
345 unimplemented!();
346}
347
348#[unsafe(no_mangle)]
357pub unsafe extern "C" fn gmtime(timer: *const time_t) -> *mut tm {
358 unsafe { gmtime_r(timer, GMTIME_LOCALTIME_RETURN_TM.as_mut_ptr()) }
362}
363
364#[unsafe(no_mangle)]
371pub unsafe extern "C" fn gmtime_r(timer: *const time_t, result: *mut tm) -> *mut tm {
372 let timer_val = unsafe { *timer };
374
375 let result_out = unsafe { Out::nonnull(result) };
378
379 let _ = get_localtime(timer_val, result_out);
380 result
381}
382
383#[unsafe(no_mangle)]
394pub unsafe extern "C" fn localtime(timer: *const time_t) -> *mut tm {
395 unsafe { localtime_r(timer, GMTIME_LOCALTIME_RETURN_TM.as_mut_ptr()) }
399}
400
401#[unsafe(no_mangle)]
410pub unsafe extern "C" fn localtime_r(timer: *const time_t, result: *mut tm) -> *mut tm {
411 let timer_val = unsafe { *timer };
413
414 let result_out = unsafe { Out::nonnull(result) };
417
418 let mut lock = TIMEZONE_LOCK.lock();
419
420 unsafe { clear_timezone(&mut lock) };
423 if let (Some(std_time), dst_time) = get_localtime(timer_val, result_out) {
424 unsafe { set_timezone(&mut lock, &std_time, dst_time) };
427 }
428 result
429}
430
431#[unsafe(no_mangle)]
433pub unsafe extern "C" fn mktime(timeptr: *mut tm) -> time_t {
434 let mut lock = TIMEZONE_LOCK.lock();
435 unsafe { clear_timezone(&mut lock) };
436
437 let year = unsafe { (*timeptr).tm_year } + 1900;
438 let month = (unsafe { (*timeptr).tm_mon } + 1) as _;
439 let day = unsafe { (*timeptr).tm_mday } as _;
440 let hour = unsafe { (*timeptr).tm_hour } as _;
441 let minute = unsafe { (*timeptr).tm_min } as _;
442 let second = unsafe { (*timeptr).tm_sec } as _;
443
444 let naive_local = match NaiveDate::from_ymd_opt(year, month, day)
445 .and_then(|date| date.and_hms_opt(hour, minute, second))
446 {
447 Some(datetime) => datetime,
448 None => {
449 platform::ERRNO.set(EOVERFLOW);
450 return -1;
451 }
452 };
453
454 let tz = time_zone();
455 let isdst = unsafe { (*timeptr).tm_isdst };
456 let tz_datetime = match tz.from_local_datetime(&naive_local) {
457 MappedLocalTime::Single(datetime) => datetime,
458 MappedLocalTime::Ambiguous(early, late) => {
459 if isdst > 0 {
460 early
461 } else {
462 late
463 }
464 }
465 MappedLocalTime::None => {
466 platform::ERRNO.set(EOVERFLOW);
467 return -1;
468 }
469 };
470 let timestamp = tz_datetime.timestamp();
471
472 unsafe { ptr::write(timeptr, datetime_to_tm(&tz_datetime)) };
473
474 let (std_time, dst_time) = match tz.timestamp_opt(timestamp, 0) {
476 MappedLocalTime::Single(t) => (t, None),
477 MappedLocalTime::Ambiguous(t1, t2) => (t2, Some(t1)),
479 MappedLocalTime::None => return timestamp,
480 };
481 {
482 unsafe { set_timezone(&mut lock, &std_time, dst_time) };
483 }
484
485 timestamp
486}
487
488#[unsafe(no_mangle)]
490pub unsafe extern "C" fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int {
491 unsafe { Sys::nanosleep(rqtp, rmtp) }
492 .map(|()| 0)
493 .or_minus_one_errno()
494}
495
496#[unsafe(no_mangle)]
498pub unsafe extern "C" fn strftime(
499 s: *mut c_char,
500 maxsize: size_t,
501 format: *const c_char,
502 timeptr: *const tm,
503) -> size_t {
504 let mut w = platform::StringWriter(s, maxsize);
505 let ret = unsafe { strftime::strftime(&mut w, format, timeptr) };
506 if ret < maxsize { ret } else { 0 }
507}
508
509#[unsafe(no_mangle)]
518pub unsafe extern "C" fn time(tloc: *mut time_t) -> time_t {
519 let mut ts = timespec::default();
520 if Sys::clock_gettime(CLOCK_REALTIME, Out::from_mut(&mut ts)).is_ok() {}; if !tloc.is_null() {
522 unsafe { *tloc = ts.tv_sec }
523 };
524 ts.tv_sec
525}
526
527#[unsafe(no_mangle)]
529pub unsafe extern "C" fn timegm(tm: *mut tm) -> time_t {
530 let tm_val = unsafe { &mut *tm };
531 let dt = match convert_tm_generic(&Utc, tm_val) {
532 Some(dt) => dt,
533 None => return -1,
534 };
535
536 unsafe {
537 (*tm).tm_wday = dt.weekday().num_days_from_sunday() as _;
538 (*tm).tm_yday = dt.ordinal0() as _; (*tm).tm_isdst = 0; (*tm).tm_gmtoff = 0; (*tm).tm_zone = UTC_STR.as_ptr().cast::<c_char>();
542 }
543
544 dt.timestamp()
545}
546
547#[deprecated]
549#[unsafe(no_mangle)]
550pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t {
551 let tm_val = unsafe { &mut *tm };
552 let tz = time_zone();
553 let dt = match convert_tm_generic(&tz, tm_val) {
554 Some(dt) => dt,
555 None => return -1,
556 };
557
558 let tz_name = CString::new(tz.name()).unwrap();
559 unsafe {
560 (*tm).tm_wday = dt.weekday().num_days_from_sunday() as _;
561 (*tm).tm_yday = dt.ordinal0() as _; (*tm).tm_isdst = dt.offset().dst_offset().num_hours() as _;
563 (*tm).tm_gmtoff = dt.offset().fix().local_minus_utc().into();
564 (*tm).tm_zone = tz_name.into_raw().cast();
565 }
566
567 dt.timestamp()
568}
569
570#[unsafe(no_mangle)]
572pub unsafe extern "C" fn timer_create(
573 clock_id: clockid_t,
574 evp: *mut sigevent,
575 timerid: *mut timer_t,
576) -> c_int {
577 if evp.is_null() || timerid.is_null() {
578 return Err(Errno(EFAULT)).or_minus_one_errno();
579 }
580 let (evp, timerid) = unsafe { (&*evp, Out::nonnull(timerid)) };
581 Sys::timer_create(clock_id, evp, timerid)
582 .map(|()| 0)
583 .or_minus_one_errno()
584}
585
586#[unsafe(no_mangle)]
588pub unsafe extern "C" fn timer_delete(timerid: timer_t) -> c_int {
589 Sys::timer_delete(timerid).map(|()| 0).or_minus_one_errno()
590}
591
592pub extern "C" fn timer_getoverrun(timerid: timer_t) -> c_int {
595 unimplemented!();
596}
597
598#[unsafe(no_mangle)]
600pub unsafe extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_int {
601 if value.is_null() {
602 return Err(Errno(EFAULT)).or_minus_one_errno();
603 }
604 let value = unsafe { Out::nonnull(value) };
605 Sys::timer_gettime(timerid, value)
606 .map(|()| 0)
607 .or_minus_one_errno()
608}
609
610#[unsafe(no_mangle)]
612pub unsafe extern "C" fn timer_settime(
613 timerid: timer_t,
614 flags: c_int,
615 value: *const itimerspec,
616 ovalue: *mut itimerspec,
617) -> c_int {
618 if value.is_null() {
619 return Err(Errno(EFAULT)).or_minus_one_errno();
620 }
621 let (value, ovalue) = unsafe { (&*value, Out::nullable(ovalue)) };
622 Sys::timer_settime(timerid, flags, value, ovalue)
623 .map(|()| 0)
624 .or_minus_one_errno()
625}
626
627#[unsafe(no_mangle)]
635pub unsafe extern "C" fn timespec_get(tp: *mut timespec, base: c_int) -> c_int {
636 let tp = unsafe { Out::nonnull(tp) };
637 Sys::clock_gettime(base - 1, tp).map(|()| base).unwrap_or(0)
638}
639
640#[unsafe(no_mangle)]
646pub unsafe extern "C" fn timespec_getres(res: *mut timespec, base: c_int) -> c_int {
647 let res = unsafe { Out::nullable(res) };
648 Sys::clock_getres(base - 1, res).map(|()| base).unwrap_or(0)
649}
650
651#[unsafe(no_mangle)]
657pub unsafe extern "C" fn tzset() {
658 let mut lock = TIMEZONE_LOCK.lock();
659 unsafe { clear_timezone(&mut lock) };
662
663 let tz = time_zone();
664 let datetime = now();
665 let (std_time, dst_time) = match tz.from_local_datetime(&datetime) {
666 MappedLocalTime::Single(t) => (t, None),
667 MappedLocalTime::Ambiguous(t1, t2) => (t2, Some(t1)),
669 MappedLocalTime::None => return,
670 };
671
672 unsafe { set_timezone(&mut lock, &std_time, dst_time) }
675}
676
677fn convert_tm_generic<Tz: TimeZone>(tz: &Tz, tm_val: &tm) -> Option<DateTime<Tz>> {
678 let year = tm_val.tm_year + 1900;
680 let month = tm_val.tm_mon + 1; let day = tm_val.tm_mday;
682 let hour = tm_val.tm_hour;
683 let minute = tm_val.tm_min;
684 let second = tm_val.tm_sec;
685
686 match tz.with_ymd_and_hms(
687 year,
688 month as u32,
689 day as u32,
690 hour as u32,
691 minute as u32,
692 second as u32,
693 ) {
694 MappedLocalTime::Single(dt) => Some(dt),
695 MappedLocalTime::Ambiguous(dt1, _dt2) => Some(dt1), _ => None,
697 }
698}
699
700unsafe fn clear_timezone(guard: &mut MutexGuard<'_, (Option<CString>, Option<CString>)>) {
705 guard.0 = None;
706 guard.1 = None;
707
708 unsafe {
711 tzname.0[0] = ptr::null_mut();
712 tzname.0[1] = ptr::null_mut();
713 timezone = 0;
714 daylight = 0;
715 }
716}
717
718#[inline(always)]
719fn get_system_time_zone<'a>() -> Option<&'a str> {
720 const BSIZE: size_t = 100;
722 let mut buffer: [u8; BSIZE] = [0; BSIZE];
723
724 #[cfg(not(target_os = "redox"))]
725 let (localtime, prefix) = (c"/etc/localtime", "/usr/share/zoneinfo/");
726
727 #[cfg(target_os = "redox")]
728 let (localtime, prefix) = (c"/etc/localtime", "/usr/share/zoneinfo/");
729
730 if unsafe { readlink(localtime.as_ptr().cast(), buffer.as_mut_ptr().cast(), BSIZE) } == -1 {
731 return None;
732 }
733
734 let path = unsafe { CStr::from_ptr(buffer.as_mut_ptr().cast()) };
735
736 if let Ok(tz_name) = path.to_str()
737 && let Some(stripped) = tz_name.strip_prefix(prefix)
738 {
739 return Some(stripped);
740 }
741
742 None
743}
744
745fn get_current_time_zone<'a>() -> &'a str {
746 let tz_env = unsafe { getenv(c"TZ".as_ptr().cast()) };
748 if !tz_env.is_null()
749 && let Ok(tz) = unsafe { CStr::from_ptr(tz_env) }.to_str()
750 {
751 return tz;
752 }
753
754 if let Some(tz) = get_system_time_zone() {
756 return tz;
757 }
758
759 "UTC"
761}
762
763#[inline(always)]
764fn time_zone() -> Tz {
765 get_current_time_zone().parse().unwrap_or(Tz::UTC)
766}
767
768#[inline(always)]
769fn now() -> NaiveDateTime {
770 let mut now = timespec::default();
771 if Sys::clock_gettime(CLOCK_REALTIME, Out::from_mut(&mut now)).is_ok() {}; DateTime::from_timestamp(now.tv_sec, now.tv_nsec as _)
773 .unwrap_or_default()
774 .naive_local()
775}
776
777#[inline(always)]
778fn get_localtime(
779 timer: time_t,
780 mut result: Out<tm>,
781) -> (Option<DateTime<Tz>>, Option<DateTime<Tz>>) {
782 let tz = time_zone();
783
784 let (std_time, dst_time) = match tz.timestamp_opt(timer, 0) {
786 MappedLocalTime::Single(t) => (Some(t), None),
787 MappedLocalTime::Ambiguous(t1, t2) => (Some(t2), Some(t1)),
789 MappedLocalTime::None => return (None, None),
790 };
791
792 let localtime = datetime_to_tm(&std_time.unwrap());
793 result.write(localtime);
794 (std_time, dst_time)
795}
796
797fn datetime_to_tm(local_time: &DateTime<Tz>) -> tm {
798 let tz = local_time.timezone().name();
799 let tz = tz.strip_prefix("Etc/").unwrap_or(tz);
800
801 let mut t = blank_tm();
802 t.tm_sec = local_time.second() as _;
804 t.tm_min = local_time.minute() as _;
805 t.tm_hour = local_time.hour() as _;
806 t.tm_mday = local_time.day() as _;
807 t.tm_mon = local_time.month0() as _; t.tm_year = (local_time.year() - 1900) as _; t.tm_wday = local_time.weekday().num_days_from_sunday() as _;
810 t.tm_yday = local_time.ordinal0() as _; let offset = local_time.offset();
813 t.tm_isdst = offset.dst_offset().num_hours() as _;
814 t.tm_gmtoff = offset.fix().local_minus_utc().into();
816
817 let tm_zone = {
818 let mut timezone_names = TIMEZONE_NAMES.lock();
819 timezone_names.get_or_init(BTreeSet::new);
820 let cstr = CString::new(tz).unwrap();
821 timezone_names.get_mut().unwrap().insert(cstr.clone());
822 timezone_names.get().unwrap().get(&cstr).unwrap().as_ptr()
823 };
824
825 t.tm_zone = tm_zone.cast();
826 t
827}
828
829unsafe fn set_timezone(
834 guard: &mut MutexGuard<'_, (Option<CString>, Option<CString>)>,
835 std: &DateTime<Tz>,
836 dst: Option<DateTime<Tz>>,
837) {
838 unsafe {
841 let ut_offset = std.offset();
842
843 guard.0 = Some(CString::new(ut_offset.abbreviation().expect("Wrong timezone")).unwrap());
844 tzname.0[0] = guard.0.as_ref().unwrap().as_ptr().cast_mut();
845
846 match dst {
847 Some(dst) => {
848 guard.1 = Some(
849 CString::new(dst.offset().abbreviation().expect("Wrong timezone")).unwrap(),
850 );
851 tzname.0[1] = guard.1.as_ref().unwrap().as_ptr().cast_mut();
852 daylight = 1;
853 }
854 None => {
855 guard.1 = None;
856 tzname.0[1] = guard.0.as_ref().unwrap().as_ptr().cast_mut();
857 daylight = 0;
858 }
859 }
860
861 timezone = -c_long::from(ut_offset.fix().local_minus_utc());
862 }
863}
864
865const fn blank_tm() -> tm {
866 tm {
867 tm_year: 0,
868 tm_mon: 0,
869 tm_mday: 0,
870 tm_hour: 0,
871 tm_min: 0,
872 tm_sec: 0,
873 tm_wday: 0,
874 tm_yday: 0,
875 tm_isdst: -1,
876 tm_gmtoff: 0,
877 tm_zone: ptr::null_mut(),
878 }
879}
880
881pub(crate) fn timespec_realtime_to_monotonic(abstime: ×pec) -> Result<timespec, Errno> {
882 let mut realtime = timespec::default();
883 unsafe { clock_gettime(CLOCK_REALTIME, &raw mut realtime) };
884 let mut monotonic = timespec::default();
885 unsafe { clock_gettime(CLOCK_MONOTONIC, &raw mut monotonic) };
886 let Some(delta) = timespec::subtract(abstime, &realtime) else {
887 return Err(Errno(ETIMEDOUT));
888 };
889 let Some(relative) = timespec::add(&monotonic, &delta) else {
890 return Err(Errno(ENOMEM));
891 };
892 Ok(relative)
893}