@@ -1,6 +1,6 @@ | |||||
[package] | [package] | ||||
name = "tzconvert" | name = "tzconvert" | ||||
version = "1.0.0" | |||||
version = "1.1.0" | |||||
edition = "2018" | edition = "2018" | ||||
authors = ["Jonathan Strong <jstrong@mmcxi.com>"] | authors = ["Jonathan Strong <jstrong@mmcxi.com>"] | ||||
@@ -26,4 +26,4 @@ chrono-tz = { version = "0.5", features = ["serde"] } | |||||
structopt = "0.3" | structopt = "0.3" | ||||
regex = "1.5" | regex = "1.5" | ||||
lazy_static = "1.4" | lazy_static = "1.4" | ||||
colored = "2" |
@@ -13,4 +13,28 @@ debug-build: | |||||
cargo build --bin cet2est | cargo build --bin cet2est | ||||
cargo build --bin est2cet | cargo build --bin est2cet | ||||
send-release remote-machine: release-build | |||||
scp -C target/release/tzconvert {{remote-machine}}:~/.cargo/bin | |||||
scp -C target/release/cet2est {{remote-machine}}:~/.cargo/bin | |||||
scp -C target/release/est2cet {{remote-machine}}:~/.cargo/bin | |||||
cargo +args: | |||||
cargo {{args}} | |||||
run-examples: debug-build | |||||
./target/debug/tzconvert CET EST 2pm | |||||
./target/debug/tzconvert EST CET 1:30pm tue | |||||
./target/debug/tzconvert Africa/Timbuktu America/Jamaica 18:30 --date 2021-11-03 | |||||
./target/debug/tzconvert Africa/Timbuktu America/Jamaica 18:30 sat --date 2021-11-03 | |||||
./target/debug/cet2est 2pm | |||||
./target/debug/cet2est 1:30pm tue | |||||
./target/debug/cet2est 14:15 -d 2021-11-03 | |||||
./target/debug/cet2est 14:15 tue -d 2021-11-03 | |||||
./target/debug/est2cet 2pm | |||||
./target/debug/est2cet 1:30pm tue | |||||
./target/debug/est2cet 14:15 -d 2021-11-03 | |||||
./target/debug/est2cet 14:15 tue -d 2021-11-03 | |||||
@@ -1,19 +1,42 @@ | |||||
use structopt::StructOpt; | use structopt::StructOpt; | ||||
use chrono::prelude::*; | use chrono::prelude::*; | ||||
/// convert CET time to EST | |||||
/// | |||||
/// examples: | |||||
/// | |||||
/// - cet2est 2pm | |||||
/// | |||||
/// - cet2est 1:30pm thu | |||||
/// | |||||
/// - cet2est 14:15 -d 2021-11-03 | |||||
/// | |||||
#[derive(StructOpt, Debug)] | #[derive(StructOpt, Debug)] | ||||
#[structopt(author = env!("CARGO_PKG_AUTHORS"))] | #[structopt(author = env!("CARGO_PKG_AUTHORS"))] | ||||
struct Opt { | struct Opt { | ||||
/// time to convert | |||||
/// time to convert (%H:%M or %I[:%M]%p) | |||||
#[structopt(value_name = "TIME")] | #[structopt(value_name = "TIME")] | ||||
time: String, | time: String, | ||||
/// specify the date on which the time lies. defaults to today. | |||||
#[structopt(long, short)] | |||||
/// optional: specify day of week the time lies, relative to today. | |||||
/// | |||||
/// this will always result in a DATE greater than or equal to | |||||
/// today (in FROM TIMEZONE). | |||||
/// | |||||
/// example: if today is Thursday, Aug. 26, passing DAY OF WEEK "Friday"/"fri" | |||||
/// will result in a DATE of Friday, Aug. 27. | |||||
#[structopt(value_name = "DAY OF WEEK")] | |||||
day: Option<String>, | |||||
/// optional: specify date the time lies. defaults to today. | |||||
/// | |||||
/// when DAY OF WEEK argument is also specified, DAY OF WEEK will be picked relative | |||||
/// to DATE (i.e. the next occuring instance of, on or following DATE). | |||||
#[structopt(long, short, value_name = "DATE")] | |||||
date: Option<NaiveDate>, | date: Option<NaiveDate>, | ||||
} | } | ||||
fn main() { | fn main() { | ||||
let Opt { time, date } = Opt::from_args(); | |||||
tzconvert::convert("Europe/Brussels", "EST", &time, date); | |||||
let Opt { time, date, day } = Opt::from_args(); | |||||
tzconvert::convert("Europe/Brussels", "America/New_York", &time, day, date); | |||||
} | } |
@@ -1,19 +1,42 @@ | |||||
use structopt::StructOpt; | use structopt::StructOpt; | ||||
use chrono::prelude::*; | use chrono::prelude::*; | ||||
/// convert EST time to CET | |||||
/// | |||||
/// examples: | |||||
/// | |||||
/// - est2cet 2pm | |||||
/// | |||||
/// - est2cet 1:30pm thu | |||||
/// | |||||
/// - est2cet 14:15 -d 2021-11-03 | |||||
/// | |||||
#[derive(StructOpt, Debug)] | #[derive(StructOpt, Debug)] | ||||
#[structopt(author = env!("CARGO_PKG_AUTHORS"))] | #[structopt(author = env!("CARGO_PKG_AUTHORS"))] | ||||
struct Opt { | struct Opt { | ||||
/// time to convert | |||||
#[structopt(value_name = "HH:MM")] | |||||
/// time to convert (%H:%M or %I[:%M]%p) | |||||
#[structopt(value_name = "TIME")] | |||||
time: String, | time: String, | ||||
/// specify the date on which the time lies. defaults to today. | |||||
#[structopt(long, short)] | |||||
/// optional: specify day of week the time lies, relative to today. | |||||
/// | |||||
/// this will always result in a DATE greater than or equal to | |||||
/// today (in FROM TIMEZONE). | |||||
/// | |||||
/// example: if today is Thursday, Aug. 26, passing DAY OF WEEK "Friday"/"fri" | |||||
/// will result in a DATE of Friday, Aug. 27. | |||||
#[structopt(value_name = "DAY OF WEEK")] | |||||
day: Option<String>, | |||||
/// optional: specify date the time lies. defaults to today. | |||||
/// | |||||
/// when DAY OF WEEK argument is also specified, DAY OF WEEK will be picked relative | |||||
/// to DATE (i.e. the next occuring instance of, on or following DATE). | |||||
#[structopt(long, short, value_name = "DATE")] | |||||
date: Option<NaiveDate>, | date: Option<NaiveDate>, | ||||
} | } | ||||
fn main() { | fn main() { | ||||
let Opt { time, date } = Opt::from_args(); | |||||
tzconvert::convert("EST", "Europe/Brussels", &time, date); | |||||
let Opt { time, date, day } = Opt::from_args(); | |||||
tzconvert::convert("America/New_York", "Europe/Brussels", &time, day, date); | |||||
} | } |
@@ -1,5 +1,7 @@ | |||||
use std::str::FromStr; | |||||
use chrono::prelude::*; | use chrono::prelude::*; | ||||
use chrono_tz::Tz; | use chrono_tz::Tz; | ||||
use colored::*; | |||||
lazy_static::lazy_static! { | 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_AMPM: regex::Regex = regex::Regex::new(r#"(?P<hr>\d{1,2})\s?(?P<ampm>(am|pm|AM|PM))"#).unwrap(); | ||||
@@ -40,7 +42,7 @@ fn convert_common_tz_abbrs(tz: &str) -> &str { | |||||
tz | tz | ||||
} | } | ||||
pub fn convert(from: &str, to: &str, time: &str, date: Option<NaiveDate>) { | |||||
pub fn convert(from: &str, to: &str, time: &str, day: Option<String>, date: Option<NaiveDate>) { | |||||
let from = convert_common_tz_abbrs(from); | let from = convert_common_tz_abbrs(from); | ||||
let to = convert_common_tz_abbrs(to); | let to = convert_common_tz_abbrs(to); | ||||
@@ -61,6 +63,23 @@ pub fn convert(from: &str, to: &str, time: &str, date: Option<NaiveDate>) { | |||||
None => Utc::today().with_timezone(&fromtz), | None => Utc::today().with_timezone(&fromtz), | ||||
}; | }; | ||||
let dt = match day { | |||||
Some(day_str) => { | |||||
let target_day_of_week: Weekday = Weekday::from_str(&day_str) | |||||
.map_err(|e| { | |||||
eprintln!("failed to parse day (input = '{}')", day_str); | |||||
e | |||||
}).unwrap(); | |||||
let mut cur = dt; | |||||
while cur.weekday() != target_day_of_week { | |||||
cur = cur.succ(); | |||||
} | |||||
cur | |||||
} | |||||
None => dt, | |||||
}; | |||||
let src = dt.and_time(tm).unwrap(); | let src = dt.and_time(tm).unwrap(); | ||||
let dst = src.with_timezone(&totz); | let dst = src.with_timezone(&totz); | ||||
@@ -71,7 +90,7 @@ pub fn convert(from: &str, to: &str, time: &str, date: Option<NaiveDate>) { | |||||
let indent = " "; | let indent = " "; | ||||
println!(); | println!(); | ||||
println!("{} {} ... {} ... {}", | |||||
println!("{}{} ... {} ... {}", | |||||
indent, | indent, | ||||
pad_spaces(fromtz_str, n_spaces), | pad_spaces(fromtz_str, n_spaces), | ||||
src.format("%a, %b %e"), | src.format("%a, %b %e"), | ||||
@@ -80,13 +99,14 @@ pub fn convert(from: &str, to: &str, time: &str, date: Option<NaiveDate>) { | |||||
println!(); | println!(); | ||||
println!("{} {} ... {} ... {}", | |||||
let dst_line = format!("{}{} ... {} ... {}", | |||||
indent, | indent, | ||||
pad_spaces(totz_str, n_spaces), | pad_spaces(totz_str, n_spaces), | ||||
dst.format("%a, %b %e"), | dst.format("%a, %b %e"), | ||||
dst.format("%l:%M%P"), | |||||
dst.format("%l:%M%P").to_string(), | |||||
); | ); | ||||
println!("{}", dst_line.bold()); | |||||
println!(); | println!(); | ||||
} | } | ||||
@@ -3,10 +3,12 @@ use chrono::prelude::*; | |||||
/// timezone converter | /// timezone converter | ||||
/// | /// | ||||
/// Examples: | |||||
/// examples: | |||||
/// | /// | ||||
/// - tzconvert CET EST 2pm | /// - tzconvert CET EST 2pm | ||||
/// | /// | ||||
/// - tzconvert EST CET 1:30pm thu | |||||
/// | |||||
/// - tzconvert Africa/Timbuktu America/Jamaica 18:30 --date 2021-11-03 | /// - tzconvert Africa/Timbuktu America/Jamaica 18:30 --date 2021-11-03 | ||||
/// | /// | ||||
#[derive(StructOpt, Debug)] | #[derive(StructOpt, Debug)] | ||||
@@ -18,16 +20,29 @@ struct Opt { | |||||
/// destination time zone | /// destination time zone | ||||
#[structopt(value_name = "TO TIMEZONE")] | #[structopt(value_name = "TO TIMEZONE")] | ||||
to: String, | to: String, | ||||
/// time to convert (HH:MM or HH[:MM]am/HH[:MM]pm) | |||||
/// time to convert (%H:%M or %I[:%M]%p) | |||||
#[structopt(value_name = "TIME")] | #[structopt(value_name = "TIME")] | ||||
time: String, | time: String, | ||||
/// specify the date on which the time lies; defaults to today | |||||
#[structopt(long, short)] | |||||
/// optional: specify day of week the time lies, relative to today. | |||||
/// | |||||
/// this will always result in a DATE greater than or equal to | |||||
/// today (in FROM TIMEZONE). | |||||
/// | |||||
/// example: if today is Thursday, Aug. 26, passing DAY OF WEEK "Friday"/"fri" | |||||
/// will result in a DATE of Friday, Aug. 27. | |||||
#[structopt(value_name = "DAY OF WEEK")] | |||||
day: Option<String>, | |||||
/// optional: specify date the time lies. defaults to today. | |||||
/// | |||||
/// when DAY OF WEEK argument is also specified, DAY OF WEEK will be picked relative | |||||
/// to DATE (i.e. the next occuring instance of, on or following DATE). | |||||
#[structopt(long, short, value_name = "DATE")] | |||||
date: Option<NaiveDate>, | date: Option<NaiveDate>, | ||||
} | } | ||||
fn main() { | fn main() { | ||||
let Opt { from, to, time, date } = Opt::from_args(); | |||||
tzconvert::convert(&from, &to, &time, date); | |||||
let Opt { from, to, time, date, day } = Opt::from_args(); | |||||
tzconvert::convert(&from, &to, &time, day, date); | |||||
} | } |