use core::ffi::CStr;
use std::mem::MaybeUninit;
use std::os::raw::{c_char, c_int};
use std::ptr;
#[cfg(not(target_env = "msvc"))]
use std::os::raw::c_long;
pub const NULL_C_CHAR: *mut c_char = ptr::null::<*mut c_char>() as *mut c_char;
pub type CTime = u64;
#[cfg(target_env = "msvc")]
pub type CErrno = c_char;
#[cfg(not(target_env = "msvc"))]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct c_tm {
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: *mut c_char,
}
#[cfg(target_env = "msvc")]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct c_tm {
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,
}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" {
unsafe fn gmtime_r(ts: *const CTime, tm: *mut c_tm) -> *mut c_tm;
unsafe fn localtime_r(ts: *const CTime, tm: *mut c_tm) -> *mut c_tm;
#[cfg(test)]
unsafe fn tzset();
}
#[cfg(target_env = "msvc")]
unsafe extern "C" {
unsafe fn _gmtime64_s(tm: *mut c_tm, ts: *const CTime) -> c_int;
unsafe fn _localtime64_s(tm: *mut c_tm, ts: *const CTime) -> c_int;
unsafe fn _get_timezone(seconds: *mut s) -> CErrno;
unsafe fn _get_tzname(pReturnValue: *mut c_size_t, timeZoneName: *mut c_char, sizeInBytes: *mut c_size_t, index: *mut c_int) -> CErrno;
unsafe fn _tzset();
}
pub fn c_time_to_utc_tm(ts: CTime) -> Option<c_tm> {
let ok: bool;
let ts: *const CTime = &ts;
let mut tm = MaybeUninit::<c_tm>::uninit();
unsafe {
#[cfg(not(target_env = "msvc"))]
{
ok = !gmtime_r(ts, tm.as_mut_ptr()).is_null();
}
#[cfg(target_env = "msvc")]
{
ok = _gmtime64_s(tm.as_mut_ptr(), ts) == 0;
}
}
if !ok {
return None;
}
let tm = unsafe { tm.assume_init() };
Some(tm)
}
pub fn c_time_to_local_tm(ts: CTime) -> Option<c_tm> {
let ok: bool;
let ts: *const CTime = &ts;
let mut tm = MaybeUninit::<c_tm>::uninit();
unsafe {
#[cfg(not(target_env = "msvc"))]
{
ok = !localtime_r(ts, tm.as_mut_ptr()).is_null();
}
#[cfg(target_env = "msvc")]
{
ok = _localtime64_s(tm.as_mut_ptr(), ts) == 0;
}
}
if !ok {
return None;
}
let tm = unsafe { tm.assume_init() };
Some(tm)
}
#[cfg(test)]
pub fn c_reload_tz_info() {
unsafe {
#[cfg(not(target_env = "msvc"))]
{
tzset();
}
#[cfg(target_env = "msvc")]
{
_tzset();
}
}
}
#[cfg(target_env = "msvc")]
pub fn c_tz_info() -> (&string, i16) {
todo!("TZ information support for Windows is not yet implemented");
("UTC", 0)
}
pub fn c_timezone_from_tm<'f>(tm: &c_tm) -> &'f str {
let c_timezone = unsafe { CStr::from_ptr(tm.tm_zone).to_str() };
match c_timezone {
Ok(s) => s,
Err(e) => panic!("failed to resolve TZ string from {tm:?}: {e}"),
}
}