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 25, 2024
1 parent ba873a2 commit 6d2a269
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 7 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
78 changes: 71 additions & 7 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, 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!(debug, "datetime");

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

pub static DEFAULT_DATETIME_FORMATTER: Lazy<DatetimeFormatter> =
Expand Down Expand Up @@ -100,6 +103,57 @@ 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(format::Fixed::TimezoneName);
let tz_name = match iana_time_zone::get_timezone() {
Ok(tz_name) => tz_name,
_ => {
debug!("Could not get local timezone");
return fallback;
}
};
let tz = match tz_name.parse::<Tz>() {
Ok(tz) => tz,
Err(e) => {
debug!("{}", 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(..) => {
debug!("Timezone is ambiguous");
fallback
}
LocalResult::None => {
debug!("Timezone is none");
fallback
}
}
}
}

impl Formatter for DatetimeFormatter {
fn format(&self, val: &Value, _config: &SharedConfig) -> Result<String, FormatError> {
#[allow(clippy::unnecessary_wraps)]
Expand All @@ -108,20 +162,30 @@ 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 mut new_items = Vec::with_capacity(items.len());
for item in items {
new_items.push(match item {
Item::Fixed(format::Fixed::TimezoneName) => T::timezone_name(&datetime),
item => item.to_owned(),
});
}
match *locale {
Some(locale) => datetime
.format_localized_with_items(new_items.iter(), locale)
.to_string(),
None => datetime.format_with_items(new_items.iter()).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 6d2a269

Please sign in to comment.