mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-12 07:14:38 +00:00
time_util: add functions to parse format spec and apply it later
In templater, it's easier to handle invalid format string at parsing stage, so I want to build formatting items upfront. Since the formatting items borrow the input string by reference, we need to manually convert them to the owned variants.
This commit is contained in:
parent
731a94e4f2
commit
b6a5d63b09
1 changed files with 45 additions and 3 deletions
|
@ -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<chrono::format::Item<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FormattingItems<'a> {
|
||||
/// Parses strftime-like format string.
|
||||
pub fn parse(format: &'a str) -> Option<Self> {
|
||||
// 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::<Option<_>>()?;
|
||||
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<DateTime<FixedOffset>> {
|
||||
let utc = match Utc.timestamp_opt(
|
||||
context.timestamp.0.div_euclid(1000),
|
||||
|
@ -24,10 +62,14 @@ fn datetime_from_timestamp(context: &Timestamp) -> Option<DateTime<FixedOffset>>
|
|||
}
|
||||
|
||||
pub fn format_absolute_timestamp(timestamp: &Timestamp) -> String {
|
||||
static FORMAT_ITEMS: Lazy<Vec<chrono::format::Item>> =
|
||||
Lazy::new(|| StrftimeItems::new("%Y-%m-%d %H:%M:%S.%3f %:z").collect());
|
||||
static DEFAULT_FORMAT: Lazy<FormattingItems> =
|
||||
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 => "<out-of-range date>".to_string(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue