use crate::time::tz::TimezoneSource;
use chrono::{DateTime, Utc};
pub const DEFAULT_FORMAT: &str = "%b %d %H:%M:%S";
pub const DEFAULT_FRACTIONAL_DIGITS: usize = 6;
pub fn format_default(now: DateTime<Utc>, tz: &TimezoneSource) -> String {
format_with(DEFAULT_FORMAT, now, tz)
}
pub fn format_with(spec: &str, now: DateTime<Utc>, tz: &TimezoneSource) -> String {
if !spec.contains("%.S") && !spec.contains("%.s") {
return tz.render(now, spec);
}
let micros = now.timestamp_subsec_micros();
let epoch = now.timestamp();
let frac_seconds = format!("{micros:06}");
let frac_epoch = format!("{epoch}.{micros:06}");
let mut out = String::with_capacity(spec.len() + 16);
let mut remaining = spec;
loop {
let pos_big = remaining.find("%.S");
let pos_small = remaining.find("%.s");
let (pos, is_big) = match (pos_big, pos_small) {
(Some(a), Some(b)) if a < b => (a, true),
(Some(_), Some(b)) => (b, false),
(Some(a), None) => (a, true),
(None, Some(b)) => (b, false),
(None, None) => {
out.push_str(&tz.render(now, remaining));
break;
}
};
if pos > 0 {
out.push_str(&tz.render(now, &remaining[..pos]));
}
if is_big {
let seconds = tz.render(now, "%S");
out.push_str(&seconds);
out.push('.');
out.push_str(&frac_seconds);
} else {
out.push_str(&frac_epoch);
}
remaining = &remaining[pos + 3..];
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::{TimeZone, Timelike};
fn fixture_instant() -> DateTime<Utc> {
Utc.with_ymd_and_hms(2026, 5, 22, 14, 30, 45)
.unwrap()
.with_nanosecond(123_456_000)
.unwrap()
}
#[test]
fn default_format_matches_moreutils_default_string() {
assert_eq!(DEFAULT_FORMAT, "%b %d %H:%M:%S");
}
#[test]
fn default_format_under_utc_is_deterministic() {
let rendered = format_default(fixture_instant(), &TimezoneSource::Utc);
assert_eq!(rendered, "May 22 14:30:45");
}
#[test]
fn custom_format_renders_tokens() {
let rendered = format_with("%Y-%m-%d %H:%M:%S", fixture_instant(), &TimezoneSource::Utc);
assert_eq!(rendered, "2026-05-22 14:30:45");
}
#[test]
fn literal_brackets_are_preserved() {
let rendered = format_with("[%H:%M:%S]", fixture_instant(), &TimezoneSource::Utc);
assert_eq!(rendered, "[14:30:45]");
}
#[test]
fn fractional_seconds_token_expands() {
let rendered = format_with("%H:%M:%.S", fixture_instant(), &TimezoneSource::Utc);
assert_eq!(rendered, "14:30:45.123456");
}
#[test]
fn fractional_epoch_token_expands() {
let rendered = format_with("%.s", fixture_instant(), &TimezoneSource::Utc);
let expected_epoch: i64 = fixture_instant().timestamp();
assert_eq!(rendered, format!("{expected_epoch}.123456"));
}
#[test]
fn both_fractional_tokens_in_one_string() {
let rendered = format_with(
"%H:%M:%.S epoch=%.s",
fixture_instant(),
&TimezoneSource::Utc,
);
let expected_epoch: i64 = fixture_instant().timestamp();
assert_eq!(
rendered,
format!("14:30:45.123456 epoch={expected_epoch}.123456"),
);
}
}