clia_local_time/lib.rs
1//! A LocalTime implementation to set timezone manually.
2
3use std::fmt;
4use std::io;
5use time::{format_description::well_known, formatting::Formattable, OffsetDateTime, UtcOffset};
6use tracing_subscriber::fmt::{format::Writer, time::FormatTime};
7
8/// Formats the current [local time] using a [formatter] from the [`time` crate].
9///
10/// To format the current [UTC time] instead, use the [`UtcTime`] type.
11///
12/// [local time]: https://docs.rs/time/0.3/time/struct.OffsetDateTime.html#method.now_local
13/// [UTC time]: https://docs.rs/time/0.3/time/struct.OffsetDateTime.html#method.now_utc
14/// [formatter]: https://docs.rs/time/0.3/time/formatting/trait.Formattable.html
15/// [`time` crate]: https://docs.rs/time/0.3/time/
16#[derive(Clone, Debug)]
17// #[cfg_attr(docsrs, doc(cfg(all(feature = "time", feature = "local-time"))))]
18// #[cfg(feature = "local-time")]
19pub struct LocalTime<F> {
20 format: F,
21 tz_hours: i8,
22 tz_minutes: i8,
23 tz_seconds: i8,
24}
25
26// === impl LocalTime ===
27
28// #[cfg(feature = "local-time")]
29impl LocalTime<well_known::Rfc3339> {
30 /// Returns a formatter that formats the current [local time] in the
31 /// [RFC 3339] format (a subset of the [ISO 8601] timestamp format).
32 ///
33 /// # Examples
34 ///
35 /// ```
36 /// use tracing_subscriber::fmt::{self, time};
37 ///
38 /// let collector = tracing_subscriber::fmt()
39 /// .with_timer(time::LocalTime::rfc_3339());
40 /// # drop(collector);
41 /// ```
42 ///
43 /// [local time]: https://docs.rs/time/0.3/time/struct.OffsetDateTime.html#method.now_local
44 /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
45 /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
46 pub fn rfc_3339() -> Self {
47 Self::new(well_known::Rfc3339)
48 }
49}
50
51// #[cfg(feature = "local-time")]
52impl<F: Formattable> LocalTime<F> {
53 /// Returns a formatter that formats the current [local time] using the
54 /// [`time` crate] with the provided provided format. The format may be any
55 /// type that implements the [`Formattable`] trait.
56 ///
57 /// This default use UTC.
58 ///
59 /// Typically, the format will be a format description string, or one of the
60 /// `time` crate's [well-known formats].
61 ///
62 /// If the format description is statically known, then the
63 /// [`format_description!`] macro should be used. This is identical to the
64 /// [`time::format_description::parse`] method, but runs at compile-time,
65 /// throwing an error if the format description is invalid. If the desired format
66 /// is not known statically (e.g., a user is providing a format string), then the
67 /// [`time::format_description::parse`] method should be used. Note that this
68 /// method is fallible.
69 ///
70 /// See the [`time` book] for details on the format description syntax.
71 ///
72 /// # Examples
73 ///
74 /// Using the [`format_description!`] macro:
75 ///
76 /// ```
77 /// use tracing_subscriber::fmt::{self, time::LocalTime};
78 /// use time::macros::format_description;
79 ///
80 /// let timer = LocalTime::new(format_description!("[hour]:[minute]:[second]"));
81 /// let collector = tracing_subscriber::fmt()
82 /// .with_timer(timer);
83 /// # drop(collector);
84 /// ```
85 ///
86 /// Using [`time::format_description::parse`]:
87 ///
88 /// ```
89 /// use tracing_subscriber::fmt::{self, time::LocalTime};
90 ///
91 /// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
92 /// .expect("format string should be valid!");
93 /// let timer = LocalTime::new(time_format);
94 /// let collector = tracing_subscriber::fmt()
95 /// .with_timer(timer);
96 /// # drop(collector);
97 /// ```
98 ///
99 /// Using the [`format_description!`] macro requires enabling the `time`
100 /// crate's "macros" feature flag.
101 ///
102 /// Using a [well-known format][well-known formats] (this is equivalent to
103 /// [`LocalTime::rfc_3339`]):
104 ///
105 /// ```
106 /// use tracing_subscriber::fmt::{self, time::LocalTime};
107 ///
108 /// let timer = LocalTime::new(time::format_description::well_known::Rfc3339);
109 /// let collector = tracing_subscriber::fmt()
110 /// .with_timer(timer);
111 /// # drop(collector);
112 /// ```
113 ///
114 /// [local time]: https://docs.rs/time/latest/time/struct.OffsetDateTime.html#method.now_local
115 /// [`time` crate]: https://docs.rs/time/0.3/time/
116 /// [`Formattable`]: https://docs.rs/time/0.3/time/formatting/trait.Formattable.html
117 /// [well-known formats]: https://docs.rs/time/0.3/time/format_description/well_known/index.html
118 /// [`format_description!`]: https://docs.rs/time/0.3/time/macros/macro.format_description.html
119 /// [`time::format_description::parse`]: https://docs.rs/time/0.3/time/format_description/fn.parse.html
120 /// [`time` book]: https://time-rs.github.io/book/api/format-description.html
121 pub fn new(format: F) -> Self {
122 Self {
123 format,
124 tz_hours: 0,
125 tz_minutes: 0,
126 tz_seconds: 0,
127 }
128 }
129
130 /// New with a format and timezone setting.
131 ///
132 /// Timezone format: (tz_hours, tz_minutes, tz_seconds)
133 ///
134 /// # Examples:
135 ///
136 /// ```
137 /// (8, 0, 0)
138 /// (-2, 30, 0)
139 /// ```
140 ///
141 pub fn with_timezone(format: F, tz_hms: (i8, i8, i8)) -> Self {
142 Self {
143 format,
144 tz_hours: tz_hms.0,
145 tz_minutes: tz_hms.1,
146 tz_seconds: tz_hms.2,
147 }
148 }
149}
150
151// #[cfg(feature = "local-time")]
152impl<F> FormatTime for LocalTime<F>
153where
154 F: Formattable,
155{
156 fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
157 //
158 //
159 //
160 // Fix here:
161 //
162 //
163 //
164
165 // let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?;
166 let offset = UtcOffset::from_hms(self.tz_hours, self.tz_minutes, self.tz_seconds)
167 .unwrap_or(UtcOffset::UTC);
168 let now = OffsetDateTime::now_utc().to_offset(offset);
169 format_datetime(now, w, &self.format)
170 }
171}
172
173// #[cfg(feature = "local-time")]
174impl<F> Default for LocalTime<F>
175where
176 F: Formattable + Default,
177{
178 fn default() -> Self {
179 Self::new(F::default())
180 }
181}
182
183fn format_datetime(
184 now: OffsetDateTime,
185 into: &mut Writer<'_>,
186 fmt: &impl Formattable,
187) -> fmt::Result {
188 let mut into = WriteAdaptor::new(into);
189 now.format_into(&mut into, fmt)
190 .map_err(|_| fmt::Error)
191 .map(|_| ())
192}
193
194/// A bridge between `fmt::Write` and `io::Write`.
195///
196/// This is used by the timestamp formatting implementation for the `time`
197/// crate and by the JSON formatter. In both cases, this is needed because
198/// `tracing-subscriber`'s `FormatEvent`/`FormatTime` traits expect a
199/// `fmt::Write` implementation, while `serde_json::Serializer` and `time`'s
200/// `format_into` methods expect an `io::Write`.
201// #[cfg(any(feature = "json", feature = "time"))]
202pub(crate) struct WriteAdaptor<'a> {
203 fmt_write: &'a mut dyn fmt::Write,
204}
205
206// === impl WriteAdaptor ===
207
208// #[cfg(any(feature = "json", feature = "time"))]
209impl<'a> WriteAdaptor<'a> {
210 pub fn new(fmt_write: &'a mut dyn fmt::Write) -> Self {
211 Self { fmt_write }
212 }
213}
214// #[cfg(any(feature = "json", feature = "time"))]
215impl<'a> io::Write for WriteAdaptor<'a> {
216 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
217 let s =
218 std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
219
220 self.fmt_write
221 .write_str(s)
222 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
223
224 Ok(s.as_bytes().len())
225 }
226
227 fn flush(&mut self) -> io::Result<()> {
228 Ok(())
229 }
230}
231
232// #[cfg(any(feature = "json", feature = "time"))]
233impl<'a> fmt::Debug for WriteAdaptor<'a> {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 f.pad("WriteAdaptor { .. }")
236 }
237}
238// === blanket impls ===
239
240#[cfg(test)]
241mod tests {
242 use super::LocalTime;
243 use time::macros::format_description;
244
245 #[test]
246 fn test_init_tracing() {
247 let timer = LocalTime::with_timezone(
248 format_description!(
249 "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]"
250 ),
251 (8, 0, 0),
252 );
253 tracing_subscriber::fmt().with_timer(timer).init();
254 }
255}