1 use crate::fmt::format::Writer; 2 use crate::fmt::time::FormatTime; 3 4 use std::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)] 19 pub struct ChronoLocal { 20 format: Arc<ChronoFmtType>, 21 } 22 23 impl 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 rfc_3339() -> Self29 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. new(format_string: String) -> Self38 pub fn new(format_string: String) -> Self { 39 Self { 40 format: Arc::new(ChronoFmtType::Custom(format_string)), 41 } 42 } 43 } 44 45 impl FormatTime for ChronoLocal { format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result46 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)] 70 pub struct ChronoUtc { 71 format: Arc<ChronoFmtType>, 72 } 73 74 impl 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 rfc_3339() -> Self80 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. new(format_string: String) -> Self89 pub fn new(format_string: String) -> Self { 90 Self { 91 format: Arc::new(ChronoFmtType::Custom(format_string)), 92 } 93 } 94 } 95 96 impl FormatTime for ChronoUtc { format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result97 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)] 112 #[derive(Default)] 113 enum ChronoFmtType { 114 /// Format according to the RFC 3339 convention. 115 #[default] 116 Rfc3339, 117 /// Format according to a custom format string. 118 Custom(String), 119 } 120 121 122 #[cfg(test)] 123 mod tests { 124 use crate::fmt::format::Writer; 125 use crate::fmt::time::FormatTime; 126 127 use std::sync::Arc; 128 129 use super::ChronoFmtType; 130 use super::ChronoLocal; 131 use super::ChronoUtc; 132 133 #[test] test_chrono_format_time_utc_default()134 fn test_chrono_format_time_utc_default() { 135 let mut buf = String::new(); 136 let mut dst: Writer<'_> = Writer::new(&mut buf); 137 assert!(FormatTime::format_time(&ChronoUtc::default(), &mut dst).is_ok()); 138 // e.g. `buf` contains "2023-08-18T19:05:08.662499+00:00" 139 assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); 140 } 141 142 #[test] test_chrono_format_time_utc_custom()143 fn test_chrono_format_time_utc_custom() { 144 let fmt = ChronoUtc { 145 format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), 146 }; 147 let mut buf = String::new(); 148 let mut dst: Writer<'_> = Writer::new(&mut buf); 149 assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); 150 // e.g. `buf` contains "Wed Aug 23 15:53:23 2023" 151 assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); 152 } 153 154 #[test] test_chrono_format_time_local_default()155 fn test_chrono_format_time_local_default() { 156 let mut buf = String::new(); 157 let mut dst: Writer<'_> = Writer::new(&mut buf); 158 assert!(FormatTime::format_time(&ChronoLocal::default(), &mut dst).is_ok()); 159 // e.g. `buf` contains "2023-08-18T14:59:08.662499-04:00". 160 assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); 161 } 162 163 #[test] test_chrono_format_time_local_custom()164 fn test_chrono_format_time_local_custom() { 165 let fmt = ChronoLocal { 166 format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), 167 }; 168 let mut buf = String::new(); 169 let mut dst: Writer<'_> = Writer::new(&mut buf); 170 assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); 171 // e.g. `buf` contains "Wed Aug 23 15:55:46 2023". 172 assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); 173 } 174 } 175