add an option to specify local timezone
This commit is contained in:
parent
85cf3e546e
commit
3cec3953c0
|
|
@ -7,7 +7,6 @@ use std::fmt;
|
||||||
pub enum CalendarError {
|
pub enum CalendarError {
|
||||||
UnknownTimezone(String),
|
UnknownTimezone(String),
|
||||||
WrongTimeFormat,
|
WrongTimeFormat,
|
||||||
WrongDateFormat,
|
|
||||||
TimezoneConversionFailed(String),
|
TimezoneConversionFailed(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,7 +15,6 @@ impl fmt::Display for CalendarError {
|
||||||
let message = match *self {
|
let message = match *self {
|
||||||
Self::UnknownTimezone(ref tz) => format!("Unknown timezone: {}", tz),
|
Self::UnknownTimezone(ref tz) => format!("Unknown timezone: {}", tz),
|
||||||
Self::WrongTimeFormat => "Wrong time format".to_string(),
|
Self::WrongTimeFormat => "Wrong time format".to_string(),
|
||||||
Self::WrongDateFormat => "Wrong date format".to_string(),
|
|
||||||
Self::TimezoneConversionFailed(ref tz) => {
|
Self::TimezoneConversionFailed(ref tz) => {
|
||||||
format!("Could not convert time to timezone: {}", tz)
|
format!("Could not convert time to timezone: {}", tz)
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +26,11 @@ impl fmt::Display for CalendarError {
|
||||||
|
|
||||||
impl Error for CalendarError {}
|
impl Error for CalendarError {}
|
||||||
|
|
||||||
|
pub struct SimpleTimezone {
|
||||||
|
pub name: String,
|
||||||
|
pub offset: FixedOffset,
|
||||||
|
}
|
||||||
|
|
||||||
fn create_calendar_strings(time: &NaiveTime, padding_hrs: i8) -> Vec<String> {
|
fn create_calendar_strings(time: &NaiveTime, padding_hrs: i8) -> Vec<String> {
|
||||||
let start = -padding_hrs;
|
let start = -padding_hrs;
|
||||||
let end = padding_hrs;
|
let end = padding_hrs;
|
||||||
|
|
@ -37,32 +40,61 @@ fn create_calendar_strings(time: &NaiveTime, padding_hrs: i8) -> Vec<String> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_local_timezone_offset(
|
||||||
|
time: &NaiveTime,
|
||||||
|
timezone: &String,
|
||||||
|
) -> Result<SimpleTimezone, CalendarError> {
|
||||||
|
if timezone == "Local" {
|
||||||
|
Ok(SimpleTimezone {
|
||||||
|
name: "Local".to_string(),
|
||||||
|
offset: Local::now()
|
||||||
|
.with_time(time.clone())
|
||||||
|
.single()
|
||||||
|
.ok_or(CalendarError::WrongTimeFormat)?
|
||||||
|
.offset()
|
||||||
|
.clone(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let offset = timezone
|
||||||
|
.parse::<Tz>()
|
||||||
|
.map_err(|_| CalendarError::UnknownTimezone(timezone.to_string()))?
|
||||||
|
.offset_from_utc_datetime(&Utc::now().naive_utc());
|
||||||
|
|
||||||
|
Ok(SimpleTimezone {
|
||||||
|
name: timezone.to_string(),
|
||||||
|
offset: offset.fix(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_timetable(
|
pub fn create_timetable(
|
||||||
target_time: &String,
|
target_time: &NaiveTime,
|
||||||
target_date: &String,
|
target_date: &NaiveDate,
|
||||||
|
local_timezone: &FixedOffset,
|
||||||
in_timezone: &String,
|
in_timezone: &String,
|
||||||
padding_hrs: i8,
|
padding_hrs: i8,
|
||||||
) -> Result<Vec<String>, CalendarError> {
|
) -> Result<Vec<String>, CalendarError> {
|
||||||
|
let datetime = local_timezone.from_local_datetime(&NaiveDateTime::new(
|
||||||
|
target_date.clone(),
|
||||||
|
target_time.clone(),
|
||||||
|
));
|
||||||
|
let time_in_timezone = if in_timezone == "Local" {
|
||||||
|
Local::now()
|
||||||
|
.with_time(target_time.clone())
|
||||||
|
.single()
|
||||||
|
.ok_or(CalendarError::WrongTimeFormat)?
|
||||||
|
.naive_local()
|
||||||
|
} else {
|
||||||
let parsed_timezone = in_timezone
|
let parsed_timezone = in_timezone
|
||||||
.parse::<Tz>()
|
.parse::<Tz>()
|
||||||
.map_err(|_| CalendarError::UnknownTimezone(in_timezone.clone()))?;
|
.map_err(|_| CalendarError::UnknownTimezone(in_timezone.clone()))?;
|
||||||
|
|
||||||
let parsed_time = if let Ok(hour) = target_time.parse::<u32>() {
|
datetime
|
||||||
NaiveTime::from_hms_opt(hour, 0, 0).ok_or(CalendarError::WrongTimeFormat)?
|
|
||||||
} else {
|
|
||||||
NaiveTime::parse_from_str(target_time, "%H:%M")
|
|
||||||
.map_err(|_| CalendarError::WrongTimeFormat)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let parsed_date = NaiveDate::parse_from_str(target_date, "%d-%m-%Y")
|
|
||||||
.or_else(|_| NaiveDate::parse_from_str(target_date, "%d-%m"))
|
|
||||||
.map_err(|_| CalendarError::WrongDateFormat)?;
|
|
||||||
|
|
||||||
let datetime = Local.from_local_datetime(&NaiveDateTime::new(parsed_date, parsed_time));
|
|
||||||
let time_in_timezone = datetime
|
|
||||||
.single()
|
.single()
|
||||||
.ok_or(CalendarError::TimezoneConversionFailed(in_timezone.clone()))?
|
.ok_or(CalendarError::TimezoneConversionFailed(in_timezone.clone()))?
|
||||||
.with_timezone(&parsed_timezone);
|
.with_timezone(&parsed_timezone)
|
||||||
|
.naive_local()
|
||||||
|
};
|
||||||
|
|
||||||
Ok(create_calendar_strings(
|
Ok(create_calendar_strings(
|
||||||
&time_in_timezone.time(),
|
&time_in_timezone.time(),
|
||||||
|
|
|
||||||
77
src/cli.rs
77
src/cli.rs
|
|
@ -1,5 +1,13 @@
|
||||||
use super::calendar::create_timetable;
|
use crate::calendar::CalendarError;
|
||||||
|
|
||||||
|
use super::calendar::{create_timetable, get_local_timezone_offset, get_todays_date};
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use chrono::NaiveTime;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
|
|
@ -10,19 +18,78 @@ pub struct Args {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub date: Option<String>,
|
pub date: Option<String>,
|
||||||
|
|
||||||
|
#[arg(short, long, default_value = "Local")]
|
||||||
|
pub local: String,
|
||||||
|
|
||||||
pub time: String,
|
pub time: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_timezones(timezones: &Vec<String>, time: &String, date: &String) {
|
#[derive(PartialEq, Debug)]
|
||||||
let max_len = timezones.iter().map(|t| t.len()).max().unwrap_or(0) as u32;
|
pub enum CliError {
|
||||||
for timezone in timezones {
|
CouldNotParseTime,
|
||||||
|
CouldNotParseDate,
|
||||||
|
CouldNoGenerateTimetable(CalendarError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CliError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let message = match *self {
|
||||||
|
Self::CouldNotParseTime => "Couldn't parse time".to_string(),
|
||||||
|
Self::CouldNotParseDate => "Couldn't parse date".to_string(),
|
||||||
|
Self::CouldNoGenerateTimetable(ref er) => {
|
||||||
|
format!("Could not generate timetable: {}", er)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write_str(&message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for CliError {}
|
||||||
|
|
||||||
|
pub fn print_timezones(
|
||||||
|
timezones: &Vec<String>,
|
||||||
|
time: &String,
|
||||||
|
date: Option<&String>,
|
||||||
|
local_timezone: &String,
|
||||||
|
) -> Result<(), CliError> {
|
||||||
|
let todays_date = &get_todays_date();
|
||||||
|
let date = date.unwrap_or(todays_date);
|
||||||
|
|
||||||
|
let parsed_time = if let Ok(hour) = time.parse::<u32>() {
|
||||||
|
NaiveTime::from_hms_opt(hour, 0, 0).ok_or(CliError::CouldNotParseTime)?
|
||||||
|
} else {
|
||||||
|
NaiveTime::parse_from_str(time, "%H:%M").map_err(|_| CliError::CouldNotParseTime)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed_date = NaiveDate::parse_from_str(date, "%d-%m-%Y")
|
||||||
|
.or_else(|_| NaiveDate::parse_from_str(date, "%d-%m"))
|
||||||
|
.map_err(|_| CliError::CouldNotParseDate)?;
|
||||||
|
|
||||||
|
let local_timezone_offset = &get_local_timezone_offset(&parsed_time, local_timezone)
|
||||||
|
.map_err(|e| CliError::CouldNoGenerateTimetable(e))?;
|
||||||
|
|
||||||
|
let all_timezones: Vec<&String> = iter::once(&local_timezone_offset.name)
|
||||||
|
.chain(timezones)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let max_len = all_timezones.iter().map(|t| t.len()).max().unwrap_or(0) as u32;
|
||||||
|
|
||||||
|
for timezone in all_timezones {
|
||||||
let name_padding = (0..(max_len - timezone.len() as u32))
|
let name_padding = (0..(max_len - timezone.len() as u32))
|
||||||
.map(|_| " ".to_string())
|
.map(|_| " ".to_string())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("");
|
.join("");
|
||||||
match create_timetable(time, date, timezone, 5) {
|
match create_timetable(
|
||||||
|
&parsed_time,
|
||||||
|
&parsed_date,
|
||||||
|
&local_timezone_offset.offset,
|
||||||
|
timezone,
|
||||||
|
5,
|
||||||
|
) {
|
||||||
Ok(timetable) => println!("{}:{}\t{}", timezone, name_padding, timetable.join("\t")),
|
Ok(timetable) => println!("{}:{}\t{}", timezone, name_padding, timetable.join("\t")),
|
||||||
Err(error) => panic!("Failed: {}", error),
|
Err(error) => panic!("Failed: {}", error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
mod calendar;
|
mod calendar;
|
||||||
mod cli;
|
mod cli;
|
||||||
|
|
||||||
use calendar::get_todays_date;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::{Args, print_timezones};
|
use cli::{Args, print_timezones};
|
||||||
|
|
||||||
|
|
@ -9,7 +8,10 @@ fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
if args.timezone.len() > 0 {
|
if args.timezone.len() > 0 {
|
||||||
let date = args.date.unwrap_or(get_todays_date());
|
let res = print_timezones(&args.timezone, &args.time, args.date.as_ref(), &args.local);
|
||||||
print_timezones(&args.timezone, &args.time, &date);
|
|
||||||
|
if let Err(error) = res {
|
||||||
|
println!("Error: {}", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue