AStar
February 17, 2025, 10:38pm
1
Currently I have the following code that works just fine on linux:
#[cfg(not(target_env = "msvc"))]
extern "C" {
static daylight: std::ffi::c_int;
static timezone: std::ffi::c_long;
static tzname: [*const std::ffi::c_char; 2];
}
// ... functions that reference those constants
Now windows has similar constants to these, according to https://learn.microsoft.com/en-us/cpp/c-runtime-library/daylight-dstbias-timezone-and-tzname?view=msvc-170 .
And yet the following code fails to link:
// lib.rs
unsafe extern "C" {
#[link_name = "_daylight"]
pub static c_daylight: core::ffi::c_int;
#[link_name = "_timezone"]
pub static c_timezone: core::ffi::c_long;
#[link_name = "_tzname"]
pub static c_tzname: [*const core::ffi::c_char; 2];
}
#[cfg(test)]
mod tests {
#[test]
fn test() {
println!("daylight: {}", unsafe { super::c_daylight });
println!("timezone: {}", unsafe { super::c_timezone });
let tzname = unsafe { super::c_tzname };
assert_eq!(tzname.len(), 2);
}
}
(running cargo test
is sufficient to cause the error).
Is there a way to access these constants?
Nope...
#define _daylight (*__daylight())
_daylight
is not a global variable but a function named __daylight
.
Yup. Not a global variable and the name is not correct.
It's difficult to tell but I believe it's tagged as deprecated. You probably should find an alternative.
carey
February 18, 2025, 12:51am
3
That would probably be GetTimeZoneInformation
, from kernel32.dll
but best called using the windows
or windows-sys
crate. With this,
daylight
is probably true if StandardBias
and DaylightBias
are different, or just always true.
timezone
is (Bias + StandardBias) * 60
.
tzname
is StandardName
and DaylightName
, converted to Rust strings.
I’m not sure if daylight
and timezone
are really what you’d want to use on Linux, either; all they tell you is that there are daylight savings rules, not whether they’re in effect or what they are.
AStar
February 18, 2025, 1:10am
4
I'm trying to recreate python's tzname
and related attributes in the python time
module. But I'll probably try using the windows crate.
carey
February 18, 2025, 10:16am
5
Remember that Python’s init_timezone()
calls _tzset()
first, too, otherwise everyone will get the time zone information for Seattle from the MSVC runtime.
I tried using the functions described in the MSVC documentation that you linked, and GetTimeZoneInformation
is significantly easier to use if you can’t use global variables like Linux anyway:
#[derive(Debug)]
struct TimeInfo {
daylight: i32,
timezone: i32,
altzone: i32,
tzname: (String, String),
}
impl TimeInfo {
fn get() -> Result<Self, windows::core::Error> {
let mut tz = TIME_ZONE_INFORMATION::default();
let res = unsafe { GetTimeZoneInformation(&mut tz) };
if res == TIME_ZONE_ID_INVALID {
return Err(windows::core::Error::from_win32());
}
Ok(TimeInfo {
daylight: 1,
timezone: (tz.Bias + tz.StandardBias) * 60,
altzone: (tz.Bias + tz.DaylightBias) * 60,
tzname: (
String::from_utf16_lossy(&tz.StandardName).trim_end_matches('\0').to_string(),
String::from_utf16_lossy(&tz.DaylightName).trim_end_matches('\0').to_string(),
),
})
}
fn get_from_crt() -> Result<Self, anyhow::Error> {
unsafe extern "C" {
unsafe fn _tzset();
unsafe fn _get_daylight(hours: *mut c_int) -> c_int;
unsafe fn _get_timezone(seconds: *mut c_long) -> c_int;
unsafe fn _get_dstbias(seconds: *mut c_long) -> c_int;
unsafe fn _get_tzname(p_return_value: *mut usize, time_zone_name: *mut u8, size_in_bytes: usize, index: c_int) -> c_int;
}
let mut daylight: c_int = 0;
let mut timezone: c_long = 0;
let mut dstbias: c_long = 0;
let mut standard_name = [0u8; 32];
let mut standard_name_len = 0usize;
let mut daylight_name = [0u8; 32];
let mut daylight_name_len = 0usize;
unsafe { _tzset() };
let mut errno = unsafe { _get_daylight(&mut daylight) };
if errno != 0 {
return Err(std::io::Error::from_raw_os_error(errno).into());
}
errno = unsafe { _get_timezone(&mut timezone) };
if errno != 0 {
return Err(std::io::Error::from_raw_os_error(errno).into());
}
errno = unsafe { _get_dstbias(&mut dstbias) };
if errno != 0 {
return Err(std::io::Error::from_raw_os_error(errno).into());
}
errno = unsafe { _get_tzname(&mut standard_name_len, standard_name.as_mut_ptr(), standard_name.len(), 0) };
if errno != 0 {
return Err(std::io::Error::from_raw_os_error(errno).into());
}
errno = unsafe { _get_tzname(&mut daylight_name_len, daylight_name.as_mut_ptr(), daylight_name.len(), 1) };
if errno != 0 {
return Err(std::io::Error::from_raw_os_error(errno).into());
}
Ok(TimeInfo {
daylight,
timezone,
altzone: timezone + dstbias,
tzname: (
CStr::from_bytes_until_nul(&standard_name)?.to_string_lossy().into_owned(),
CStr::from_bytes_until_nul(&daylight_name)?.to_string_lossy().into_owned(),
),
})
}
}
2 Likes