use chrono::prelude::*;
use chrono_tz::Tz;
lazy_static::lazy_static! {
static ref HOUR_AMPM: regex::Regex = regex::Regex::new(r#"(?P
\d{1,2})\s?(?P(am|pm|AM|PM))"#).unwrap();
static ref HOUR_MINUTE_AMPM: regex::Regex = regex::Regex::new(r#"(?P
\d{1,2}):(?P\d{2})\s?(?P(am|pm|AM|PM))"#).unwrap();
}
fn parse_time(time_input: &str) -> NaiveTime {
let time: String = time_input.split_whitespace().collect::>().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) {
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 = 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: 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));
}