use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
#[derive(Debug, Clone, Copy, Default)]
pub enum DateFormatStyle {
#[default]
Long,
Numeric,
}
#[derive(Debug, Clone, Copy, Default)]
pub enum TimeFormatStyle {
#[default]
TwentyFourHour,
TwentyFourHourNoSeconds,
TwelveHour,
TwelveHourNoSeconds,
}
#[derive(Debug, Clone, Copy)]
pub struct FormatOptions {
pub date_style: DateFormatStyle,
pub time_style: TimeFormatStyle,
pub include_time_preposition: bool,
}
impl Default for FormatOptions {
fn default() -> Self {
Self {
date_style: DateFormatStyle::Long,
time_style: TimeFormatStyle::TwentyFourHourNoSeconds,
include_time_preposition: true,
}
}
}
const MONTHS_GENITIVE: [&str; 12] = [
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня",
];
pub fn format_date(date: NaiveDate, style: DateFormatStyle) -> String {
match style {
DateFormatStyle::Long => {
let month_idx = (date.month0() as usize).min(MONTHS_GENITIVE.len() - 1);
format!(
"{} {} {} року",
date.day(),
MONTHS_GENITIVE[month_idx],
date.year()
)
}
DateFormatStyle::Numeric => {
format!("{:02}.{:02}.{}", date.day(), date.month(), date.year())
}
}
}
pub fn format_time(time: NaiveTime, style: TimeFormatStyle) -> String {
match style {
TimeFormatStyle::TwentyFourHour => format!("{:02}:{:02}:{:02}", time.hour(), time.minute(), time.second()),
TimeFormatStyle::TwelveHour => {
let (is_pm, hour12) = time.hour12();
let designator = if is_pm { "вечора" } else { "ранку" };
format!("{:02}:{:02}:{:02} {}", hour12, time.minute(), time.second(), designator)
}
TimeFormatStyle::TwentyFourHourNoSeconds => {
format!("{:02}:{:02}", time.hour(), time.minute())
}
TimeFormatStyle::TwelveHourNoSeconds => {
let (is_pm, hour12) = time.hour12();
let designator = if is_pm { "вечора" } else { "ранку" };
format!("{:02}:{:02} {}", hour12, time.minute(), designator)
}
}
}
pub fn format_datetime(datetime: NaiveDateTime, opts: FormatOptions) -> String {
let date_part = format_date(datetime.date(), opts.date_style);
let time_part = format_time(datetime.time(), opts.time_style);
if opts.include_time_preposition {
format!("{date_part} о {time_part}")
} else {
format!("{date_part} {time_part}")
}
}
pub fn format_local_now(opts: FormatOptions) -> String {
let now = Local::now().naive_local();
format_datetime(now, opts)
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
#[test]
fn formats_long_date() {
let date = NaiveDate::from_ymd_opt(2024, 8, 24).unwrap();
assert_eq!(
format_date(date, DateFormatStyle::Long),
"24 серпня 2024 року"
);
}
#[test]
fn formats_numeric_date() {
let date = NaiveDate::from_ymd_opt(2024, 1, 9).unwrap();
assert_eq!(format_date(date, DateFormatStyle::Numeric), "09.01.2024");
}
#[test]
fn formats_datetime_with_preposition() {
let datetime = NaiveDateTime::new(
NaiveDate::from_ymd_opt(2023, 11, 21).unwrap(),
NaiveTime::from_hms_opt(17, 45, 0).unwrap(),
);
assert_eq!(
format_datetime(datetime, FormatOptions::default()),
"21 листопада 2023 року о 17:45"
);
}
#[test]
fn formats_12_pm_hour_time() {
let time = NaiveTime::from_hms_opt(23, 59, 59).unwrap();
assert_eq!(
format_time(time, TimeFormatStyle::TwelveHour),
"11:59:59 вечора"
);
}
#[test]
fn formats_12_pm_hour_time_no_seconds() {
let time = NaiveTime::from_hms_opt(22, 59, 00).unwrap();
assert_eq!(
format_time(time, TimeFormatStyle::TwelveHourNoSeconds),
"10:59 вечора"
);
}
#[test]
fn formats_12_am_hour_time() {
let time = NaiveTime::from_hms_opt(8, 10, 59).unwrap();
assert_eq!(
format_time(time, TimeFormatStyle::TwelveHour),
"08:10:59 ранку"
);
}
#[test]
fn formats_24_hour_time() {
let time = NaiveTime::from_hms_opt(23, 59, 59).unwrap();
assert_eq!(
format_time(time, TimeFormatStyle::TwentyFourHour),
"23:59:59"
);
}
#[test]
fn formats_24_hour_time_no_seconds() {
let time = NaiveTime::from_hms_opt(23, 59, 59).unwrap();
assert_eq!(
format_time(time, TimeFormatStyle::TwentyFourHourNoSeconds),
"23:59"
);
}
}