diff options
Diffstat (limited to 'third_party/rust/time-0.1.45/src/sys.rs')
-rw-r--r-- | third_party/rust/time-0.1.45/src/sys.rs | 996 |
1 files changed, 996 insertions, 0 deletions
diff --git a/third_party/rust/time-0.1.45/src/sys.rs b/third_party/rust/time-0.1.45/src/sys.rs new file mode 100644 index 0000000000..c5765ca895 --- /dev/null +++ b/third_party/rust/time-0.1.45/src/sys.rs @@ -0,0 +1,996 @@ +#![allow(bad_style)] + +pub use self::inner::*; + +#[cfg(any( + all(target_arch = "wasm32", not(target_os = "emscripten")), + target_env = "sgx" +))] +mod common { + use Tm; + + pub fn time_to_tm(ts: i64, tm: &mut Tm) { + let leapyear = |year| -> bool { + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) + }; + + static _ytab: [[i64; 12]; 2] = [ + [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ], + [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ] + ]; + + let mut year = 1970; + + let dayclock = ts % 86400; + let mut dayno = ts / 86400; + + tm.tm_sec = (dayclock % 60) as i32; + tm.tm_min = ((dayclock % 3600) / 60) as i32; + tm.tm_hour = (dayclock / 3600) as i32; + tm.tm_wday = ((dayno + 4) % 7) as i32; + loop { + let yearsize = if leapyear(year) { + 366 + } else { + 365 + }; + if dayno >= yearsize { + dayno -= yearsize; + year += 1; + } else { + break; + } + } + tm.tm_year = (year - 1900) as i32; + tm.tm_yday = dayno as i32; + let mut mon = 0; + while dayno >= _ytab[if leapyear(year) { 1 } else { 0 }][mon] { + dayno -= _ytab[if leapyear(year) { 1 } else { 0 }][mon]; + mon += 1; + } + tm.tm_mon = mon as i32; + tm.tm_mday = dayno as i32 + 1; + tm.tm_isdst = 0; + } + + pub fn tm_to_time(tm: &Tm) -> i64 { + let mut y = tm.tm_year as i64 + 1900; + let mut m = tm.tm_mon as i64 + 1; + if m <= 2 { + y -= 1; + m += 12; + } + let d = tm.tm_mday as i64; + let h = tm.tm_hour as i64; + let mi = tm.tm_min as i64; + let s = tm.tm_sec as i64; + (365*y + y/4 - y/100 + y/400 + 3*(m+1)/5 + 30*m + d - 719561) + * 86400 + 3600 * h + 60 * mi + s + } +} + +#[cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))] +mod inner { + use std::ops::{Add, Sub}; + use Tm; + use Duration; + use super::common::{time_to_tm, tm_to_time}; + + #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] + pub struct SteadyTime; + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + time_to_tm(sec, tm); + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + // FIXME: Add timezone logic + time_to_tm(sec, tm); + } + + pub fn utc_tm_to_time(tm: &Tm) -> i64 { + tm_to_time(tm) + } + + pub fn local_tm_to_time(tm: &Tm) -> i64 { + // FIXME: Add timezone logic + tm_to_time(tm) + } + + pub fn get_time() -> (i64, i32) { + unimplemented!() + } + + pub fn get_precise_ns() -> u64 { + unimplemented!() + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + unimplemented!() + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, _other: SteadyTime) -> Duration { + unimplemented!() + } + } + + impl Sub<Duration> for SteadyTime { + type Output = SteadyTime; + fn sub(self, _other: Duration) -> SteadyTime { + unimplemented!() + } + } + + impl Add<Duration> for SteadyTime { + type Output = SteadyTime; + fn add(self, _other: Duration) -> SteadyTime { + unimplemented!() + } + } +} + +#[cfg(target_os = "wasi")] +mod inner { + use std::ops::{Add, Sub}; + use Tm; + use Duration; + use super::common::{time_to_tm, tm_to_time}; + use wasi::{clock_time_get, CLOCKID_MONOTONIC, CLOCKID_REALTIME}; + + #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] + pub struct SteadyTime { + t: u64 + } + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + time_to_tm(sec, tm); + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + // FIXME: Add timezone logic + time_to_tm(sec, tm); + } + + pub fn utc_tm_to_time(tm: &Tm) -> i64 { + tm_to_time(tm) + } + + pub fn local_tm_to_time(tm: &Tm) -> i64 { + // FIXME: Add timezone logic + tm_to_time(tm) + } + + pub fn get_time() -> (i64, i32) { + let ts = get_precise_ns(); + ( + ts as i64 / 1_000_000_000, + (ts as i64 % 1_000_000_000) as i32, + ) + } + + pub fn get_precise_ns() -> u64 { + unsafe { clock_time_get(CLOCKID_REALTIME, 1_000_000_000) } + .expect("Host doesn't implement a real-time clock") + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + SteadyTime { + t: unsafe { clock_time_get(CLOCKID_MONOTONIC, 1_000_000_000) } + .expect("Host doesn't implement a monotonic clock"), + } + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + Duration::nanoseconds(self.t as i64 - other.t as i64) + } + } + + impl Sub<Duration> for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + + impl Add<Duration> for SteadyTime { + type Output = SteadyTime; + fn add(self, other: Duration) -> SteadyTime { + let delta = other.num_nanoseconds().unwrap(); + SteadyTime { + t: (self.t as i64 + delta) as u64, + } + } + } +} + +#[cfg(target_env = "sgx")] +mod inner { + use std::ops::{Add, Sub}; + use Tm; + use Duration; + use super::common::{time_to_tm, tm_to_time}; + use std::time::SystemTime; + + /// The number of nanoseconds in seconds. + const NANOS_PER_SEC: u64 = 1_000_000_000; + + #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] + pub struct SteadyTime { + t: Duration + } + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + time_to_tm(sec, tm); + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + // FIXME: Add timezone logic + time_to_tm(sec, tm); + } + + pub fn utc_tm_to_time(tm: &Tm) -> i64 { + tm_to_time(tm) + } + + pub fn local_tm_to_time(tm: &Tm) -> i64 { + // FIXME: Add timezone logic + tm_to_time(tm) + } + + pub fn get_time() -> (i64, i32) { + SteadyTime::now().t.raw() + } + + pub fn get_precise_ns() -> u64 { + // This unwrap is safe because current time is well ahead of UNIX_EPOCH, unless system + // clock is adjusted backward. + let std_duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + std_duration.as_secs() * NANOS_PER_SEC + std_duration.subsec_nanos() as u64 + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + // This unwrap is safe because current time is well ahead of UNIX_EPOCH, unless system + // clock is adjusted backward. + let std_duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + // This unwrap is safe because duration is well within the limits of i64. + let duration = Duration::from_std(std_duration).unwrap(); + SteadyTime { t: duration } + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + self.t - other.t + } + } + + impl Sub<Duration> for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + SteadyTime { t: self.t - other } + } + } + + impl Add<Duration> for SteadyTime { + type Output = SteadyTime; + fn add(self, other: Duration) -> SteadyTime { + SteadyTime { t: self.t + other } + } + } +} + +#[cfg(unix)] +mod inner { + use libc::{self, time_t}; + use std::mem; + use std::io; + use Tm; + + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub use self::mac::*; + #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))] + pub use self::unix::*; + + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + extern { + static timezone: time_t; + static altzone: time_t; + } + + fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut libc::tm) { + tm.tm_sec = rust_tm.tm_sec; + tm.tm_min = rust_tm.tm_min; + tm.tm_hour = rust_tm.tm_hour; + tm.tm_mday = rust_tm.tm_mday; + tm.tm_mon = rust_tm.tm_mon; + tm.tm_year = rust_tm.tm_year; + tm.tm_wday = rust_tm.tm_wday; + tm.tm_yday = rust_tm.tm_yday; + tm.tm_isdst = rust_tm.tm_isdst; + } + + fn tm_to_rust_tm(tm: &libc::tm, utcoff: i32, rust_tm: &mut Tm) { + rust_tm.tm_sec = tm.tm_sec; + rust_tm.tm_min = tm.tm_min; + rust_tm.tm_hour = tm.tm_hour; + rust_tm.tm_mday = tm.tm_mday; + rust_tm.tm_mon = tm.tm_mon; + rust_tm.tm_year = tm.tm_year; + rust_tm.tm_wday = tm.tm_wday; + rust_tm.tm_yday = tm.tm_yday; + rust_tm.tm_isdst = tm.tm_isdst; + rust_tm.tm_utcoff = utcoff; + } + + #[cfg(any(target_os = "nacl", target_os = "solaris", target_os = "illumos"))] + unsafe fn timegm(tm: *mut libc::tm) -> time_t { + use std::env::{set_var, var_os, remove_var}; + extern { + fn tzset(); + } + + let ret; + + let current_tz = var_os("TZ"); + set_var("TZ", "UTC"); + tzset(); + + ret = libc::mktime(tm); + + if let Some(tz) = current_tz { + set_var("TZ", tz); + } else { + remove_var("TZ"); + } + tzset(); + + ret + } + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + unsafe { + let sec = sec as time_t; + let mut out = mem::zeroed(); + if libc::gmtime_r(&sec, &mut out).is_null() { + panic!("gmtime_r failed: {}", io::Error::last_os_error()); + } + tm_to_rust_tm(&out, 0, tm); + } + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + unsafe { + let sec = sec as time_t; + let mut out = mem::zeroed(); + if libc::localtime_r(&sec, &mut out).is_null() { + panic!("localtime_r failed: {}", io::Error::last_os_error()); + } + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + let gmtoff = { + ::tzset(); + // < 0 means we don't know; assume we're not in DST. + if out.tm_isdst == 0 { + // timezone is seconds west of UTC, tm_gmtoff is seconds east + -timezone + } else if out.tm_isdst > 0 { + -altzone + } else { + -timezone + } + }; + #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + let gmtoff = out.tm_gmtoff; + tm_to_rust_tm(&out, gmtoff as i32, tm); + } + } + + pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 { + #[cfg(all(target_os = "android", target_pointer_width = "32"))] + use libc::timegm64 as timegm; + #[cfg(not(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "nacl", + target_os = "solaris", + target_os = "illumos" + )))] + use libc::timegm; + + let mut tm = unsafe { mem::zeroed() }; + rust_tm_to_tm(rust_tm, &mut tm); + unsafe { timegm(&mut tm) as i64 } + } + + pub fn local_tm_to_time(rust_tm: &Tm) -> i64 { + let mut tm = unsafe { mem::zeroed() }; + rust_tm_to_tm(rust_tm, &mut tm); + unsafe { libc::mktime(&mut tm) as i64 } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + mod mac { + #[allow(deprecated)] + use libc::{self, timeval, mach_timebase_info}; + #[allow(deprecated)] + use std::sync::{Once, ONCE_INIT}; + use std::ops::{Add, Sub}; + use Duration; + + #[allow(deprecated)] + fn info() -> &'static mach_timebase_info { + static mut INFO: mach_timebase_info = mach_timebase_info { + numer: 0, + denom: 0, + }; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + mach_timebase_info(&mut INFO); + }); + &INFO + } + } + + pub fn get_time() -> (i64, i32) { + use std::ptr; + let mut tv = timeval { tv_sec: 0, tv_usec: 0 }; + unsafe { libc::gettimeofday(&mut tv, ptr::null_mut()); } + (tv.tv_sec as i64, tv.tv_usec * 1000) + } + + #[allow(deprecated)] + #[inline] + pub fn get_precise_ns() -> u64 { + unsafe { + let time = libc::mach_absolute_time(); + let info = info(); + time * info.numer as u64 / info.denom as u64 + } + } + + #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)] + pub struct SteadyTime { t: u64 } + + impl SteadyTime { + pub fn now() -> SteadyTime { + SteadyTime { t: get_precise_ns() } + } + } + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + Duration::nanoseconds(self.t as i64 - other.t as i64) + } + } + impl Sub<Duration> for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + impl Add<Duration> for SteadyTime { + type Output = SteadyTime; + fn add(self, other: Duration) -> SteadyTime { + let delta = other.num_nanoseconds().unwrap(); + SteadyTime { + t: (self.t as i64 + delta) as u64 + } + } + } + } + + #[cfg(test)] + pub struct TzReset; + + #[cfg(test)] + pub fn set_los_angeles_time_zone() -> TzReset { + use std::env; + env::set_var("TZ", "America/Los_Angeles"); + ::tzset(); + TzReset + } + + #[cfg(test)] + pub fn set_london_with_dst_time_zone() -> TzReset { + use std::env; + env::set_var("TZ", "Europe/London"); + ::tzset(); + TzReset + } + + #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))] + mod unix { + use std::fmt; + use std::cmp::Ordering; + use std::mem::zeroed; + use std::ops::{Add, Sub}; + use libc; + + use Duration; + + pub fn get_time() -> (i64, i32) { + // SAFETY: libc::timespec is zero initializable. + let mut tv: libc::timespec = unsafe { zeroed() }; + unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut tv); } + (tv.tv_sec as i64, tv.tv_nsec as i32) + } + + pub fn get_precise_ns() -> u64 { + // SAFETY: libc::timespec is zero initializable. + let mut ts: libc::timespec = unsafe { zeroed() }; + unsafe { + libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts); + } + (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64) + } + + #[derive(Copy)] + pub struct SteadyTime { + t: libc::timespec, + } + + impl fmt::Debug for SteadyTime { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "SteadyTime {{ tv_sec: {:?}, tv_nsec: {:?} }}", + self.t.tv_sec, self.t.tv_nsec) + } + } + + impl Clone for SteadyTime { + fn clone(&self) -> SteadyTime { + SteadyTime { t: self.t } + } + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + let mut t = SteadyTime { + // SAFETY: libc::timespec is zero initializable. + t: unsafe { zeroed() } + }; + unsafe { + assert_eq!(0, libc::clock_gettime(libc::CLOCK_MONOTONIC, + &mut t.t)); + } + t + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + if self.t.tv_nsec >= other.t.tv_nsec { + Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64) + } else { + Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 + ::NSEC_PER_SEC as i64 - + other.t.tv_nsec as i64) + } + } + } + + impl Sub<Duration> for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + + impl Add<Duration> for SteadyTime { + type Output = SteadyTime; + fn add(mut self, other: Duration) -> SteadyTime { + let seconds = other.num_seconds(); + let nanoseconds = other - Duration::seconds(seconds); + let nanoseconds = nanoseconds.num_nanoseconds().unwrap(); + + #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] + type nsec = i64; + #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] + type nsec = libc::c_long; + + self.t.tv_sec += seconds as libc::time_t; + self.t.tv_nsec += nanoseconds as nsec; + if self.t.tv_nsec >= ::NSEC_PER_SEC as nsec { + self.t.tv_nsec -= ::NSEC_PER_SEC as nsec; + self.t.tv_sec += 1; + } else if self.t.tv_nsec < 0 { + self.t.tv_sec -= 1; + self.t.tv_nsec += ::NSEC_PER_SEC as nsec; + } + self + } + } + + impl PartialOrd for SteadyTime { + fn partial_cmp(&self, other: &SteadyTime) -> Option<Ordering> { + Some(self.cmp(other)) + } + } + + impl Ord for SteadyTime { + fn cmp(&self, other: &SteadyTime) -> Ordering { + match self.t.tv_sec.cmp(&other.t.tv_sec) { + Ordering::Equal => self.t.tv_nsec.cmp(&other.t.tv_nsec), + ord => ord + } + } + } + + impl PartialEq for SteadyTime { + fn eq(&self, other: &SteadyTime) -> bool { + self.t.tv_sec == other.t.tv_sec && + self.t.tv_nsec == other.t.tv_nsec + } + } + + impl Eq for SteadyTime {} + + } +} + +#[cfg(windows)] +#[allow(non_snake_case)] +mod inner { + use std::io; + use std::mem; + #[allow(deprecated)] + use std::sync::{Once, ONCE_INIT}; + use std::ops::{Add, Sub}; + use {Tm, Duration}; + + use winapi::um::winnt::*; + use winapi::shared::minwindef::*; + use winapi::um::minwinbase::SYSTEMTIME; + use winapi::um::profileapi::*; + use winapi::um::timezoneapi::*; + use winapi::um::sysinfoapi::GetSystemTimeAsFileTime; + + fn frequency() -> i64 { + static mut FREQUENCY: i64 = 0; + #[allow(deprecated)] + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + let mut l = i64_to_large_integer(0); + QueryPerformanceFrequency(&mut l); + FREQUENCY = large_integer_to_i64(l); + }); + FREQUENCY + } + } + + fn i64_to_large_integer(i: i64) -> LARGE_INTEGER { + unsafe { + let mut large_integer: LARGE_INTEGER = mem::zeroed(); + *large_integer.QuadPart_mut() = i; + large_integer + } + } + + fn large_integer_to_i64(l: LARGE_INTEGER) -> i64 { + unsafe { + *l.QuadPart() + } + } + + const HECTONANOSECS_IN_SEC: i64 = 10_000_000; + const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC; + + fn time_to_file_time(sec: i64) -> FILETIME { + let t = (((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH)) as u64; + FILETIME { + dwLowDateTime: t as DWORD, + dwHighDateTime: (t >> 32) as DWORD + } + } + + fn file_time_as_u64(ft: &FILETIME) -> u64 { + ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64) + } + + fn file_time_to_nsec(ft: &FILETIME) -> i32 { + let t = file_time_as_u64(ft) as i64; + ((t % HECTONANOSECS_IN_SEC) * 100) as i32 + } + + fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 { + let t = file_time_as_u64(ft) as i64; + ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64 + } + + fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME { + unsafe { + let mut ft = mem::zeroed(); + SystemTimeToFileTime(sys, &mut ft); + ft + } + } + + fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME { + let mut sys: SYSTEMTIME = unsafe { mem::zeroed() }; + sys.wSecond = tm.tm_sec as WORD; + sys.wMinute = tm.tm_min as WORD; + sys.wHour = tm.tm_hour as WORD; + sys.wDay = tm.tm_mday as WORD; + sys.wDayOfWeek = tm.tm_wday as WORD; + sys.wMonth = (tm.tm_mon + 1) as WORD; + sys.wYear = (tm.tm_year + 1900) as WORD; + sys + } + + fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) { + tm.tm_sec = sys.wSecond as i32; + tm.tm_min = sys.wMinute as i32; + tm.tm_hour = sys.wHour as i32; + tm.tm_mday = sys.wDay as i32; + tm.tm_wday = sys.wDayOfWeek as i32; + tm.tm_mon = (sys.wMonth - 1) as i32; + tm.tm_year = (sys.wYear - 1900) as i32; + tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday); + + fn yday(year: i32, month: i32, day: i32) -> i32 { + let leap = if month > 2 { + if year % 4 == 0 { 1 } else { 2 } + } else { + 0 + }; + let july = if month > 7 { 1 } else { 0 }; + + (month - 1) * 30 + month / 2 + (day - 1) - leap + july + } + } + + macro_rules! call { + ($name:ident($($arg:expr),*)) => { + if $name($($arg),*) == 0 { + panic!(concat!(stringify!($name), " failed with: {}"), + io::Error::last_os_error()); + } + } + } + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + let mut out = unsafe { mem::zeroed() }; + let ft = time_to_file_time(sec); + unsafe { + call!(FileTimeToSystemTime(&ft, &mut out)); + } + system_time_to_tm(&out, tm); + tm.tm_utcoff = 0; + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + let ft = time_to_file_time(sec); + unsafe { + let mut utc = mem::zeroed(); + let mut local = mem::zeroed(); + call!(FileTimeToSystemTime(&ft, &mut utc)); + call!(SystemTimeToTzSpecificLocalTime(0 as *const _, + &mut utc, &mut local)); + system_time_to_tm(&local, tm); + + let local = system_time_to_file_time(&local); + let local_sec = file_time_to_unix_seconds(&local); + + let mut tz = mem::zeroed(); + GetTimeZoneInformation(&mut tz); + + // SystemTimeToTzSpecificLocalTime already applied the biases so + // check if it non standard + tm.tm_utcoff = (local_sec - sec) as i32; + tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { + 0 + } else { + 1 + }; + } + } + + pub fn utc_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let sys_time = tm_to_system_time(tm); + call!(SystemTimeToFileTime(&sys_time, &mut ft)); + file_time_to_unix_seconds(&ft) + } + } + + pub fn local_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let mut utc = mem::zeroed(); + let mut sys_time = tm_to_system_time(tm); + call!(TzSpecificLocalTimeToSystemTime(0 as *mut _, + &mut sys_time, &mut utc)); + call!(SystemTimeToFileTime(&utc, &mut ft)); + file_time_to_unix_seconds(&ft) + } + } + + pub fn get_time() -> (i64, i32) { + unsafe { + let mut ft = mem::zeroed(); + GetSystemTimeAsFileTime(&mut ft); + (file_time_to_unix_seconds(&ft), file_time_to_nsec(&ft)) + } + } + + pub fn get_precise_ns() -> u64 { + let mut ticks = i64_to_large_integer(0); + unsafe { + assert!(QueryPerformanceCounter(&mut ticks) == 1); + } + mul_div_i64(large_integer_to_i64(ticks), 1000000000, frequency()) as u64 + + } + + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + pub struct SteadyTime { + t: i64, + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + let mut l = i64_to_large_integer(0); + unsafe { QueryPerformanceCounter(&mut l); } + SteadyTime { t : large_integer_to_i64(l) } + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + let diff = self.t as i64 - other.t as i64; + Duration::nanoseconds(mul_div_i64(diff, 1000000000, + frequency())) + } + } + + impl Sub<Duration> for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + + impl Add<Duration> for SteadyTime { + type Output = SteadyTime; + fn add(mut self, other: Duration) -> SteadyTime { + self.t += (other.num_microseconds().unwrap() * frequency() / + 1_000_000) as i64; + self + } + } + + #[cfg(test)] + pub struct TzReset { + old: TIME_ZONE_INFORMATION, + } + + #[cfg(test)] + impl Drop for TzReset { + fn drop(&mut self) { + unsafe { + call!(SetTimeZoneInformation(&self.old)); + } + } + } + + #[cfg(test)] + pub fn set_los_angeles_time_zone() -> TzReset { + acquire_privileges(); + + unsafe { + let mut tz = mem::zeroed::<TIME_ZONE_INFORMATION>(); + GetTimeZoneInformation(&mut tz); + let ret = TzReset { old: tz }; + tz.Bias = 60 * 8; + call!(SetTimeZoneInformation(&tz)); + return ret + } + } + + #[cfg(test)] + pub fn set_london_with_dst_time_zone() -> TzReset { + acquire_privileges(); + + unsafe { + let mut tz = mem::zeroed::<TIME_ZONE_INFORMATION>(); + GetTimeZoneInformation(&mut tz); + let ret = TzReset { old: tz }; + // Since date set precisely this is 2015's dates + tz.Bias = 0; + tz.DaylightBias = -60; + tz.DaylightDate.wYear = 0; + tz.DaylightDate.wMonth = 3; + tz.DaylightDate.wDayOfWeek = 0; + tz.DaylightDate.wDay = 5; + tz.DaylightDate.wHour = 2; + tz.StandardBias = 0; + tz.StandardDate.wYear = 0; + tz.StandardDate.wMonth = 10; + tz.StandardDate.wDayOfWeek = 0; + tz.StandardDate.wDay = 5; + tz.StandardDate.wHour = 2; + call!(SetTimeZoneInformation(&tz)); + return ret + } + } + + // Ensures that this process has the necessary privileges to set a new time + // zone, and this is all transcribed from: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724944%28v=vs.85%29.aspx + #[cfg(test)] + fn acquire_privileges() { + use winapi::um::processthreadsapi::*; + use winapi::um::winbase::LookupPrivilegeValueA; + const SE_PRIVILEGE_ENABLED: DWORD = 2; + #[allow(deprecated)] + static INIT: Once = ONCE_INIT; + + // TODO: FIXME + extern "system" { + fn AdjustTokenPrivileges( + TokenHandle: HANDLE, DisableAllPrivileges: BOOL, NewState: PTOKEN_PRIVILEGES, + BufferLength: DWORD, PreviousState: PTOKEN_PRIVILEGES, ReturnLength: PDWORD, + ) -> BOOL; + } + + INIT.call_once(|| unsafe { + let mut hToken = 0 as *mut _; + call!(OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &mut hToken)); + + let mut tkp = mem::zeroed::<TOKEN_PRIVILEGES>(); + assert_eq!(tkp.Privileges.len(), 1); + let c = ::std::ffi::CString::new("SeTimeZonePrivilege").unwrap(); + call!(LookupPrivilegeValueA(0 as *const _, c.as_ptr(), + &mut tkp.Privileges[0].Luid)); + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + tkp.PrivilegeCount = 1; + call!(AdjustTokenPrivileges(hToken, FALSE, &mut tkp, 0, + 0 as *mut _, 0 as *mut _)); + }); + } + + + + // Computes (value*numer)/denom without overflow, as long as both + // (numer*denom) and the overall result fit into i64 (which is the case + // for our time conversions). + fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 { + let q = value / denom; + let r = value % denom; + // Decompose value as (value/denom*denom + value%denom), + // substitute into (value*numer)/denom and simplify. + // r < denom, so (denom*numer) is the upper bound of (r*numer) + q * numer + r * numer / denom + } + + #[test] + fn test_muldiv() { + assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000), + 1_000_000_000_001_000); + assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000), + -1_000_000_000_001_000); + assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000), + 1_000_000_000_001_000); + assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000), + -1_000_000_000_001_000); + assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000), + 1_000_000_000_001_000); + } +} |