|
@@ -0,0 +1,110 @@ |
|
|
|
|
|
use chrono::prelude::*; |
|
|
|
|
|
use chrono_tz::Tz; |
|
|
|
|
|
|
|
|
|
|
|
lazy_static::lazy_static! { |
|
|
|
|
|
static ref HOUR_AMPM: regex::Regex = regex::Regex::new(r#"(?P<hr>\d{1,2})\s?(?P<ampm>(am|pm|AM|PM))"#).unwrap(); |
|
|
|
|
|
static ref HOUR_MINUTE_AMPM: regex::Regex = regex::Regex::new(r#"(?P<hr>\d{1,2}):(?P<minute>\d{2})\s?(?P<ampm>(am|pm|AM|PM))"#).unwrap(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn parse_time(time_input: &str) -> NaiveTime { |
|
|
|
|
|
let time: String = time_input.split_whitespace().collect::<Vec<_>>().join(""); |
|
|
|
|
|
match NaiveTime::parse_from_str(&time, "%H:%M") { |
|
|
|
|
|
Ok(t) => return t, |
|
|
|
|
|
Err(_e) => {} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if HOUR_MINUTE_AMPM.is_match(&time) { |
|
|
|
|
|
return NaiveTime::parse_from_str(&time, "%I:%M%p").unwrap(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let t2 = HOUR_AMPM.replace(&time, "$hr:00$ampm"); |
|
|
|
|
|
|
|
|
|
|
|
match NaiveTime::parse_from_str(&t2, "%I:%M%p") { |
|
|
|
|
|
Ok(t) => return t, |
|
|
|
|
|
Err(_e) => {} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
panic!("failed to parse time -- try HH:MM format (input = '{}')", time) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn convert_common_tz_abbrs(tz: &str) -> &str { |
|
|
|
|
|
let tz = tz.trim(); |
|
|
|
|
|
if tz.eq_ignore_ascii_case("EST") { return "America/New_York" } |
|
|
|
|
|
if tz.eq_ignore_ascii_case("EPT") { return "America/New_York" } |
|
|
|
|
|
if tz.eq_ignore_ascii_case("EDT") { return "America/New_York" } |
|
|
|
|
|
if tz.eq_ignore_ascii_case("US/Eastern") { return "America/New_York" } |
|
|
|
|
|
if tz.eq_ignore_ascii_case("CET") { return "Europe/Brussels" } |
|
|
|
|
|
if tz.eq_ignore_ascii_case("CEST") { return "Europe/Brussels" } |
|
|
|
|
|
if tz.eq_ignore_ascii_case("Brussels") { return "Europe/Brussels" } |
|
|
|
|
|
|
|
|
|
|
|
tz |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn convert(from: &str, to: &str, time: &str, date: Option<NaiveDate>) { |
|
|
|
|
|
let from = convert_common_tz_abbrs(from); |
|
|
|
|
|
let to = convert_common_tz_abbrs(to); |
|
|
|
|
|
|
|
|
|
|
|
let fromtz: Tz = match from.parse() { |
|
|
|
|
|
Ok(tz) => tz, |
|
|
|
|
|
Err(e) => panic!("failed to parse from tz: {} (input = '{}')", e, from), |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let totz: Tz = match to.parse() { |
|
|
|
|
|
Ok(tz) => tz, |
|
|
|
|
|
Err(e) => panic!("failed to parse to tz: {} (input = '{}')", e, to), |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let tm = parse_time(time); |
|
|
|
|
|
|
|
|
|
|
|
let dt: Date<Tz> = match date { |
|
|
|
|
|
Some(dt) => fromtz.from_local_date(&dt).unwrap(), |
|
|
|
|
|
None => Utc::today().with_timezone(&fromtz), |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let src = dt.and_time(tm).unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let dst = src.with_timezone(&totz); |
|
|
|
|
|
|
|
|
|
|
|
let fromtz_str = fromtz.to_string(); |
|
|
|
|
|
let totz_str = totz.to_string(); |
|
|
|
|
|
let n_spaces = std::cmp::max(fromtz_str.len(), totz_str.len()); |
|
|
|
|
|
|
|
|
|
|
|
let indent = " "; |
|
|
|
|
|
println!(); |
|
|
|
|
|
println!("{} {} ... {} ... {}", |
|
|
|
|
|
indent, |
|
|
|
|
|
pad_spaces(fromtz_str, n_spaces), |
|
|
|
|
|
src.format("%a, %b %e"), |
|
|
|
|
|
src.format("%l:%M%P"), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
println!(); |
|
|
|
|
|
|
|
|
|
|
|
println!("{} {} ... {} ... {}", |
|
|
|
|
|
indent, |
|
|
|
|
|
pad_spaces(totz_str, n_spaces), |
|
|
|
|
|
dst.format("%a, %b %e"), |
|
|
|
|
|
dst.format("%l:%M%P"), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
println!(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn pad_spaces<S: ToString>(s: S, n: usize) -> String { |
|
|
|
|
|
let s = s.to_string(); |
|
|
|
|
|
let mut out = String::with_capacity(n); |
|
|
|
|
|
out.push_str(&s); |
|
|
|
|
|
while out.len() < n { |
|
|
|
|
|
out.push_str(" "); |
|
|
|
|
|
} |
|
|
|
|
|
out |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
fn parse_time_like_2pm() { |
|
|
|
|
|
dbg!(parse_time("2pm")); |
|
|
|
|
|
assert_eq!(parse_time("2pm"), NaiveTime::from_hms(14, 0, 0)); |
|
|
|
|
|
assert_eq!(parse_time("7am"), NaiveTime::from_hms(7, 0, 0)); |
|
|
|
|
|
dbg!(HOUR_AMPM.replace("8:30am", "$hr:00$ampm")); |
|
|
|
|
|
assert_eq!(parse_time("8:30am"), NaiveTime::from_hms(8, 30, 0)); |
|
|
|
|
|
} |