diff --git a/melib/src/addressbook.rs b/melib/src/addressbook.rs index 714d462cf..3b5920907 100644 --- a/melib/src/addressbook.rs +++ b/melib/src/addressbook.rs @@ -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 for CardId { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct AddressBook { display_name: String, - created: DateTime, - last_edited: DateTime, + created: UnixTimestamp, + last_edited: UnixTimestamp, pub cards: FnvHashMap, } @@ -73,13 +73,13 @@ pub struct Card { name_prefix: String, name_suffix: String, //address - birthday: Option>, + birthday: Option, email: String, url: String, key: String, color: u8, - last_edited: DateTime, + last_edited: UnixTimestamp, extra_properties: FnvHashMap, /// 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) { diff --git a/melib/src/addressbook/vcard.rs b/melib/src/addressbook/vcard.rs index 654882466..bc57e7392 100644 --- a/melib/src/addressbook/vcard.rs +++ b/melib/src/addressbook/vcard.rs @@ -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 TryInto for VCard { 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); diff --git a/melib/src/datetime.rs b/melib/src/datetime.rs new file mode 100644 index 000000000..7c56686ff --- /dev/null +++ b/melib/src/datetime.rs @@ -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 . + */ + +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 = 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(s: T) -> UnixTimestamp +where + T: Into>, +{ + 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(s: T, fmt: &str) -> Option +where + T: Into>, +{ + 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); +} diff --git a/melib/src/email.rs b/melib/src/email.rs index 48f7c76b4..cab8565e4 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -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 { - 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) { - 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, diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index 594ae1ba4..47b24f861 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -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::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(), diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs index 7a2aad284..b392f04e0 100644 --- a/melib/src/email/parser.rs +++ b/melib/src/email/parser.rs @@ -19,7 +19,6 @@ * along with meli. If not, see . */ 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 { * 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> { +pub fn date(input: &[u8]) -> Result { 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]>, diff --git a/melib/src/lib.rs b/melib/src/lib.rs index 0ff5c75a9..36cc0e723 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -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; diff --git a/melib/src/logging.rs b/melib/src/logging.rs index 379855161..cef2fdb94 100644 --- a/melib/src/logging.rs +++ b/melib/src/logging.rs @@ -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(); diff --git a/melib/src/thread.rs b/melib/src/thread.rs index a1f2445c2..9c8fdbfea 100644 --- a/melib/src/thread.rs +++ b/melib/src/thread.rs @@ -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 { - 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() } diff --git a/ui/src/cache.rs b/ui/src/cache.rs index 3f8dbfce0..afb542e1c 100644 --- a/ui/src/cache.rs +++ b/ui/src/cache.rs @@ -19,8 +19,8 @@ * along with meli. If not, see . */ -use melib::email::UnixTimestamp; use melib::parsec::*; +use melib::UnixTimestamp; use melib::{ backends::{FolderHash, MailBackend}, email::EnvelopeHash, diff --git a/ui/src/components/mail/listing/conversations.rs b/ui/src/components/mail/listing/conversations.rs index edc357eb2..3b72cdc4e 100644 --- a/ui/src/components/mail/listing/conversations.rs +++ b/ui/src/components/mail/listing/conversations.rs @@ -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")), } } diff --git a/ui/src/components/mail/listing/plain.rs b/ui/src/components/mail/listing/plain.rs index 3f0f0f742..b1f8de8cd 100644 --- a/ui/src/components/mail/listing/plain.rs +++ b/ui/src/components/mail/listing/plain.rs @@ -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), } } diff --git a/ui/src/components/mail/listing/thread.rs b/ui/src/components/mail/listing/thread.rs index 503d8a05b..cbcdbcd7d 100644 --- a/ui/src/components/mail/listing/thread.rs +++ b/ui/src/components/mail/listing/thread.rs @@ -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), } } }