melib/datetime: add timestamp_to_string_utc
Tests were using `timestamp_to_string` which in turn uses `localtime_r` which assumes the local machine's time zone. Use gmtime_r instead. Fixes #252pull/253/head
parent
c2ed3e283f
commit
d93ee413a7
|
@ -25,7 +25,7 @@ use std::collections::VecDeque;
|
|||
|
||||
use crate::{
|
||||
search::*,
|
||||
utils::datetime::{formats::IMAP_DATE, timestamp_to_string},
|
||||
utils::datetime::{formats::IMAP_DATE, timestamp_to_string_utc},
|
||||
};
|
||||
|
||||
mod private {
|
||||
|
@ -165,25 +165,25 @@ impl ToImapSearch for Query {
|
|||
Q(Before(t)) => {
|
||||
space_pad!(s);
|
||||
s.push_str("BEFORE ");
|
||||
s.push_str(×tamp_to_string(*t, Some(IMAP_DATE), true));
|
||||
s.push_str(×tamp_to_string_utc(*t, Some(IMAP_DATE), true));
|
||||
}
|
||||
Q(After(t)) => {
|
||||
space_pad!(s);
|
||||
s.push_str("SINCE ");
|
||||
s.push_str(×tamp_to_string(*t, Some(IMAP_DATE), true));
|
||||
s.push_str(×tamp_to_string_utc(*t, Some(IMAP_DATE), true));
|
||||
}
|
||||
Q(Between(t1, t2)) => {
|
||||
space_pad!(s);
|
||||
s.push_str("(SINCE ");
|
||||
s.push_str(×tamp_to_string(*t1, Some(IMAP_DATE), true));
|
||||
s.push_str(×tamp_to_string_utc(*t1, Some(IMAP_DATE), true));
|
||||
s.push_str(" BEFORE ");
|
||||
s.push_str(×tamp_to_string(*t2, Some(IMAP_DATE), true));
|
||||
s.push_str(×tamp_to_string_utc(*t2, Some(IMAP_DATE), true));
|
||||
s.push(')');
|
||||
}
|
||||
Q(On(t)) => {
|
||||
space_pad!(s);
|
||||
s.push_str("ON ");
|
||||
s.push_str(×tamp_to_string(*t, Some(IMAP_DATE), true));
|
||||
s.push_str(×tamp_to_string_utc(*t, Some(IMAP_DATE), true));
|
||||
}
|
||||
Q(InReplyTo(t)) => {
|
||||
space_pad!(s);
|
||||
|
@ -281,8 +281,8 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
×tamp_to_string(1685739600, Some(IMAP_DATE), true),
|
||||
"03-Jun-2023"
|
||||
×tamp_to_string_utc(1685739600, Some(IMAP_DATE), true),
|
||||
"02-Jun-2023"
|
||||
);
|
||||
|
||||
let (_, q) = query()
|
||||
|
@ -290,7 +290,7 @@ mod tests {
|
|||
.unwrap();
|
||||
assert_eq!(
|
||||
&q.to_imap_search(),
|
||||
r#"BEFORE 04-Jun-2023 FROM "user@example.org""#
|
||||
r#"BEFORE 03-Jun-2023 FROM "user@example.org""#
|
||||
);
|
||||
let (_, q) = query()
|
||||
.parse_complete(r#"subject:"wah ah ah" or (from:Manos and from:Sia)"#)
|
||||
|
|
|
@ -585,7 +585,7 @@ impl From<crate::search::Query> for Filter<EmailFilterCondition, EmailObject> {
|
|||
fn from(val: crate::search::Query) -> Self {
|
||||
let mut ret = Self::Condition(EmailFilterCondition::new().into());
|
||||
fn rec(q: &crate::search::Query, f: &mut Filter<EmailFilterCondition, EmailObject>) {
|
||||
use datetime::{formats::RFC3339_DATE, timestamp_to_string};
|
||||
use datetime::{formats::RFC3339_DATE, timestamp_to_string_utc};
|
||||
|
||||
use crate::search::Query::*;
|
||||
|
||||
|
@ -614,26 +614,26 @@ impl From<crate::search::Query> for Filter<EmailFilterCondition, EmailObject> {
|
|||
Before(t) => {
|
||||
*f = Filter::Condition(
|
||||
EmailFilterCondition::new()
|
||||
.before(timestamp_to_string(*t, Some(RFC3339_DATE), true))
|
||||
.before(timestamp_to_string_utc(*t, Some(RFC3339_DATE), true))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
After(t) => {
|
||||
*f = Filter::Condition(
|
||||
EmailFilterCondition::new()
|
||||
.after(timestamp_to_string(*t, Some(RFC3339_DATE), true))
|
||||
.after(timestamp_to_string_utc(*t, Some(RFC3339_DATE), true))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
Between(a, b) => {
|
||||
*f = Filter::Condition(
|
||||
EmailFilterCondition::new()
|
||||
.after(timestamp_to_string(*a, Some(RFC3339_DATE), true))
|
||||
.after(timestamp_to_string_utc(*a, Some(RFC3339_DATE), true))
|
||||
.into(),
|
||||
);
|
||||
*f &= Filter::Condition(
|
||||
EmailFilterCondition::new()
|
||||
.before(timestamp_to_string(*b, Some(RFC3339_DATE), true))
|
||||
.before(timestamp_to_string_utc(*b, Some(RFC3339_DATE), true))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ impl MailBackend for NntpType {
|
|||
let mut conn = timeout(Some(Duration::from_secs(60 * 16)), connection.lock()).await?;
|
||||
if let Some(mut latest_article) = latest_article {
|
||||
let timestamp = latest_article - 10 * 60;
|
||||
let datetime_str = crate::utils::datetime::timestamp_to_string(
|
||||
let datetime_str = crate::utils::datetime::timestamp_to_string_utc(
|
||||
timestamp,
|
||||
Some("%Y%m%d %H%M%S"),
|
||||
true,
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
//! assert_eq!(timestamp, 1578509043);
|
||||
//!
|
||||
//! // Convert timestamp back to string
|
||||
//! let s = timestamp_to_string(timestamp, Some("%Y-%m-%d"), true);
|
||||
//! let s = timestamp_to_string_utc(timestamp, Some("%Y-%m-%d"), true);
|
||||
//! assert_eq!(s, "2020-01-08");
|
||||
//! ```
|
||||
use std::{
|
||||
|
@ -87,6 +87,8 @@ extern "C" {
|
|||
|
||||
fn localtime_r(timep: *const libc::time_t, tm: *mut libc::tm) -> *mut libc::tm;
|
||||
|
||||
fn gmtime_r(timep: *const libc::time_t, tm: *mut libc::tm) -> *mut libc::tm;
|
||||
|
||||
fn gettimeofday(tv: *mut libc::timeval, tz: *mut libc::timezone) -> i32;
|
||||
}
|
||||
|
||||
|
@ -199,11 +201,21 @@ impl Locale {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn timestamp_to_string(timestamp: UnixTimestamp, fmt: Option<&str>, posix: bool) -> String {
|
||||
#[inline]
|
||||
fn timestamp_to_string_inner(
|
||||
timestamp: UnixTimestamp,
|
||||
fmt: Option<&str>,
|
||||
posix: bool,
|
||||
local: bool,
|
||||
) -> String {
|
||||
let mut new_tm: libc::tm = unsafe { std::mem::zeroed() };
|
||||
unsafe {
|
||||
let i: i64 = timestamp.try_into().unwrap_or(0);
|
||||
localtime_r(std::ptr::addr_of!(i), std::ptr::addr_of_mut!(new_tm));
|
||||
if local {
|
||||
localtime_r(std::ptr::addr_of!(i), std::ptr::addr_of_mut!(new_tm));
|
||||
} else {
|
||||
gmtime_r(std::ptr::addr_of!(i), std::ptr::addr_of_mut!(new_tm));
|
||||
}
|
||||
}
|
||||
let format: Cow<'_, CStr> = if let Some(cs) = fmt
|
||||
.map(str::as_bytes)
|
||||
|
@ -251,6 +263,18 @@ pub fn timestamp_to_string(timestamp: UnixTimestamp, fmt: Option<&str>, posix: b
|
|||
String::from_utf8_lossy(&vec[0..ret]).into_owned()
|
||||
}
|
||||
|
||||
/// Return a UNIX epoch timestamp as string in the local timezone, using `fmt`
|
||||
/// as the format argument passed to `strptime`.
|
||||
pub fn timestamp_to_string(timestamp: UnixTimestamp, fmt: Option<&str>, posix: bool) -> String {
|
||||
timestamp_to_string_inner(timestamp, fmt, posix, true)
|
||||
}
|
||||
|
||||
/// Return a UNIX epoch timestamp as string in the UTC/GMT/+00:00 timezone,
|
||||
/// using `fmt` as the format argument passed to `strptime`.
|
||||
pub fn timestamp_to_string_utc(timestamp: UnixTimestamp, fmt: Option<&str>, posix: bool) -> String {
|
||||
timestamp_to_string_inner(timestamp, fmt, posix, false)
|
||||
}
|
||||
|
||||
fn tm_to_secs(tm: libc::tm) -> std::result::Result<i64, ()> {
|
||||
let mut is_leap = false;
|
||||
let mut year = tm.tm_year;
|
||||
|
|
Loading…
Reference in New Issue