use std::io::Write; use chrono::{DateTime, Utc, Local, LocalResult, TimeZone}; use ansi_term::{Color::Yellow, Style}; use crate::error::{Error::*, Result}; use crate::models::Entry; use crate::database::{Database, DBVersion}; use crate::env::Env; /// Treat t as if it wasnt actually in Utc but in the local timezone and return /// the actual Utc time. /// /// Used to convert times from the old database version. fn local_to_utc(t: DateTime) -> Result> { let local_time = match Local.from_local_datetime(&t.naive_utc()) { LocalResult::None => return Err(NoneLocalTime(t.naive_utc().to_string())), LocalResult::Single(t) => t, LocalResult::Ambiguous(t1, t2) => return Err(AmbiguousLocalTime { orig: t.naive_utc().to_string(), t1: t1.naive_local(), t2: t2.naive_local(), }), }; Ok(Utc.from_utc_datetime(&local_time.naive_utc())) } /// takes an otherwise perfectly good timestamp in Utc and turns it into the /// local timezone, but using the same DateTime type. /// /// Used to insert times into the old database format. fn utc_to_local(t: DateTime) -> DateTime { Utc.from_utc_datetime(&t.with_timezone(&Local).naive_local()) } /// Maps an entire vector of entries from the old database format to the new, /// converting their timestamps from the local timezone to Utc. fn local_to_utc_vec(entries: Vec) -> Result> { entries .into_iter() .map(|e| { Ok(Entry { start: local_to_utc(e.start)?, end: e.end.map(local_to_utc).transpose()?, ..e }) }) .collect() } /// the logic used by many subcommands that transform entries from the old /// format to the new. Used in conjunction with warn_if_needed. pub fn entries_or_warning(entries: Vec, db: &D) -> Result<(Vec, bool)> { if let DBVersion::Timetrap = db.version()? { // this indicates that times in the database are specified in the // local time and need to be converted to utc before displaying Ok((local_to_utc_vec(entries)?, true)) } else { Ok((entries, false)) } } /// Wrapper around utc_to_local that also returns a flag in case a warning is /// needed pub fn time_or_warning(time: DateTime, db: &D) -> Result<(DateTime, bool)> { if let DBVersion::Timetrap = db.version()? { Ok((utc_to_local(time), true)) } else { Ok((time, false)) } } /// emits the appropiate warning if the old database format was detected. pub fn warn_if_needed(err: &mut E, needs_warning: bool, env: &Env) -> Result<()> { if needs_warning && !env.supress_warming { writeln!( err, "{} You are using the old timetrap format, it is advised that \ you update your database using t migrate", if env.stderr_is_tty { Yellow.bold().paint("[WARNING]") } else { Style::new().paint("[WARNING]") }, ).map_err(IOError)?; } Ok(()) }