use chrono::{Datelike, NaiveDate, NaiveDateTime, ParseError};
pub fn date2iso(date: &NaiveDate) -> String {
date.format("%Y-%m-%d").to_string()
}
pub fn iso2date(s: &str) -> Result<NaiveDate, ParseError> {
let date = NaiveDate::parse_from_str(s, "%Y-%m-%d")?;
if date2iso(&date) == s {
Ok(date)
} else {
NaiveDate::parse_from_str("xxxx-xx-xx", "%Y-%m-%d")
}
}
pub fn datetime2iso(dt: &NaiveDateTime) -> String {
dt.format("%Y-%m-%d %H:%M:%S").to_string()
}
pub fn iso2datetime(s: &str) -> Result<NaiveDateTime, ParseError> {
let dt = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S")?;
if datetime2iso(&dt) == s {
Ok(dt)
} else {
NaiveDateTime::parse_from_str("xxxx-xx-xx xx:xx:xx", "%Y-%m-%d %H:%M:%S")
}
}
pub fn parse_work_duration_to_minutes(s: &str) -> i64 {
let mut hours = 0;
let mut minutes = 0;
let parts: Vec<&str> = s.split_whitespace().collect();
for part in parts {
if part.ends_with('h') {
if let Ok(h) = part.trim_end_matches('h').parse::<i64>() {
hours = h;
}
} else if part.ends_with('m')
&& let Ok(m) = part.trim_end_matches('m').parse::<i64>()
{
minutes = m;
}
}
hours * 60 + minutes
}
pub fn mins2hhmm(minutes: i32) -> String {
let hours = minutes / 60;
let mins = minutes % 60;
format!("{:02}:{:02}", hours, mins)
}
pub fn make_separator(ch: char, width: usize, align: usize) -> String {
let line = ch.to_string().repeat(width);
format!("{:>align$}", line, align = align)
}
pub fn print_separator(ch: char, width: usize, align: usize) {
println!("{}", make_separator(ch, width, align));
}
pub fn describe_position(pos: &str) -> (String, String) {
match pos {
"O" => {
let label = "Office".to_string();
let colored = "\x1b[34m".to_string();
(label, colored)
}
"R" => {
let label = "Remote".to_string();
let colored = "\x1b[36m".to_string();
(label, colored)
}
"C" => {
let label = "On-site (Client)".to_string();
let colored = "\x1b[33m".to_string();
(label, colored)
}
"H" => {
let label = "Holiday".to_string();
let colored = "\x1b[45;97;1m".to_string();
(label, colored)
}
"M" => {
let label = "Mixed".to_string();
let colored = "\x1b[35m".to_string(); (label, colored)
}
_ => {
let label = pos.to_string();
(label.clone(), "\x1b[0m".to_string()) }
}
}
pub fn is_last_day_of_month(date_str: &str) -> bool {
match NaiveDate::parse_from_str(date_str, "%Y-%m-%d") {
Ok(d) => {
let (y, m) = (d.year(), d.month());
let next_month_first = if m == 12 {
NaiveDate::from_ymd_opt(y + 1, 1, 1)
} else {
NaiveDate::from_ymd_opt(y, m + 1, 1)
};
if let Some(next_first) = next_month_first {
let last_day = next_first - chrono::Duration::days(1);
d == last_day
} else {
false
}
}
Err(_) => false,
}
}