melib/datetime: Add Locale struct for error checking

jmap-eventsource
Manos Pitsidianakis 2021-01-04 23:11:47 +02:00
parent f7cbd9a64d
commit 34e970d922
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
1 changed files with 62 additions and 14 deletions

View File

@ -37,13 +37,13 @@
//! let s = timestamp_to_string(timestamp, Some("%Y-%m-%d"));
//! assert_eq!(s, "2020-01-08");
//! ```
use crate::error::Result;
use crate::error::{Result, ResultIntoMeliError};
use std::convert::TryInto;
use std::ffi::{CStr, CString};
pub type UnixTimestamp = u64;
use libc::{timeval, timezone, uselocale};
use libc::{locale_t, timeval, timezone};
extern "C" {
fn strptime(
@ -66,6 +66,43 @@ extern "C" {
fn gettimeofday(tv: *mut timeval, tz: *mut timezone) -> i32;
}
struct Locale {
new_locale: locale_t,
old_locale: locale_t,
}
impl Drop for Locale {
fn drop(&mut self) {
unsafe {
let _ = libc::uselocale(self.old_locale);
libc::freelocale(self.new_locale);
}
}
}
// How to unit test this? Test machine is not guaranteed to have non-english locales.
impl Locale {
fn new(
mask: ::std::os::raw::c_int,
locale: *const ::std::os::raw::c_char,
base: locale_t,
) -> Result<Self> {
let new_locale = unsafe { libc::newlocale(mask, locale, base) };
if new_locale.is_null() {
return Err(nix::Error::last().into());
}
let old_locale = unsafe { libc::uselocale(new_locale) };
if old_locale.is_null() {
unsafe { libc::freelocale(new_locale) };
return Err(nix::Error::last().into());
}
Ok(Locale {
new_locale,
old_locale,
})
}
}
pub fn timestamp_to_string(timestamp: UnixTimestamp, fmt: Option<&str>) -> String {
let mut new_tm: ::libc::tm = unsafe { std::mem::zeroed() };
unsafe {
@ -215,17 +252,16 @@ where
unsafe {
let fmt = CStr::from_bytes_with_nul_unchecked(fmt);
let locale = ::libc::newlocale(
::libc::LC_TIME,
b"C\0".as_ptr() as *const i8,
std::ptr::null_mut(),
);
let old_locale = uselocale(locale);
let ret = strptime(s.as_ptr(), fmt.as_ptr(), &mut new_tm as *mut _);
uselocale(old_locale);
::libc::freelocale(locale);
let ret = {
let _with_locale = Locale::new(
::libc::LC_TIME,
b"C\0".as_ptr() as *const i8,
std::ptr::null_mut(),
)
.chain_err_summary(|| "Could not set locale for datetime conversion")
.chain_err_kind(crate::error::ErrorKind::External)?;
strptime(s.as_ptr(), fmt.as_ptr(), &mut new_tm as *mut _)
};
if ret.is_null() {
continue;
@ -278,7 +314,16 @@ where
for fmt in &[&b"%Y-%m-%dT%H:%M:%S\0"[..], &b"%Y-%m-%d\0"[..]] {
unsafe {
let fmt = CStr::from_bytes_with_nul_unchecked(fmt);
let ret = strptime(s.as_ptr(), fmt.as_ptr(), &mut new_tm as *mut _);
let ret = {
let _with_locale = Locale::new(
::libc::LC_TIME,
b"C\0".as_ptr() as *const i8,
std::ptr::null_mut(),
)
.chain_err_summary(|| "Could not set locale for datetime conversion")
.chain_err_kind(crate::error::ErrorKind::External)?;
strptime(s.as_ptr(), fmt.as_ptr(), &mut new_tm as *mut _)
};
if ret.is_null() {
continue;
}
@ -362,6 +407,9 @@ fn test_timestamp() {
#[test]
fn test_rfcs() {
if unsafe { libc::setlocale(libc::LC_ALL, b"\0".as_ptr() as _) }.is_null() {
println!("Unable to set locale.");
}
/* Some tests were lazily stolen from https://rachelbythebay.com/w/2013/06/11/time/ */
assert_eq!(