timmy 0.3.0

A time tracker mainly for programming tasks
Documentation
use regex::Regex;
use chrono::*;

pub fn parse_datetime<Tz: TimeZone>(s: &str, now: DateTime<Tz>) -> Option<DateTime<Tz>> {
    let date_words_re = Regex::new(r"today|yesterday|now").unwrap();
    let date_absolute_re =
        Regex::new(r"(?P<day>\d{1,2})/(?P<month>\d{1,2})(/(?P<year>\d{4}|\d{2}))?").unwrap();
    let date_relative_re = Regex::new(r"(?P<n>\d+) (days?|ds?)(?P<ago>( ago)?)").unwrap();
    let time_absolute_re = Regex::new(r"(?P<hr>\d{2}):(?P<mins>\d{2})").unwrap();
    let time_relative_re = Regex::new(r"(?x)
        (?P<n>\d+) \s (?P<dur>hrs?|hours?|hs?|minutes?|mins?|ms?) (?P<ago>(\s ago)?)").unwrap();

    let now = if let Some(caps) = date_words_re.captures(s) {
        match caps.at(0) {
            Some("now") | Some("today") | None => now,
            Some("yesterday") => now - Duration::days(1),
            _ => unreachable!(),
        }
    } else if let Some(caps) = date_absolute_re.captures(s) {
        let day = caps.name("day").unwrap().parse().unwrap();
        let month = caps.name("month").unwrap().parse().unwrap();
        let current_year: i32 = Local::now().year() / 1000;
        match caps.name("year") {
            Some(s) if s.len() == 2 =>
                now.with_year(1000 * current_year + s.parse::<i32>().unwrap()).unwrap(),
            Some(s) if s.len() == 4 => now.with_year(s.parse().unwrap()).unwrap(),
            None => now,
            _ => unreachable!(),
        }.with_month(month).unwrap().with_day(day).unwrap()
    } else if let Some(caps) = date_relative_re.captures(s) {
        let n = caps.name("n").unwrap().parse().unwrap();
        let duration = Duration::days(n);
        match caps.name("ago") {
            Some(s) if s.ends_with("ago") => now - duration,
            _ => now + duration,
        }
    } else {
        now
    };

    let now = if let Some(caps) = time_absolute_re.captures(s) {
        let hr = caps.name("hr").unwrap().parse().unwrap();
        let now = now.with_hour(hr).unwrap();
        let min = caps.name("mins").unwrap().parse().unwrap();
        now.with_minute(min).unwrap()
    } else if let Some(caps) = time_relative_re.captures(s) {
        let n = caps.name("n").unwrap().parse().unwrap();
        let dur = caps.name("dur").unwrap();
        let duration = if dur.starts_with("h") {
            Duration::hours(n)
        } else {
            Duration::minutes(n)
        };
        match caps.name("ago") {
            Some(s) if s.ends_with("ago") => now - duration,
            _ => now + duration
        }
    } else {
        now
    };
    Some(now)
}