use core::fmt::{self, Write as _};
use chrono::{DateTime, TimeZone, Utc};
pub const LOG_TIMESTAMP_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.3f%:z";
pub const LOG_STAMP_BYTES: usize = 30;
const STAMP_HEAD: &str = "%Y-%m-%dT%H:%M:%S";
const STAMP_TAIL: &str = "%:z";
#[track_caller]
pub fn stamp_into(buf: &mut String) {
Stamper::default().stamp_into(buf);
}
#[derive(Debug, Clone, Default)]
pub struct Stamper {
second: Option<i64>,
head: String,
tail: String,
}
impl Stamper {
#[track_caller]
pub fn stamp_into(&mut self, buf: &mut String) {
self.stamp_at(Utc::now(), &chrono::Local, buf);
}
#[track_caller]
fn stamp_at<Tz>(&mut self, now: DateTime<Utc>, zone: &Tz, buf: &mut String)
where
Tz: TimeZone,
Tz::Offset: fmt::Display,
{
let second = now.timestamp();
if self.second != Some(second) {
let local = now.with_timezone(zone);
self.head.clear();
self.tail.clear();
let _ = write!(self.head, "{}", local.format(STAMP_HEAD));
let _ = write!(self.tail, "{} ", local.format(STAMP_TAIL));
self.second = Some(second);
}
let start = buf.len();
buf.push_str(&self.head);
let _ = write!(buf, ".{:03}", now.timestamp_subsec_millis());
buf.push_str(&self.tail);
debug_assert_eq!(
buf.len() - start,
LOG_STAMP_BYTES,
"the stamp's width is fixed and readers strip it by count"
);
}
}
#[must_use]
pub fn strip(line: &str) -> &str {
let Some((stamp, rest)) = line.split_at_checked(LOG_STAMP_BYTES) else {
return line;
};
let Some(stamp) = stamp.strip_suffix(' ') else {
return line;
};
if chrono::DateTime::parse_from_rfc3339(stamp).is_ok() {
rest
} else {
line
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_stamped_line_comes_back_the_way_it_went_in() {
let mut written = String::new();
stamp_into(&mut written);
written.push_str("the sheep said this");
assert_eq!(written.len(), LOG_STAMP_BYTES + "the sheep said this".len());
assert_eq!(strip(&written), "the sheep said this");
}
#[test]
fn a_stamper_writes_what_the_whole_format_writes() {
assert_eq!(
format!("{STAMP_HEAD}%.3f{STAMP_TAIL}"),
LOG_TIMESTAMP_FORMAT
);
let zones = [
chrono_tz::UTC,
chrono_tz::America::New_York,
chrono_tz::Asia::Kolkata,
chrono_tz::Australia::Lord_Howe,
];
let instants = [
-1_500,
1_788_351_751_001,
1_788_351_751_999,
1_772_953_199_999,
1_772_953_200_000,
1_793_512_799_999,
1_793_512_800_000,
1_775_314_799_999,
1_775_314_800_000,
];
for zone in zones {
let mut stamper = Stamper::default();
for millis in instants {
let now = DateTime::from_timestamp_millis(millis).expect("in chrono's range");
let mut written = String::new();
stamper.stamp_at(now, &zone, &mut written);
let whole = format!("{} ", now.with_timezone(&zone).format(LOG_TIMESTAMP_FORMAT));
assert_eq!(written, whole, "{millis} in {zone}");
}
}
}
#[test]
fn an_unstamped_line_is_left_exactly_as_it_is() {
for line in [
"",
"short",
"an old line from before shep stamped anything at all",
"2026-99-99T99:99:99.999+99:99 nonsense in the shape of a stamp",
"############################# looks like a prefix, parses as nothing",
] {
assert_eq!(strip(line), line, "{line:?} carries no stamp to strip");
}
}
#[test]
fn a_line_split_mid_character_is_returned_whole() {
let line = format!("{}x", "é".repeat(LOG_STAMP_BYTES));
assert_eq!(strip(&line), line);
}
}