diff options
Diffstat (limited to 'src/fmt/time/chrono_crate.rs')
-rw-r--r-- | src/fmt/time/chrono_crate.rs | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/fmt/time/chrono_crate.rs b/src/fmt/time/chrono_crate.rs new file mode 100644 index 0000000..1a831ef --- /dev/null +++ b/src/fmt/time/chrono_crate.rs @@ -0,0 +1,177 @@ +use crate::fmt::format::Writer; +use crate::fmt::time::FormatTime; + +use std::sync::Arc; + +/// Formats [local time]s and [UTC time]s with `FormatTime` implementations +/// that use the [`chrono` crate]. +/// +/// [local time]: [`chrono::offset::Local`] +/// [UTC time]: [`chrono::offset::Utc`] +/// [`chrono` crate]: [`chrono`] + +/// Formats the current [local time] using a [formatter] from the [`chrono`] crate. +/// +/// [local time]: chrono::Local::now() +/// [formatter]: chrono::format +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct ChronoLocal { + format: Arc<ChronoFmtType>, +} + +impl ChronoLocal { + /// Format the time using the [`RFC 3339`] format + /// (a subset of [`ISO 8601`]). + /// + /// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339 + /// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601 + pub fn rfc_3339() -> Self { + Self { + format: Arc::new(ChronoFmtType::Rfc3339), + } + } + + /// Format the time using the given format string. + /// + /// See [`chrono::format::strftime`] for details on the supported syntax. + pub fn new(format_string: String) -> Self { + Self { + format: Arc::new(ChronoFmtType::Custom(format_string)), + } + } +} + +impl FormatTime for ChronoLocal { + fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result { + let t = chrono::Local::now(); + match self.format.as_ref() { + ChronoFmtType::Rfc3339 => { + use chrono::format::{Fixed, Item}; + write!( + w, + "{}", + t.format_with_items(core::iter::once(Item::Fixed(Fixed::RFC3339))) + ) + } + ChronoFmtType::Custom(fmt) => { + write!(w, "{}", t.format(fmt)) + } + } + } +} + +/// Formats the current [UTC time] using a [formatter] from the [`chrono`] crate. +/// +/// [UTC time]: chrono::Utc::now() +/// [formatter]: chrono::format +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct ChronoUtc { + format: Arc<ChronoFmtType>, +} + +impl ChronoUtc { + /// Format the time using the [`RFC 3339`] format + /// (a subset of [`ISO 8601`]). + /// + /// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339 + /// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601 + pub fn rfc_3339() -> Self { + Self { + format: Arc::new(ChronoFmtType::Rfc3339), + } + } + + /// Format the time using the given format string. + /// + /// See [`chrono::format::strftime`] for details on the supported syntax. + pub fn new(format_string: String) -> Self { + Self { + format: Arc::new(ChronoFmtType::Custom(format_string)), + } + } +} + +impl FormatTime for ChronoUtc { + fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result { + let t = chrono::Utc::now(); + match self.format.as_ref() { + ChronoFmtType::Rfc3339 => w.write_str(&t.to_rfc3339()), + ChronoFmtType::Custom(fmt) => w.write_str(&format!("{}", t.format(fmt))), + } + } +} + +/// The RFC 3339 format is used by default but a custom format string +/// can be used. See [`chrono::format::strftime`]for details on +/// the supported syntax. +/// +/// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.9/chrono/format/strftime/index.html +#[derive(Debug, Clone, Eq, PartialEq)] +enum ChronoFmtType { + /// Format according to the RFC 3339 convention. + Rfc3339, + /// Format according to a custom format string. + Custom(String), +} + +impl Default for ChronoFmtType { + fn default() -> Self { + ChronoFmtType::Rfc3339 + } +} + +#[cfg(test)] +mod tests { + use crate::fmt::format::Writer; + use crate::fmt::time::FormatTime; + + use std::sync::Arc; + + use super::ChronoFmtType; + use super::ChronoLocal; + use super::ChronoUtc; + + #[test] + fn test_chrono_format_time_utc_default() { + let mut buf = String::new(); + let mut dst: Writer<'_> = Writer::new(&mut buf); + assert!(FormatTime::format_time(&ChronoUtc::default(), &mut dst).is_ok()); + // e.g. `buf` contains "2023-08-18T19:05:08.662499+00:00" + assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); + } + + #[test] + fn test_chrono_format_time_utc_custom() { + let fmt = ChronoUtc { + format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), + }; + let mut buf = String::new(); + let mut dst: Writer<'_> = Writer::new(&mut buf); + assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); + // e.g. `buf` contains "Wed Aug 23 15:53:23 2023" + assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); + } + + #[test] + fn test_chrono_format_time_local_default() { + let mut buf = String::new(); + let mut dst: Writer<'_> = Writer::new(&mut buf); + assert!(FormatTime::format_time(&ChronoLocal::default(), &mut dst).is_ok()); + // e.g. `buf` contains "2023-08-18T14:59:08.662499-04:00". + assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); + } + + #[test] + fn test_chrono_format_time_local_custom() { + let fmt = ChronoLocal { + format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), + }; + let mut buf = String::new(); + let mut dst: Writer<'_> = Writer::new(&mut buf); + assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); + // e.g. `buf` contains "Wed Aug 23 15:55:46 2023". + assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); + } +} |