Skip to content

Commit

Permalink
formatter[datetime]: fix timezone abbreviation (%Z)
Browse files Browse the repository at this point in the history
  • Loading branch information
bim9262 committed May 26, 2024
1 parent ba873a2 commit 49ba54f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ env_logger = "0.11"
futures = { version = "0.3", default-features = false }
glob = { version = "0.3.1", optional = true }
hyper = "0.14"
iana-time-zone = "0.1.60"
icu_calendar = { version = "1.3.0", optional = true }
icu_datetime = { version = "1.3.0", optional = true }
icu_locid = { version = "1.3.0", optional = true }
Expand Down
86 changes: 78 additions & 8 deletions src/formatting/formatter/datetime.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use chrono::format::{Item, StrftimeItems};
use chrono::{DateTime, Local, Locale, TimeZone};
use chrono::format::{Fixed, Item, StrftimeItems};
use chrono::{DateTime, Datelike, Local, LocalResult, Locale, TimeZone, Timelike};
use chrono_tz::{OffsetName, Tz};
use once_cell::sync::Lazy;

use std::fmt::Display;

use super::*;

make_log_macro!(error, "datetime");

const DEFAULT_DATETIME_FORMAT: &str = "%a %d/%m %R";

pub static DEFAULT_DATETIME_FORMATTER: Lazy<DatetimeFormatter> =
Expand Down Expand Up @@ -100,6 +103,66 @@ impl DatetimeFormatter {
}
}

pub(crate) trait TimezoneName {
fn timezone_name(datetime: &DateTime<Self>) -> Item
where
Self: TimeZone;
}

impl TimezoneName for Tz {
fn timezone_name(datetime: &DateTime<Tz>) -> Item {
Item::OwnedLiteral(datetime.offset().abbreviation().into())
}
}

impl TimezoneName for Local {
fn timezone_name(datetime: &DateTime<Local>) -> Item {
let fallback = Item::Fixed(Fixed::TimezoneName);
let Ok(tz_name) = iana_time_zone::get_timezone() else {
error!("Could not get local timezone");
return fallback;
};
let tz = match tz_name.parse::<Tz>() {
Ok(tz) => tz,
Err(e) => {
error!("{}", e);
return fallback;
}
};

match tz.with_ymd_and_hms(
datetime.year(),
datetime.month(),
datetime.day(),
datetime.hour(),
datetime.minute(),
datetime.second(),
) {
LocalResult::Single(tz_datetime) => Tz::timezone_name(&tz_datetime).to_owned(),
LocalResult::Ambiguous(..) => {
error!("Timezone is ambiguous");
fallback
}
LocalResult::None => {
error!("Timezone is none");
fallback
}
}
}
}

fn borrow_item<'a>(item: &'a Item) -> Item<'a> {
match item {
Item::Literal(s) => Item::Literal(s),
Item::OwnedLiteral(s) => Item::Literal(s),
Item::Space(s) => Item::Space(s),
Item::OwnedSpace(s) => Item::Space(s),
Item::Numeric(n, p) => Item::Numeric(n.clone(), *p),
Item::Fixed(f) => Item::Fixed(f.clone()),
Item::Error => Item::Error,
}
}

impl Formatter for DatetimeFormatter {
fn format(&self, val: &Value, _config: &SharedConfig) -> Result<String, FormatError> {
#[allow(clippy::unnecessary_wraps)]
Expand All @@ -108,20 +171,27 @@ impl Formatter for DatetimeFormatter {
datetime: DateTime<T>,
) -> Result<String, FormatError>
where
T: TimeZone,
T: TimeZone + TimezoneName,
T::Offset: Display,
{
Ok(match this {
DatetimeFormatter::Chrono { items, locale } => match *locale {
Some(locale) => datetime.format_localized_with_items(items.iter(), locale),
None => datetime.format_with_items(items.iter()),
DatetimeFormatter::Chrono { items, locale } => {
let new_items = items.iter().map(|item| match item {
Item::Fixed(Fixed::TimezoneName) => T::timezone_name(&datetime),
item => borrow_item(item),
});
match *locale {
Some(locale) => datetime
.format_localized_with_items(new_items, locale)
.to_string(),
None => datetime.format_with_items(new_items).to_string(),
}
}
.to_string(),
#[cfg(feature = "icu_calendar")]
DatetimeFormatter::Icu { locale, length } => {
use chrono::Datelike as _;
let date = icu_calendar::Date::try_new_iso_date(
datetime.year_ce().1 as i32,
datetime.year(),
datetime.month() as u8,
datetime.day() as u8,
)
Expand Down

0 comments on commit 49ba54f

Please sign in to comment.