use compact_str::CompactString;
use jiff::{Timestamp, Zoned, civil::DateTime, fmt::rfc2822, tz::TimeZone};
use std::str::FromStr;
use crate::pretty_print::FormatOptions;
pub const DEFAULT_DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
pub fn get_local_timezone_or_utc() -> TimeZone {
TimeZone::system()
}
pub fn parse_datetime(input: &str) -> Result<Zoned, jiff::Error> {
if let zoned @ Ok(_) = Zoned::from_str(input) {
return zoned;
}
if let Ok(timestamp) = DateTime::strptime("%Y-%m-%dT%H:%M:%S%.fZ", input)
&& let zoned @ Ok(_) = timestamp.to_zoned(TimeZone::UTC)
{
return zoned;
}
if let dt @ Ok(_) = rfc2822::parse(input) {
return dt;
}
const FORMATS: [&str; 8] = [
"%Y-%m-%d %H:%M:%S%.f",
"%Y/%m/%d %H:%M:%S%.f",
"%Y-%m-%d %H:%M",
"%Y/%m/%d %H:%M",
"%Y-%m-%d %I:%M:%S %p%.f",
"%Y-%m-%d %I:%M %p",
"%Y/%m/%d %I:%M:%S %p%.f",
"%Y/%m/%d %I:%M %p",
];
for format in FORMATS {
if let Ok(dt) = Zoned::strptime(format!("{format} %z"), input) {
return Ok(dt);
}
if let Some((rest, potential_timezone_name)) = input.rsplit_once(' ')
&& let Ok(tz) = TimeZone::get(potential_timezone_name)
&& let Ok(datetime) = DateTime::strptime(format, rest)
{
return datetime.to_zoned(tz);
}
if let Ok(dt) = DateTime::strptime(format, input) {
return dt.to_zoned(get_local_timezone_or_utc());
}
}
Timestamp::from_str(input).map(|ts| ts.to_zoned(get_local_timezone_or_utc()))
}
pub fn to_string(dt: &Zoned, options: &FormatOptions) -> CompactString {
let tz = dt.time_zone();
if dt.time_zone() == &TimeZone::UTC {
use std::fmt::Write;
let mut out = CompactString::new("");
write!(out, "{} UTC", dt.strftime(&options.datetime_format)).unwrap();
out
} else {
use std::fmt::Write;
let mut out = CompactString::with_capacity("2000-01-01 00:00:00 (UTC +00:00)".len());
write!(out, "{}", dt.strftime(&options.datetime_format)).unwrap();
let offset = dt.offset();
let offset_info = tz.to_offset_info(dt.timestamp());
let zone_abbreviation = offset_info.abbreviation();
if zone_abbreviation.starts_with('+') || zone_abbreviation.starts_with('-') {
write!(out, " (UTC {offset})").unwrap();
} else {
write!(out, " {zone_abbreviation} (UTC {offset})").unwrap();
};
if let Some(iana_tz_name) = tz.iana_name() {
write!(out, ", {iana_tz_name}").unwrap();
}
out
}
}