diff --git a/src/time_util.rs b/src/time_util.rs index f94517717..1f281d08c 100644 --- a/src/time_util.rs +++ b/src/time_util.rs @@ -3,6 +3,44 @@ use chrono::{DateTime, FixedOffset, LocalResult, TimeZone, Utc}; use jujutsu_lib::backend::Timestamp; use once_cell::sync::Lazy; +/// Parsed formatting items which should never contain an error. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct FormattingItems<'a> { + items: Vec>, +} + +impl<'a> FormattingItems<'a> { + /// Parses strftime-like format string. + pub fn parse(format: &'a str) -> Option { + // If the parsed format contained an error, format().to_string() would panic. + let items = StrftimeItems::new(format) + .map(|item| match item { + chrono::format::Item::Error => None, + _ => Some(item), + }) + .collect::>()?; + Some(FormattingItems { items }) + } + + pub fn into_owned(self) -> FormattingItems<'static> { + use chrono::format::Item; + let items = self + .items + .into_iter() + .map(|item| match item { + Item::Literal(s) => Item::OwnedLiteral(s.into()), + Item::OwnedLiteral(s) => Item::OwnedLiteral(s), + Item::Space(s) => Item::OwnedSpace(s.into()), + Item::OwnedSpace(s) => Item::OwnedSpace(s), + Item::Numeric(spec, pad) => Item::Numeric(spec, pad), + Item::Fixed(spec) => Item::Fixed(spec), + Item::Error => Item::Error, // shouldn't exist, but just copy + }) + .collect(); + FormattingItems { items } + } +} + fn datetime_from_timestamp(context: &Timestamp) -> Option> { let utc = match Utc.timestamp_opt( context.timestamp.0.div_euclid(1000), @@ -24,10 +62,14 @@ fn datetime_from_timestamp(context: &Timestamp) -> Option> } pub fn format_absolute_timestamp(timestamp: &Timestamp) -> String { - static FORMAT_ITEMS: Lazy> = - Lazy::new(|| StrftimeItems::new("%Y-%m-%d %H:%M:%S.%3f %:z").collect()); + static DEFAULT_FORMAT: Lazy = + Lazy::new(|| FormattingItems::parse("%Y-%m-%d %H:%M:%S.%3f %:z").unwrap()); + format_absolute_timestamp_with(timestamp, &DEFAULT_FORMAT) +} + +pub fn format_absolute_timestamp_with(timestamp: &Timestamp, format: &FormattingItems) -> String { match datetime_from_timestamp(timestamp) { - Some(datetime) => datetime.format_with_items(FORMAT_ITEMS.iter()).to_string(), + Some(datetime) => datetime.format_with_items(format.items.iter()).to_string(), None => "".to_string(), } }