diff --git a/src/uu/dmesg/src/dmesg.rs b/src/uu/dmesg/src/dmesg.rs index 51153d5..1298d09 100644 --- a/src/uu/dmesg/src/dmesg.rs +++ b/src/uu/dmesg/src/dmesg.rs @@ -153,8 +153,15 @@ impl Dmesg<'_> { fn print_normal(&self) { if let Some(records) = &self.records { + let mut reltime_formatter = time_formatter::ReltimeFormatter::new(); for record in records { match self.time_format { + TimeFormat::Reltime => { + print!( + "[{}] ", + reltime_formatter.format(record.timestamp_us as i64) + ) + } TimeFormat::Ctime => { print!("[{}] ", time_formatter::ctime(record.timestamp_us)) } diff --git a/src/uu/dmesg/src/time_formatter.rs b/src/uu/dmesg/src/time_formatter.rs index 23e0bbe..26b4833 100644 --- a/src/uu/dmesg/src/time_formatter.rs +++ b/src/uu/dmesg/src/time_formatter.rs @@ -23,6 +23,60 @@ pub fn iso(timestamp_us: u64) -> String { date_time.format("%Y-%m-%dT%H:%M:%S,%6f%:z").to_string() } +pub struct ReltimeFormatter { + state: State, + prev_timestamp_us: i64, + previous_unix_timestamp: i64, +} + +pub enum State { + Initial, + AfterBoot, + Delta, +} + +impl ReltimeFormatter { + pub fn new() -> Self { + ReltimeFormatter { + state: State::Initial, + prev_timestamp_us: 0, + previous_unix_timestamp: 0, + } + } + + pub fn format(&mut self, timestamp_us: i64) -> String { + let date_time = boot_time() + .checked_add_signed(TimeDelta::microseconds(timestamp_us)) + .unwrap(); + let unix_timestamp = date_time.timestamp(); + let minute_changes = (unix_timestamp / 60) != (self.previous_unix_timestamp / 60); + let format_res = match self.state { + State::Initial => date_time.format("%b%d %H:%M").to_string(), + _ if minute_changes => date_time.format("%b%d %H:%M").to_string(), + State::AfterBoot => Self::delta(0), + State::Delta => Self::delta(timestamp_us - self.prev_timestamp_us), + }; + self.prev_timestamp_us = timestamp_us; + self.previous_unix_timestamp = unix_timestamp; + self.state = match self.state { + State::Initial if timestamp_us == 0 => State::AfterBoot, + _ => State::Delta, + }; + format_res + } + + fn delta(delta_us: i64) -> String { + let seconds = i64::abs(delta_us / 1000000); + let sub_seconds = i64::abs(delta_us % 1000000); + let sign = if delta_us >= 0 { 1.0 } else { -1.0 }; + format!( + "{:>+4.0}.{:0>6}", + sign * f64::from(seconds as i32), + sub_seconds + ) + } +} + static BOOT_TIME: OnceLock<DateTime<FixedOffset>> = OnceLock::new(); #[cfg(feature = "fixed-boot-time")]