From 2a8af8b2cf3a88fda0e7edf17fc6be5e2654b3fe Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 12 Apr 2024 09:00:55 +0200 Subject: [PATCH] Add `DateTime::to_timezone` and `FromOffset` trait --- src/datetime/mod.rs | 22 +++++++++++++++++++--- src/datetime/tests.rs | 17 +++++++++++++++++ src/offset/fixed.rs | 8 +++++++- src/offset/mod.rs | 6 ++++++ src/offset/utc.rs | 8 +++++++- 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 366bbe6ad7..30ee474bd2 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -25,7 +25,7 @@ use crate::format::{write_rfc2822, write_rfc3339, DelayedFormat, SecondsFormat}; use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; #[cfg(feature = "clock")] use crate::offset::Local; -use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone, Utc}; +use crate::offset::{FixedOffset, FromOffset, LocalResult, Offset, TimeZone, Utc}; #[allow(deprecated)] use crate::Date; use crate::{expect, try_opt}; @@ -405,14 +405,30 @@ impl DateTime { } /// Changes the associated time zone. - /// The returned `DateTime` references the same instant of time from the perspective of the - /// provided time zone. + /// + /// The returned `DateTime` references the same instant of time in UTC but with the offset of + /// the target time zone. #[inline] #[must_use] pub fn with_timezone(&self, tz: &Tz2) -> DateTime { tz.from_utc_datetime(&self.datetime) } + /// Changes the associated `TimeZone` type. + /// + /// The returned `DateTime` references the same instant of time in UTC but with another + /// [`TimeZone`] type. A best effort is made to convert the value of the associated [`Offset`] + /// type to the `Offset` type of the target `TimeZone`. + #[inline] + #[must_use] + pub fn to_timezone(&self) -> DateTime + where + ::Offset: FromOffset<::Offset>, + { + let new_offset = ::Offset::from_offset(self.offset()); + DateTime { datetime: self.datetime, offset: new_offset } + } + /// Fix the offset from UTC to its current value, dropping the associated timezone information. /// This it useful for converting a generic `DateTime` to `DateTime`. #[inline] diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 126e1d34d1..77fd030d56 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -638,6 +638,23 @@ fn test_datetime_with_timezone() { assert_eq!(local_now, local_now2); } +#[test] +#[cfg(feature = "clock")] +fn test_datetime_to_timezone() { + let dt = Local::now(); + + let fixed: DateTime = dt.to_timezone(); + assert_eq!(fixed, dt); + assert_eq!(fixed.offset().fix(), dt.offset().fix()); + + let utc: DateTime = dt.to_timezone(); + assert_eq!(utc, dt); + + let local: DateTime = fixed.to_timezone(); + assert_eq!(local, fixed); + assert_eq!(local.offset().fix(), fixed.offset().fix()); +} + #[test] #[cfg(feature = "alloc")] fn test_datetime_rfc2822() { diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index e7382bed1d..3c0f19bf3c 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -9,7 +9,7 @@ use core::str::FromStr; #[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] use rkyv::{Archive, Deserialize, Serialize}; -use super::{MappedLocalTime, Offset, TimeZone}; +use super::{FromOffset, MappedLocalTime, Offset, TimeZone}; use crate::format::{scan, ParseError, OUT_OF_RANGE}; use crate::naive::{NaiveDate, NaiveDateTime}; @@ -152,6 +152,12 @@ impl Offset for FixedOffset { } } +impl FromOffset for FixedOffset { + fn from_offset(offset: &Off) -> Self { + offset.fix() + } +} + impl fmt::Debug for FixedOffset { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let offset = self.local_minus_utc; diff --git a/src/offset/mod.rs b/src/offset/mod.rs index c8f9c59c20..358b9dadc3 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -595,6 +595,12 @@ pub trait TimeZone: Sized + Clone { } } +/// Trait to create one `Offset` type from another `Offset` type. +pub trait FromOffset { + /// Converts to this type from the input type. + fn from_offset(offset: &Off) -> Self; +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/offset/utc.rs b/src/offset/utc.rs index 5ae26ed86d..7f09d5ff00 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -17,7 +17,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; #[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] use rkyv::{Archive, Deserialize, Serialize}; -use super::{FixedOffset, MappedLocalTime, Offset, TimeZone}; +use super::{FixedOffset, FromOffset, MappedLocalTime, Offset, TimeZone}; use crate::naive::{NaiveDate, NaiveDateTime}; #[cfg(feature = "now")] #[allow(deprecated)] @@ -139,6 +139,12 @@ impl Offset for Utc { } } +impl FromOffset for Utc { + fn from_offset(_offset: &Off) -> Self { + Utc + } +} + impl fmt::Debug for Utc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z")