tracing_subscriber/fmt/time/
chrono_crate.rs

1use crate::fmt::format::Writer;
2use crate::fmt::time::FormatTime;
3
4use alloc::{format, string::String, sync::Arc};
5
6/// Formats [local time]s and [UTC time]s with `FormatTime` implementations
7/// that use the [`chrono` crate].
8///
9/// [local time]: [`chrono::offset::Local`]
10/// [UTC time]: [`chrono::offset::Utc`]
11/// [`chrono` crate]: [`chrono`]
12
13/// Formats the current [local time] using a [formatter] from the [`chrono`] crate.
14///
15/// [local time]: chrono::Local::now()
16/// [formatter]: chrono::format
17#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
18#[derive(Debug, Clone, Eq, PartialEq, Default)]
19pub struct ChronoLocal {
20    format: Arc<ChronoFmtType>,
21}
22
23impl ChronoLocal {
24    /// Format the time using the [`RFC 3339`] format
25    /// (a subset of [`ISO 8601`]).
26    ///
27    /// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
28    /// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
29    pub fn rfc_3339() -> Self {
30        Self {
31            format: Arc::new(ChronoFmtType::Rfc3339),
32        }
33    }
34
35    /// Format the time using the given format string.
36    ///
37    /// See [`chrono::format::strftime`] for details on the supported syntax.
38    pub fn new(format_string: String) -> Self {
39        Self {
40            format: Arc::new(ChronoFmtType::Custom(format_string)),
41        }
42    }
43}
44
45impl FormatTime for ChronoLocal {
46    fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result {
47        let t = chrono::Local::now();
48        match self.format.as_ref() {
49            ChronoFmtType::Rfc3339 => {
50                use chrono::format::{Fixed, Item};
51                write!(
52                    w,
53                    "{}",
54                    t.format_with_items(core::iter::once(Item::Fixed(Fixed::RFC3339)))
55                )
56            }
57            ChronoFmtType::Custom(fmt) => {
58                write!(w, "{}", t.format(fmt))
59            }
60        }
61    }
62}
63
64/// Formats the current [UTC time] using a [formatter] from the [`chrono`] crate.
65///
66/// [UTC time]: chrono::Utc::now()
67/// [formatter]: chrono::format
68#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
69#[derive(Debug, Clone, Eq, PartialEq, Default)]
70pub struct ChronoUtc {
71    format: Arc<ChronoFmtType>,
72}
73
74impl ChronoUtc {
75    /// Format the time using the [`RFC 3339`] format
76    /// (a subset of [`ISO 8601`]).
77    ///
78    /// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
79    /// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
80    pub fn rfc_3339() -> Self {
81        Self {
82            format: Arc::new(ChronoFmtType::Rfc3339),
83        }
84    }
85
86    /// Format the time using the given format string.
87    ///
88    /// See [`chrono::format::strftime`] for details on the supported syntax.
89    pub fn new(format_string: String) -> Self {
90        Self {
91            format: Arc::new(ChronoFmtType::Custom(format_string)),
92        }
93    }
94}
95
96impl FormatTime for ChronoUtc {
97    fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result {
98        let t = chrono::Utc::now();
99        match self.format.as_ref() {
100            ChronoFmtType::Rfc3339 => w.write_str(&t.to_rfc3339()),
101            ChronoFmtType::Custom(fmt) => w.write_str(&format!("{}", t.format(fmt))),
102        }
103    }
104}
105
106/// The RFC 3339 format is used by default but a custom format string
107/// can be used. See [`chrono::format::strftime`]for details on
108/// the supported syntax.
109///
110/// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.9/chrono/format/strftime/index.html
111#[derive(Debug, Clone, Eq, PartialEq, Default)]
112enum ChronoFmtType {
113    /// Format according to the RFC 3339 convention.
114    #[default]
115    Rfc3339,
116    /// Format according to a custom format string.
117    Custom(String),
118}
119
120#[cfg(test)]
121mod tests {
122    use crate::fmt::format::Writer;
123    use crate::fmt::time::FormatTime;
124
125    use alloc::{borrow::ToOwned, string::String, sync::Arc};
126
127    use super::ChronoFmtType;
128    use super::ChronoLocal;
129    use super::ChronoUtc;
130
131    #[test]
132    fn test_chrono_format_time_utc_default() {
133        let mut buf = String::new();
134        let mut dst: Writer<'_> = Writer::new(&mut buf);
135        assert!(FormatTime::format_time(&ChronoUtc::default(), &mut dst).is_ok());
136        // e.g. `buf` contains "2023-08-18T19:05:08.662499+00:00"
137        assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok());
138    }
139
140    #[test]
141    fn test_chrono_format_time_utc_custom() {
142        let fmt = ChronoUtc {
143            format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())),
144        };
145        let mut buf = String::new();
146        let mut dst: Writer<'_> = Writer::new(&mut buf);
147        assert!(FormatTime::format_time(&fmt, &mut dst).is_ok());
148        // e.g. `buf` contains "Wed Aug 23 15:53:23 2023"
149        assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok());
150    }
151
152    #[test]
153    fn test_chrono_format_time_local_default() {
154        let mut buf = String::new();
155        let mut dst: Writer<'_> = Writer::new(&mut buf);
156        assert!(FormatTime::format_time(&ChronoLocal::default(), &mut dst).is_ok());
157        // e.g. `buf` contains "2023-08-18T14:59:08.662499-04:00".
158        assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok());
159    }
160
161    #[test]
162    fn test_chrono_format_time_local_custom() {
163        let fmt = ChronoLocal {
164            format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())),
165        };
166        let mut buf = String::new();
167        let mut dst: Writer<'_> = Writer::new(&mut buf);
168        assert!(FormatTime::format_time(&fmt, &mut dst).is_ok());
169        // e.g. `buf` contains "Wed Aug 23 15:55:46 2023".
170        assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok());
171    }
172}