melib: add datetime module

Datetime module adds POSIX time functions interface
This commit is contained in:
Manos Pitsidianakis 2020-01-06 16:10:36 +02:00
parent f6de511abd
commit c0ac643f05
No known key found for this signature in database
GPG Key ID: 73627C2F690DF710
13 changed files with 164 additions and 52 deletions

View File

@ -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) {

View File

@ -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
View 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);
}

View File

@ -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,

View File

@ -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(),

View File

@ -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]>,

View File

@ -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;

View File

@ -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();

View File

@ -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()
}

View File

@ -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,

View File

@ -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")),
}
}

View File

@ -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),
}
}

View File

@ -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),
}
}
}