From 55b408ae562d6aef26ce4d0d82794943cdd5ca39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Dupr=C3=A9=20Bertoni?= Date: Thu, 23 Jan 2025 20:32:46 +0200 Subject: [PATCH] Add optional defmt support --- Cargo.toml | 2 ++ src/date.rs | 10 ++++++++++ src/datetime/mod.rs | 10 ++++++++++ src/lib.rs | 7 +++++++ src/month.rs | 7 +++++++ src/naive/date/mod.rs | 19 +++++++++++++++++++ src/naive/datetime/mod.rs | 7 +++++++ src/naive/isoweek.rs | 15 +++++++++++++++ src/naive/time/mod.rs | 25 +++++++++++++++++++++++++ src/offset/fixed.rs | 4 ++++ src/offset/local/mod.rs | 4 ++++ src/round.rs | 1 + src/time_delta.rs | 2 ++ src/weekday.rs | 8 ++++++++ 14 files changed, 121 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 5dc8aee5bd..b114b23707 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ name = "chrono" # Don't forget to adjust `ALL_NON_EXCLUSIVE_FEATURES` in CI scripts when adding a feature or an optional dependency. default = ["clock", "std", "oldtime", "wasmbind"] alloc = [] +defmt = ["dep:defmt"] libc = [] winapi = ["windows-targets"] std = ["alloc"] @@ -43,6 +44,7 @@ serde = { version = "1.0.99", default-features = false, optional = true } pure-rust-locales = { version = "0.8", optional = true } rkyv = { version = "0.7.43", optional = true, default-features = false } arbitrary = { version = "1.0.0", features = ["derive"], optional = true } +defmt = { version = "0.3", optional = true } [target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies] wasm-bindgen = { version = "0.2", optional = true } diff --git a/src/date.rs b/src/date.rs index a66882cecc..59cef0ef43 100644 --- a/src/date.rs +++ b/src/date.rs @@ -551,6 +551,16 @@ where } } +#[cfg(feature = "defmt")] +impl defmt::Format for Date +where + Tz::Offset: defmt::Format, +{ + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{}{}", self.naive_local(), self.offset); + } +} + // Note that implementation of Arbitrary cannot be automatically derived for Date, due to // the nontrivial bound ::Offset: Arbitrary. #[cfg(all(feature = "arbitrary", feature = "std"))] diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index db91561450..f8c867e247 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -1761,6 +1761,16 @@ impl fmt::Debug for DateTime { } } +#[cfg(feature = "defmt")] +impl defmt::Format for DateTime +where + Tz::Offset: defmt::Format, +{ + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{}{}", self.overflowing_naive_local(), self.offset); + } +} + // `fmt::Debug` is hand implemented for the `rkyv::Archive` variant of `DateTime` because // deriving a trait recursively does not propagate trait defined associated types with their own // constraints: diff --git a/src/lib.rs b/src/lib.rs index a7c603a3e9..0a006b8867 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -684,6 +684,13 @@ impl fmt::Debug for OutOfRange { } } +#[cfg(feature = "defmt")] +impl defmt::Format for OutOfRange { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "out of range"); + } +} + #[cfg(feature = "std")] impl std::error::Error for OutOfRange {} diff --git a/src/month.rs b/src/month.rs index ee5aa910f1..23ecc60f2a 100644 --- a/src/month.rs +++ b/src/month.rs @@ -284,6 +284,13 @@ impl fmt::Debug for ParseMonthError { } } +#[cfg(feature = "defmt")] +impl defmt::Format for ParseMonthError { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "ParseMonthError {{ .. }}") + } +} + #[cfg(feature = "serde")] mod month_serde { use super::Month; diff --git a/src/naive/date/mod.rs b/src/naive/date/mod.rs index 1514518ab2..508cae118a 100644 --- a/src/naive/date/mod.rs +++ b/src/naive/date/mod.rs @@ -2128,6 +2128,7 @@ impl From for NaiveDate { /// Iterator over `NaiveDate` with a step size of one day. #[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct NaiveDateDaysIterator { value: NaiveDate, } @@ -2164,6 +2165,7 @@ impl FusedIterator for NaiveDateDaysIterator {} /// Iterator over `NaiveDate` with a step size of one week. #[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct NaiveDateWeeksIterator { value: NaiveDate, } @@ -2238,6 +2240,23 @@ impl fmt::Debug for NaiveDate { } } +#[cfg(feature = "defmt")] +impl defmt::Format for NaiveDate { + fn format(&self, fmt: defmt::Formatter) { + let year = self.year(); + let mdf = self.mdf(); + if (0..=9999).contains(&year) { + defmt::write!(fmt, "{:02}{:02}", year / 100, year % 100); + } else { + // ISO 8601 requires the explicit sign for out-of-range years + let sign = ['+', '-'][(year < 0) as usize]; + defmt::write!(fmt, "{}{:05}", sign, year.abs()); + } + + defmt::write!(fmt, "-{:02}-{:02}", mdf.month(), mdf.day()); + } +} + /// The `Display` output of the naive date `d` is the same as /// [`d.format("%Y-%m-%d")`](crate::format::strftime). /// diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index a2ffc69674..beceff89ff 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -2053,6 +2053,13 @@ impl fmt::Debug for NaiveDateTime { } } +#[cfg(feature = "defmt")] +impl defmt::Format for NaiveDateTime { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{}T{}", self.date, self.time); + } +} + /// The `Display` output of the naive date and time `dt` is the same as /// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](crate::format::strftime). /// diff --git a/src/naive/isoweek.rs b/src/naive/isoweek.rs index 93d0dc423f..63f508d7ff 100644 --- a/src/naive/isoweek.rs +++ b/src/naive/isoweek.rs @@ -160,6 +160,21 @@ impl fmt::Debug for IsoWeek { } } +#[cfg(feature = "defmt")] +impl defmt::Format for IsoWeek { + fn format(&self, fmt: defmt::Formatter) { + let year = self.year(); + let week = self.week(); + if (0..=9999).contains(&year) { + defmt::write!(fmt, "{:04}-W{:02}", year, week) + } else { + // ISO 8601 requires the explicit sign for out-of-range years + let sign = ['+', '-'][(year < 0) as usize]; + defmt::write!(fmt, "{}{:05}-W{:02}", sign, year.abs(), week) + } + } +} + #[cfg(test)] mod tests { #[cfg(feature = "rkyv-validation")] diff --git a/src/naive/time/mod.rs b/src/naive/time/mod.rs index f32d828080..a9c39745ab 100644 --- a/src/naive/time/mod.rs +++ b/src/naive/time/mod.rs @@ -1529,6 +1529,31 @@ impl fmt::Debug for NaiveTime { } } +#[cfg(feature = "defmt")] +impl defmt::Format for NaiveTime { + fn format(&self, fmt: defmt::Formatter) { + let (hour, min, sec) = self.hms(); + let (sec, nano) = if self.frac >= 1_000_000_000 { + (sec + 1, self.frac - 1_000_000_000) + } else { + (sec, self.frac) + }; + + let (hour, min, sec) = (hour as u8, min as u8, sec as u8); + defmt::write!(fmt, "{:02}:{:02}:{:02}", hour, min, sec); + + if nano == 0 { + return; + } else if nano % 1_000_000 == 0 { + defmt::write!(fmt, ".{:03}", nano / 1_000_000); + } else if nano % 1_000 == 0 { + defmt::write!(fmt, ".{:06}", nano / 1_000); + } else { + defmt::write!(fmt, ".{:09}", nano); + } + } +} + /// The `Display` output of the naive time `t` is the same as /// [`t.format("%H:%M:%S%.f")`](crate::format::strftime). /// diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index e7382bed1d..234f862161 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -9,6 +9,9 @@ use core::str::FromStr; #[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] use rkyv::{Archive, Deserialize, Serialize}; +#[cfg(feature = "defmt")] +use defmt::Format; + use super::{MappedLocalTime, Offset, TimeZone}; use crate::format::{scan, ParseError, OUT_OF_RANGE}; use crate::naive::{NaiveDate, NaiveDateTime}; @@ -27,6 +30,7 @@ use crate::naive::{NaiveDate, NaiveDateTime}; archive_attr(derive(Clone, Copy, PartialEq, Eq, Hash, Debug)) )] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +#[cfg_attr(feature = "defmt", derive(Format))] pub struct FixedOffset { local_minus_utc: i32, } diff --git a/src/offset/local/mod.rs b/src/offset/local/mod.rs index 611fe18d8d..da9e78803b 100644 --- a/src/offset/local/mod.rs +++ b/src/offset/local/mod.rs @@ -9,6 +9,9 @@ use std::cmp::Ordering; #[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] use rkyv::{Archive, Deserialize, Serialize}; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + use super::fixed::FixedOffset; use super::{MappedLocalTime, TimeZone}; use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; @@ -119,6 +122,7 @@ mod tz_info; )] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Local; impl Local { diff --git a/src/round.rs b/src/round.rs index 6751562a62..6dbef3a979 100644 --- a/src/round.rs +++ b/src/round.rs @@ -301,6 +301,7 @@ where /// /// See: [`DurationRound`] #[derive(Debug, Clone, PartialEq, Eq, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RoundingError { /// Error when the TimeDelta exceeds the TimeDelta from or until the Unix epoch. /// diff --git a/src/time_delta.rs b/src/time_delta.rs index 3eb041ad57..7e057e87b1 100644 --- a/src/time_delta.rs +++ b/src/time_delta.rs @@ -57,6 +57,7 @@ const SECS_PER_WEEK: i64 = 604_800; archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)) )] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TimeDelta { secs: i64, nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC @@ -600,6 +601,7 @@ impl fmt::Display for TimeDelta { /// *seconds*, while this module supports signed range of up to /// `i64::MAX` of *milliseconds*. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OutOfRangeError(()); impl fmt::Display for OutOfRangeError { diff --git a/src/weekday.rs b/src/weekday.rs index 92644e8f6a..e859331a55 100644 --- a/src/weekday.rs +++ b/src/weekday.rs @@ -38,6 +38,7 @@ use crate::OutOfRange; )] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] #[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Weekday { /// Monday. Mon = 0, @@ -257,6 +258,13 @@ impl fmt::Debug for ParseWeekdayError { } } +#[cfg(feature = "defmt")] +impl defmt::Format for ParseWeekdayError { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "ParseWeekdayError {{ .. }}") + } +} + // the actual `FromStr` implementation is in the `format` module to leverage the existing code #[cfg(feature = "serde")]