env_logger/fmt/
humantime.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::fmt;
use std::time::SystemTime;

use crate::fmt::{Formatter, TimestampPrecision};

impl Formatter {
    /// Get a [`Timestamp`] for the current date and time in UTC.
    ///
    /// # Examples
    ///
    /// Include the current timestamp with the log record:
    ///
    /// ```
    /// use std::io::Write;
    ///
    /// let mut builder = env_logger::Builder::new();
    ///
    /// builder.format(|buf, record| {
    ///     let ts = buf.timestamp();
    ///
    ///     writeln!(buf, "{}: {}: {}", ts, record.level(), record.args())
    /// });
    /// ```
    pub fn timestamp(&self) -> Timestamp {
        Timestamp {
            time: SystemTime::now(),
            precision: TimestampPrecision::Seconds,
        }
    }

    /// Get a [`Timestamp`] for the current date and time in UTC with full
    /// second precision.
    pub fn timestamp_seconds(&self) -> Timestamp {
        Timestamp {
            time: SystemTime::now(),
            precision: TimestampPrecision::Seconds,
        }
    }

    /// Get a [`Timestamp`] for the current date and time in UTC with
    /// millisecond precision.
    pub fn timestamp_millis(&self) -> Timestamp {
        Timestamp {
            time: SystemTime::now(),
            precision: TimestampPrecision::Millis,
        }
    }

    /// Get a [`Timestamp`] for the current date and time in UTC with
    /// microsecond precision.
    pub fn timestamp_micros(&self) -> Timestamp {
        Timestamp {
            time: SystemTime::now(),
            precision: TimestampPrecision::Micros,
        }
    }

    /// Get a [`Timestamp`] for the current date and time in UTC with
    /// nanosecond precision.
    pub fn timestamp_nanos(&self) -> Timestamp {
        Timestamp {
            time: SystemTime::now(),
            precision: TimestampPrecision::Nanos,
        }
    }
}

/// An [RFC3339] formatted timestamp.
///
/// The timestamp implements [`Display`] and can be written to a [`Formatter`].
///
/// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt
/// [`Display`]: std::fmt::Display
pub struct Timestamp {
    time: SystemTime,
    precision: TimestampPrecision,
}

impl fmt::Debug for Timestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        /// A `Debug` wrapper for `Timestamp` that uses the `Display` implementation.
        struct TimestampValue<'a>(&'a Timestamp);

        impl fmt::Debug for TimestampValue<'_> {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                fmt::Display::fmt(&self.0, f)
            }
        }

        f.debug_tuple("Timestamp")
            .field(&TimestampValue(self))
            .finish()
    }
}

impl fmt::Display for Timestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let Ok(ts) = jiff::Timestamp::try_from(self.time) else {
            return Err(fmt::Error);
        };

        match self.precision {
            TimestampPrecision::Seconds => write!(f, "{ts:.0}"),
            TimestampPrecision::Millis => write!(f, "{ts:.3}"),
            TimestampPrecision::Micros => write!(f, "{ts:.6}"),
            TimestampPrecision::Nanos => write!(f, "{ts:.9}"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Timestamp;
    use crate::TimestampPrecision;

    #[test]
    fn test_display_timestamp() {
        let mut ts = Timestamp {
            time: std::time::SystemTime::UNIX_EPOCH,
            precision: TimestampPrecision::Nanos,
        };

        assert_eq!("1970-01-01T00:00:00.000000000Z", format!("{ts}"));

        ts.precision = TimestampPrecision::Micros;
        assert_eq!("1970-01-01T00:00:00.000000Z", format!("{ts}"));

        ts.precision = TimestampPrecision::Millis;
        assert_eq!("1970-01-01T00:00:00.000Z", format!("{ts}"));

        ts.precision = TimestampPrecision::Seconds;
        assert_eq!("1970-01-01T00:00:00Z", format!("{ts}"));
    }
}