You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
3.8KB

  1. use std::str::FromStr;
  2. use chrono::prelude::*;
  3. use chrono_tz::Tz;
  4. use colored::*;
  5. lazy_static::lazy_static! {
  6. static ref HOUR_AMPM: regex::Regex = regex::Regex::new(r#"(?P<hr>\d{1,2})\s?(?P<ampm>(am|pm|AM|PM))"#).unwrap();
  7. 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();
  8. }
  9. fn parse_time(time_input: &str) -> NaiveTime {
  10. let time: String = time_input.split_whitespace().collect::<Vec<_>>().join("");
  11. match NaiveTime::parse_from_str(&time, "%H:%M") {
  12. Ok(t) => return t,
  13. Err(_e) => {}
  14. }
  15. if HOUR_MINUTE_AMPM.is_match(&time) {
  16. return NaiveTime::parse_from_str(&time, "%I:%M%p").unwrap();
  17. }
  18. let t2 = HOUR_AMPM.replace(&time, "$hr:00$ampm");
  19. match NaiveTime::parse_from_str(&t2, "%I:%M%p") {
  20. Ok(t) => return t,
  21. Err(_e) => {}
  22. }
  23. panic!("failed to parse time -- try HH:MM format (input = '{}')", time)
  24. }
  25. fn convert_common_tz_abbrs(tz: &str) -> &str {
  26. let tz = tz.trim();
  27. if tz.eq_ignore_ascii_case("EST") { return "America/New_York" }
  28. if tz.eq_ignore_ascii_case("EPT") { return "America/New_York" }
  29. if tz.eq_ignore_ascii_case("EDT") { return "America/New_York" }
  30. if tz.eq_ignore_ascii_case("US/Eastern") { return "America/New_York" }
  31. if tz.eq_ignore_ascii_case("CET") { return "Europe/Brussels" }
  32. if tz.eq_ignore_ascii_case("CEST") { return "Europe/Brussels" }
  33. if tz.eq_ignore_ascii_case("Brussels") { return "Europe/Brussels" }
  34. tz
  35. }
  36. pub fn convert(from: &str, to: &str, time: &str, day: Option<String>, date: Option<NaiveDate>) {
  37. let from = convert_common_tz_abbrs(from);
  38. let to = convert_common_tz_abbrs(to);
  39. let fromtz: Tz = match from.parse() {
  40. Ok(tz) => tz,
  41. Err(e) => panic!("failed to parse from tz: {} (input = '{}')", e, from),
  42. };
  43. let totz: Tz = match to.parse() {
  44. Ok(tz) => tz,
  45. Err(e) => panic!("failed to parse to tz: {} (input = '{}')", e, to),
  46. };
  47. let tm = parse_time(time);
  48. let dt: Date<Tz> = match date {
  49. Some(dt) => fromtz.from_local_date(&dt).unwrap(),
  50. None => Utc::today().with_timezone(&fromtz),
  51. };
  52. let dt = match day {
  53. Some(day_str) => {
  54. let target_day_of_week: Weekday = Weekday::from_str(&day_str)
  55. .map_err(|e| {
  56. eprintln!("failed to parse day (input = '{}')", day_str);
  57. e
  58. }).unwrap();
  59. let mut cur = dt;
  60. while cur.weekday() != target_day_of_week {
  61. cur = cur.succ();
  62. }
  63. cur
  64. }
  65. None => dt,
  66. };
  67. let src = dt.and_time(tm).unwrap();
  68. let dst = src.with_timezone(&totz);
  69. let fromtz_str = fromtz.to_string();
  70. let totz_str = totz.to_string();
  71. let n_spaces = std::cmp::max(fromtz_str.len(), totz_str.len());
  72. let indent = " ";
  73. println!();
  74. println!("{}{} ... {} ... {}",
  75. indent,
  76. pad_spaces(fromtz_str, n_spaces),
  77. src.format("%a, %b %e"),
  78. src.format("%l:%M%P"),
  79. );
  80. println!();
  81. let dst_line = format!("{}{} ... {} ... {}",
  82. indent,
  83. pad_spaces(totz_str, n_spaces),
  84. dst.format("%a, %b %e"),
  85. dst.format("%l:%M%P").to_string(),
  86. );
  87. println!("{}", dst_line.bold());
  88. println!();
  89. }
  90. fn pad_spaces<S: ToString>(s: S, n: usize) -> String {
  91. let s = s.to_string();
  92. let mut out = String::with_capacity(n);
  93. out.push_str(&s);
  94. while out.len() < n {
  95. out.push_str(" ");
  96. }
  97. out
  98. }
  99. #[test]
  100. fn parse_time_like_2pm() {
  101. dbg!(parse_time("2pm"));
  102. assert_eq!(parse_time("2pm"), NaiveTime::from_hms(14, 0, 0));
  103. assert_eq!(parse_time("7am"), NaiveTime::from_hms(7, 0, 0));
  104. dbg!(HOUR_AMPM.replace("8:30am", "$hr:00$ampm"));
  105. assert_eq!(parse_time("8:30am"), NaiveTime::from_hms(8, 30, 0));
  106. }