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 enum ChronoFmtType { 113 /// Format according to the RFC 3339 convention. 114 Rfc3339, 115 /// Format according to a custom format string. 116 Custom(String), 117 } 118 119 impl Default for ChronoFmtType { default() -> Self120 fn default() -> Self { 121 ChronoFmtType::Rfc3339 122 } 123 } 124 125 #[cfg(test)] 126 mod tests { 127 use crate::fmt::format::Writer; 128 use crate::fmt::time::FormatTime; 129 130 use std::sync::Arc; 131 132 use super::ChronoFmtType; 133 use super::ChronoLocal; 134 use super::ChronoUtc; 135 136 #[test] test_chrono_format_time_utc_default()137 fn test_chrono_format_time_utc_default() { 138 let mut buf = String::new(); 139 let mut dst: Writer<'_> = Writer::new(&mut buf); 140 assert!(FormatTime::format_time(&ChronoUtc::default(), &mut dst).is_ok()); 141 // e.g. `buf` contains "2023-08-18T19:05:08.662499+00:00" 142 assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); 143 } 144 145 #[test] test_chrono_format_time_utc_custom()146 fn test_chrono_format_time_utc_custom() { 147 let fmt = ChronoUtc { 148 format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), 149 }; 150 let mut buf = String::new(); 151 let mut dst: Writer<'_> = Writer::new(&mut buf); 152 assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); 153 // e.g. `buf` contains "Wed Aug 23 15:53:23 2023" 154 assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); 155 } 156 157 #[test] test_chrono_format_time_local_default()158 fn test_chrono_format_time_local_default() { 159 let mut buf = String::new(); 160 let mut dst: Writer<'_> = Writer::new(&mut buf); 161 assert!(FormatTime::format_time(&ChronoLocal::default(), &mut dst).is_ok()); 162 // e.g. `buf` contains "2023-08-18T14:59:08.662499-04:00". 163 assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); 164 } 165 166 #[test] test_chrono_format_time_local_custom()167 fn test_chrono_format_time_local_custom() { 168 let fmt = ChronoLocal { 169 format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), 170 }; 171 let mut buf = String::new(); 172 let mut dst: Writer<'_> = Writer::new(&mut buf); 173 assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); 174 // e.g. `buf` contains "Wed Aug 23 15:55:46 2023". 175 assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); 176 } 177 } 178