mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-19 03:25:38 +00:00
melib: add datetime module
Datetime module adds POSIX time functions interface
This commit is contained in:
parent
f6de511abd
commit
c0ac643f05
@ -22,7 +22,7 @@
|
||||
#[cfg(feature = "vcard")]
|
||||
pub mod vcard;
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use crate::datetime::{self, UnixTimestamp};
|
||||
use fnv::FnvHashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -59,8 +59,8 @@ impl From<String> for CardId {
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct AddressBook {
|
||||
display_name: String,
|
||||
created: DateTime<Local>,
|
||||
last_edited: DateTime<Local>,
|
||||
created: UnixTimestamp,
|
||||
last_edited: UnixTimestamp,
|
||||
pub cards: FnvHashMap<CardId, Card>,
|
||||
}
|
||||
|
||||
@ -73,13 +73,13 @@ pub struct Card {
|
||||
name_prefix: String,
|
||||
name_suffix: String,
|
||||
//address
|
||||
birthday: Option<DateTime<Local>>,
|
||||
birthday: Option<UnixTimestamp>,
|
||||
email: String,
|
||||
url: String,
|
||||
key: String,
|
||||
|
||||
color: u8,
|
||||
last_edited: DateTime<Local>,
|
||||
last_edited: UnixTimestamp,
|
||||
extra_properties: FnvHashMap<String, String>,
|
||||
|
||||
/// If true, we can't make any changes because we do not manage this resource.
|
||||
@ -90,8 +90,8 @@ impl AddressBook {
|
||||
pub fn new(display_name: String) -> AddressBook {
|
||||
AddressBook {
|
||||
display_name,
|
||||
created: Local::now(),
|
||||
last_edited: Local::now(),
|
||||
created: datetime::now(),
|
||||
last_edited: datetime::now(),
|
||||
cards: FnvHashMap::default(),
|
||||
}
|
||||
}
|
||||
@ -154,7 +154,7 @@ impl Card {
|
||||
url: String::new(),
|
||||
key: String::new(),
|
||||
|
||||
last_edited: Local::now(),
|
||||
last_edited: datetime::now(),
|
||||
external_resource: false,
|
||||
extra_properties: FnvHashMap::default(),
|
||||
color: 0,
|
||||
@ -190,7 +190,7 @@ impl Card {
|
||||
self.key.as_str()
|
||||
}
|
||||
pub fn last_edited(&self) -> String {
|
||||
self.last_edited.to_rfc2822()
|
||||
datetime::timestamp_to_string(self.last_edited, None)
|
||||
}
|
||||
|
||||
pub fn set_id(&mut self, new_val: CardId) {
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
/// Convert VCard strings to meli Cards (contacts).
|
||||
use super::*;
|
||||
use crate::chrono::TimeZone;
|
||||
use crate::error::{MeliError, Result};
|
||||
use crate::parsec::{match_literal_anycase, one_or_more, peek, prefix, take_until, Parser};
|
||||
use fnv::FnvHashMap;
|
||||
@ -202,7 +201,7 @@ impl<V: VCardVersion> TryInto<Card> for VCard<V> {
|
||||
T102200Z
|
||||
T102200-0800
|
||||
*/
|
||||
card.birthday = chrono::Local.datetime_from_str(&val.value, "%Y%m%d").ok();
|
||||
card.birthday = crate::datetime::timestamp_from_string(val.value.as_str(), "%Y%m%d");
|
||||
}
|
||||
if let Some(val) = self.0.remove("EMAIL") {
|
||||
card.set_email(val.value);
|
||||
|
131
melib/src/datetime.rs
Normal file
131
melib/src/datetime.rs
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* meli - melib POSIX libc time interface
|
||||
*
|
||||
* Copyright 2020 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
pub type UnixTimestamp = u64;
|
||||
|
||||
use libc::{timeval, timezone};
|
||||
|
||||
extern "C" {
|
||||
fn strptime(
|
||||
s: *const ::std::os::raw::c_char,
|
||||
format: *const ::std::os::raw::c_char,
|
||||
tm: *mut ::libc::tm,
|
||||
) -> *const ::std::os::raw::c_char;
|
||||
|
||||
fn strftime(
|
||||
s: *mut ::std::os::raw::c_char,
|
||||
max: ::libc::size_t,
|
||||
format: *const ::std::os::raw::c_char,
|
||||
tm: *const ::libc::tm,
|
||||
) -> ::libc::size_t;
|
||||
|
||||
fn mktime(tm: *const ::libc::tm) -> ::libc::time_t;
|
||||
|
||||
fn localtime_r(timep: *const ::libc::time_t, tm: *mut ::libc::tm) -> *mut ::libc::tm;
|
||||
|
||||
fn gettimeofday(tv: *mut timeval, tz: *mut timezone) -> i32;
|
||||
}
|
||||
|
||||
pub fn timestamp_to_string(timestamp: UnixTimestamp, fmt: Option<&str>) -> String {
|
||||
let mut new_tm: ::libc::tm = unsafe { std::mem::zeroed() };
|
||||
unsafe {
|
||||
let i: i64 = timestamp.try_into().unwrap_or(0);
|
||||
localtime_r(&i as *const i64, &mut new_tm as *mut ::libc::tm);
|
||||
}
|
||||
let fmt = fmt.map(|slice| CString::new(slice).unwrap());
|
||||
let format: &CStr = if let Some(ref s) = fmt {
|
||||
&s
|
||||
} else {
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"%a, %d %b %Y %T %z\0") }
|
||||
};
|
||||
let s: CString;
|
||||
unsafe {
|
||||
let mut vec: Vec<u8> = vec![0; 256];
|
||||
let ret = strftime(
|
||||
vec.as_mut_ptr() as *mut _,
|
||||
256,
|
||||
format.as_ptr(),
|
||||
&new_tm as *const _,
|
||||
);
|
||||
s = CString::new(&vec[0..ret]).unwrap();
|
||||
}
|
||||
|
||||
s.to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
pub fn rfc822_to_timestamp<T>(s: T) -> UnixTimestamp
|
||||
where
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
let mut new_tm: ::libc::tm = unsafe { std::mem::zeroed() };
|
||||
unsafe {
|
||||
let fmt = CStr::from_bytes_with_nul_unchecked(b"%a, %e %h %Y %H:%M:%S %z\0");
|
||||
let ret = strptime(
|
||||
CString::new(s).unwrap().as_ptr(),
|
||||
fmt.as_ptr(),
|
||||
&mut new_tm as *mut _,
|
||||
);
|
||||
if ret.is_null() {
|
||||
return 0;
|
||||
}
|
||||
return mktime(&new_tm as *const _) as u64;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timestamp_from_string<T>(s: T, fmt: &str) -> Option<UnixTimestamp>
|
||||
where
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
let mut new_tm: ::libc::tm = unsafe { std::mem::zeroed() };
|
||||
let fmt = CString::new(fmt).unwrap();
|
||||
unsafe {
|
||||
let ret = strptime(
|
||||
CString::new(s).unwrap().as_ptr(),
|
||||
fmt.as_ptr(),
|
||||
&mut new_tm as *mut _,
|
||||
);
|
||||
if ret.is_null() {
|
||||
return None;
|
||||
}
|
||||
return Some(mktime(&new_tm as *const _) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn now() -> UnixTimestamp {
|
||||
use std::mem::MaybeUninit;
|
||||
let mut tv = MaybeUninit::<::libc::timeval>::uninit();
|
||||
let mut tz = MaybeUninit::<::libc::timezone>::uninit();
|
||||
unsafe {
|
||||
let ret = gettimeofday(tv.as_mut_ptr(), tz.as_mut_ptr());
|
||||
if ret == -1 {
|
||||
unreachable!("gettimeofday returned -1");
|
||||
}
|
||||
(tv.assume_init()).tv_sec as UnixTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timestamp() {
|
||||
timestamp_to_string(0);
|
||||
}
|
@ -39,6 +39,7 @@ pub use address::*;
|
||||
pub mod signatures;
|
||||
|
||||
use crate::backends::BackendOp;
|
||||
use crate::datetime::UnixTimestamp;
|
||||
use crate::error::{MeliError, Result};
|
||||
use crate::thread::ThreadHash;
|
||||
|
||||
@ -51,9 +52,6 @@ use std::option::Option;
|
||||
use std::str;
|
||||
use std::string::String;
|
||||
|
||||
use chrono;
|
||||
use chrono::TimeZone;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct Flag: u8 {
|
||||
@ -105,7 +103,6 @@ impl EnvelopeWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub type UnixTimestamp = u64;
|
||||
pub type EnvelopeHash = u64;
|
||||
|
||||
/// `Envelope` represents all the data of an email we need to know.
|
||||
@ -350,14 +347,10 @@ impl Envelope {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
|
||||
if let Ok(d) = parser::date(&self.date.as_bytes()) {
|
||||
return d;
|
||||
}
|
||||
chrono::FixedOffset::west(0)
|
||||
.ymd(1970, 1, 1)
|
||||
.and_hms(0, 0, 0)
|
||||
pub fn datetime(&self) -> UnixTimestamp {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
pub fn date_as_str(&self) -> &str {
|
||||
&self.date
|
||||
}
|
||||
@ -572,8 +565,8 @@ impl Envelope {
|
||||
pub fn set_thread(&mut self, new_val: ThreadHash) {
|
||||
self.thread = new_val;
|
||||
}
|
||||
pub fn set_datetime(&mut self, new_val: chrono::DateTime<chrono::FixedOffset>) {
|
||||
self.timestamp = new_val.timestamp() as UnixTimestamp;
|
||||
pub fn set_datetime(&mut self, new_val: UnixTimestamp) {
|
||||
self.timestamp = new_val;
|
||||
}
|
||||
pub fn set_flag(
|
||||
&mut self,
|
||||
|
@ -2,7 +2,6 @@ use super::*;
|
||||
use crate::backends::BackendOp;
|
||||
use crate::email::attachments::AttachmentBuilder;
|
||||
use crate::shellexpand::ShellExpandTrait;
|
||||
use chrono::{DateTime, Local};
|
||||
use data_encoding::BASE64_MIME;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::Read;
|
||||
@ -35,8 +34,10 @@ impl Default for Draft {
|
||||
headers.insert("Cc".into(), "".into());
|
||||
headers.insert("Bcc".into(), "".into());
|
||||
|
||||
let now: DateTime<Local> = Local::now();
|
||||
headers.insert("Date".into(), now.to_rfc2822());
|
||||
headers.insert(
|
||||
"Date".into(),
|
||||
crate::datetime::timestamp_to_string(crate::datetime::now(), None),
|
||||
);
|
||||
headers.insert("Subject".into(), "".into());
|
||||
headers.insert(
|
||||
"User-Agent".into(),
|
||||
|
@ -19,7 +19,6 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use super::*;
|
||||
use chrono;
|
||||
use data_encoding::BASE64_MIME;
|
||||
use encoding::{DecoderTrap, Encoding};
|
||||
use nom::{is_hex_digit, le_u8};
|
||||
@ -658,18 +657,13 @@ fn eat_comments(input: &[u8]) -> Vec<u8> {
|
||||
* right now we expect input will have no extra spaces in between tokens
|
||||
*
|
||||
* We should use a custom parser here*/
|
||||
pub fn date(input: &[u8]) -> Result<chrono::DateTime<chrono::FixedOffset>> {
|
||||
pub fn date(input: &[u8]) -> Result<UnixTimestamp> {
|
||||
let mut parsed_result = phrase(&eat_comments(input)).to_full_result()?;
|
||||
if let Some(pos) = parsed_result.find(b"-0000") {
|
||||
parsed_result[pos] = b'+';
|
||||
}
|
||||
|
||||
Ok(
|
||||
chrono::DateTime::parse_from_rfc2822(
|
||||
String::from_utf8_lossy(parsed_result.trim()).as_ref(),
|
||||
)
|
||||
.map_err(|err| MeliError::new(err.to_string()))?,
|
||||
)
|
||||
Ok(crate::datetime::rfc822_to_timestamp(parsed_result.trim()))
|
||||
}
|
||||
|
||||
named!(pub message_id<&[u8]>,
|
||||
|
@ -104,6 +104,9 @@ pub mod dbg {
|
||||
#[cfg(feature = "unicode_algorithms")]
|
||||
extern crate text_processing;
|
||||
|
||||
pub mod datetime;
|
||||
pub use datetime::UnixTimestamp;
|
||||
|
||||
#[macro_use]
|
||||
mod logging;
|
||||
pub use self::logging::LoggingLevel::*;
|
||||
@ -129,7 +132,6 @@ extern crate serde_derive;
|
||||
/* parser */
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
extern crate chrono;
|
||||
extern crate data_encoding;
|
||||
extern crate encoding;
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
use crate::shellexpand::ShellExpandTrait;
|
||||
use chrono::offset::Local;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::PathBuf;
|
||||
@ -79,7 +78,9 @@ pub fn log(val: String, level: LoggingLevel) {
|
||||
let mut b = f.lock().unwrap();
|
||||
if level <= b.level {
|
||||
b.dest
|
||||
.write_all(Local::now().to_string().as_bytes())
|
||||
.write_all(
|
||||
crate::datetime::timestamp_to_string(crate::datetime::now(), None).as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
b.dest.write_all(b" [").unwrap();
|
||||
b.dest.write_all(level.to_string().as_bytes()).unwrap();
|
||||
|
@ -32,6 +32,7 @@
|
||||
* ownership.
|
||||
*/
|
||||
|
||||
use crate::datetime::UnixTimestamp;
|
||||
use crate::email::parser::BytesExt;
|
||||
use crate::email::*;
|
||||
use crate::structs::StackVec;
|
||||
@ -432,13 +433,6 @@ impl ThreadNode {
|
||||
self.date
|
||||
}
|
||||
|
||||
pub fn datetime(&self) -> chrono::DateTime<chrono::Utc> {
|
||||
use chrono::{TimeZone, Utc};
|
||||
use std::convert::TryInto;
|
||||
|
||||
Utc.timestamp(self.date.try_into().unwrap_or(0), 0)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.parent.is_none() && self.message.is_none() && self.children.is_empty()
|
||||
}
|
||||
|
@ -19,8 +19,8 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use melib::email::UnixTimestamp;
|
||||
use melib::parsec::*;
|
||||
use melib::UnixTimestamp;
|
||||
use melib::{
|
||||
backends::{FolderHash, MailBackend},
|
||||
email::EnvelopeHash,
|
||||
|
@ -911,10 +911,7 @@ impl ConversationsListing {
|
||||
n / (24 * 60 * 60),
|
||||
if n / (24 * 60 * 60) == 1 { "" } else { "s" }
|
||||
),
|
||||
_ => thread_node
|
||||
.datetime()
|
||||
.format("%Y-%m-%d %H:%M:%S")
|
||||
.to_string(),
|
||||
_ => melib::datetime::timestamp_to_string(thread_node.date(), Some("%Y-%m-%d %T")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -906,7 +906,7 @@ impl PlainListing {
|
||||
n if n < 4 * 24 * 60 * 60 => {
|
||||
format!("{} days ago{}", n / (24 * 60 * 60), " ".repeat(9))
|
||||
}
|
||||
_ => envelope.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||
_ => melib::datetime::timestamp_to_string(envelope.datetime(), None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,7 +487,7 @@ impl ThreadListing {
|
||||
n if n < 4 * 24 * 60 * 60 => {
|
||||
format!("{} days ago{}", n / (24 * 60 * 60), " ".repeat(9))
|
||||
}
|
||||
_ => envelope.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||
_ => melib::datetime::timestamp_to_string(envelope.datetime(), None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user