use std::ffi::CString;
use std::mem;
mod c {
extern "C" {
#[cfg(unix)]
pub(crate) fn tzset();
#[cfg(windows)]
pub(crate) fn _tzset();
pub(crate) fn strftime(
s: *mut libc::c_char,
max: libc::size_t,
format: *const libc::c_char,
tm: *const libc::tm,
) -> usize;
pub(crate) fn time(tloc: *const libc::time_t) -> libc::time_t;
#[cfg(unix)]
pub(crate) fn localtime_r(t: *const libc::time_t, tm: *mut libc::tm);
#[cfg(windows)]
pub(crate) fn _localtime64_s(tm: *mut libc::tm, t: *const libc::time_t);
#[cfg(unix)]
pub(crate) fn gmtime_r(t: *const libc::time_t, tm: *mut libc::tm);
#[cfg(windows)]
pub(crate) fn _gmtime64_s(tm: *mut libc::tm, t: *const libc::time_t);
}
}
pub fn get_local_tm_from_epoch(epoch: libc::time_t) -> libc::tm {
unsafe {
let mut now: libc::tm = mem::zeroed();
#[cfg(unix)]
c::localtime_r(&epoch, &mut now);
#[cfg(windows)]
c::_localtime64_s(&mut now, &epoch);
now
}
}
pub fn get_gmt_tm_from_epoch(epoch: libc::time_t) -> libc::tm {
unsafe {
let mut now: libc::tm = mem::zeroed();
#[cfg(unix)]
c::gmtime_r(&epoch, &mut now);
#[cfg(windows)]
c::_gmtime64_s(&mut now, &epoch);
now
}
}
fn strftime(format: &str, tm: &libc::tm) -> String {
let f = CString::new(format).unwrap();
let buf = [0_u8; 100];
let l: usize = unsafe { c::strftime(buf.as_ptr() as _, buf.len(), f.as_ptr() as *const _, tm) };
std::string::String::from_utf8_lossy(&buf[..l]).to_string()
}
pub fn strftime_local(format: &str, epoch: libc::time_t) -> String {
let tm = get_local_tm_from_epoch(epoch);
strftime(format, &tm)
}
pub fn strftime_gmt(format: &str, epoch: libc::time_t) -> String {
let tm = get_gmt_tm_from_epoch(epoch);
strftime(format, &tm)
}
pub fn set_locale() {
unsafe {
libc::setlocale(libc::LC_ALL, b"\0".as_ptr() as _);
}
}
pub fn tz_set() {
unsafe {
#[cfg(unix)]
c::tzset();
#[cfg(windows)]
c::_tzset();
}
}
pub fn epoch() -> libc::time_t {
unsafe { c::time(std::ptr::null()) }
}
#[cfg(test)]
mod tests {
use crate::*;
const EPOCH: libc::time_t = 1_565_151_596;
#[test]
#[cfg(unix)]
fn format_time_and_date_in_gmt_and_cest() {
use std::env;
env::set_var("LC_ALL", "en_US.UTF-8");
env::set_var("TZ", "Europe/Brussels");
tz_set();
set_locale();
let gmt = strftime_gmt("%c", EPOCH);
let local = strftime_local("%c", EPOCH);
#[cfg(target_os = "linux")]
assert_eq!(gmt, "Wed 07 Aug 2019 04:19:56 AM GMT");
#[cfg(target_os = "macos")]
assert_eq!(gmt, "Wed Aug 7 04:19:56 2019");
#[cfg(target_os = "linux")]
assert_eq!(local, "Wed 07 Aug 2019 06:19:56 AM CEST");
#[cfg(target_os = "macos")]
assert_eq!(local, "Wed Aug 7 06:19:56 2019");
env::set_var("LC_ALL", "fr_BE.UTF-8");
env::set_var("TZ", "Europe/Brussels");
tz_set();
set_locale();
let gmt = strftime_gmt("%c", EPOCH);
let local = strftime_local("%c", EPOCH);
#[cfg(target_os = "linux")]
assert_eq!(gmt, "mer 07 aoû 2019 04:19:56 GMT");
#[cfg(target_os = "macos")]
assert_eq!(gmt, "Mer 7 aoû 04:19:56 2019");
#[cfg(target_os = "linux")]
assert_eq!(local, "mer 07 aoû 2019 06:19:56 CEST");
#[cfg(target_os = "macos")]
assert_eq!(local, "Mer 7 aoû 06:19:56 2019");
}
#[test]
#[cfg(windows)]
fn format_time_and_date_on_windows() {
tz_set();
set_locale();
let gmt = strftime_gmt("%c", EPOCH);
let local = strftime_local("%c", EPOCH);
#[cfg(target_os = "windows")]
assert_eq!(gmt, "8/7/2019 4:19:56 AM");
#[cfg(target_os = "windows")]
assert_eq!(local, "8/7/2019 4:19:56 AM");
}
}