diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 04ffa3b989..afd43a4df1 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -54,7 +54,7 @@ impl FixedOffset { /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00") /// ``` #[must_use] - pub fn east_opt(secs: i32) -> Option { + pub const fn east_opt(secs: i32) -> Option { if -86_400 < secs && secs < 86_400 { Some(FixedOffset { local_minus_utc: secs }) } else { @@ -87,7 +87,7 @@ impl FixedOffset { /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00") /// ``` #[must_use] - pub fn west_opt(secs: i32) -> Option { + pub const fn west_opt(secs: i32) -> Option { if -86_400 < secs && secs < 86_400 { Some(FixedOffset { local_minus_utc: -secs }) } else { diff --git a/src/offset/local/tz_info/rule.rs b/src/offset/local/tz_info/rule.rs index 7befddb5cf..a90a3e9384 100644 --- a/src/offset/local/tz_info/rule.rs +++ b/src/offset/local/tz_info/rule.rs @@ -6,6 +6,7 @@ use super::{ rem_euclid, Error, CUMUL_DAY_IN_MONTHS_NORMAL_YEAR, DAYS_PER_WEEK, DAY_IN_MONTHS_NORMAL_YEAR, SECONDS_PER_DAY, }; +use crate::FixedOffset; /// Transition rule #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -80,17 +81,17 @@ impl TransitionRule { } /// Find the local time type associated to the transition rule at the specified Unix time in seconds - pub(super) fn find_local_time_type_from_local( + pub(super) fn find_local_offset_from_local( &self, local_time: i64, year: i32, - ) -> Result, Error> { + ) -> Result, Error> { match self { TransitionRule::Fixed(local_time_type) => { - Ok(crate::LocalResult::Single(*local_time_type)) + local_time_type.offset().map(crate::LocalResult::Single) } TransitionRule::Alternate(alternate_time) => { - alternate_time.find_local_time_type_from_local(local_time, year) + alternate_time.find_local_offset_from_local(local_time, year) } } } @@ -228,11 +229,11 @@ impl AlternateTime { } } - fn find_local_time_type_from_local( + fn find_local_offset_from_local( &self, local_time: i64, current_year: i32, - ) -> Result, Error> { + ) -> Result, Error> { // Check if the current year is valid for the following computations if !(i32::min_value() + 2 <= current_year && current_year <= i32::max_value() - 2) { return Err(Error::OutOfRange("out of range date time")); @@ -253,7 +254,7 @@ impl AlternateTime { - i64::from(self.dst.ut_offset); match self.std.ut_offset.cmp(&self.dst.ut_offset) { - Ordering::Equal => Ok(crate::LocalResult::Single(self.std)), + Ordering::Equal => Ok(crate::LocalResult::Single(self.std.offset()?)), Ordering::Less => { if self.dst_start.transition_date(current_year).0 < self.dst_end.transition_date(current_year).0 @@ -261,7 +262,7 @@ impl AlternateTime { // northern hemisphere // For the DST END transition, the `start` happens at a later timestamp than the `end`. if local_time <= dst_start_transition_start { - Ok(crate::LocalResult::Single(self.std)) + Ok(crate::LocalResult::Single(self.std.offset()?)) } else if local_time > dst_start_transition_start && local_time < dst_start_transition_end { @@ -269,33 +270,33 @@ impl AlternateTime { } else if local_time >= dst_start_transition_end && local_time < dst_end_transition_end { - Ok(crate::LocalResult::Single(self.dst)) + Ok(crate::LocalResult::Single(self.dst.offset()?)) } else if local_time >= dst_end_transition_end && local_time <= dst_end_transition_start { - Ok(crate::LocalResult::Ambiguous(self.std, self.dst)) + Ok(crate::LocalResult::Ambiguous(self.std.offset()?, self.dst.offset()?)) } else { - Ok(crate::LocalResult::Single(self.std)) + Ok(crate::LocalResult::Single(self.std.offset()?)) } } else { // southern hemisphere regular DST // For the DST END transition, the `start` happens at a later timestamp than the `end`. if local_time < dst_end_transition_end { - Ok(crate::LocalResult::Single(self.dst)) + Ok(crate::LocalResult::Single(self.dst.offset()?)) } else if local_time >= dst_end_transition_end && local_time <= dst_end_transition_start { - Ok(crate::LocalResult::Ambiguous(self.std, self.dst)) + Ok(crate::LocalResult::Ambiguous(self.std.offset()?, self.dst.offset()?)) } else if local_time > dst_end_transition_end && local_time < dst_start_transition_start { - Ok(crate::LocalResult::Single(self.std)) + Ok(crate::LocalResult::Single(self.std.offset()?)) } else if local_time >= dst_start_transition_start && local_time < dst_start_transition_end { Ok(crate::LocalResult::None) } else { - Ok(crate::LocalResult::Single(self.dst)) + Ok(crate::LocalResult::Single(self.dst.offset()?)) } } } @@ -306,27 +307,27 @@ impl AlternateTime { // southern hemisphere reverse DST // For the DST END transition, the `start` happens at a later timestamp than the `end`. if local_time < dst_start_transition_end { - Ok(crate::LocalResult::Single(self.std)) + Ok(crate::LocalResult::Single(self.std.offset()?)) } else if local_time >= dst_start_transition_end && local_time <= dst_start_transition_start { - Ok(crate::LocalResult::Ambiguous(self.dst, self.std)) + Ok(crate::LocalResult::Ambiguous(self.dst.offset()?, self.std.offset()?)) } else if local_time > dst_start_transition_start && local_time < dst_end_transition_start { - Ok(crate::LocalResult::Single(self.dst)) + Ok(crate::LocalResult::Single(self.dst.offset()?)) } else if local_time >= dst_end_transition_start && local_time < dst_end_transition_end { Ok(crate::LocalResult::None) } else { - Ok(crate::LocalResult::Single(self.std)) + Ok(crate::LocalResult::Single(self.std.offset()?)) } } else { // northern hemisphere reverse DST // For the DST END transition, the `start` happens at a later timestamp than the `end`. if local_time <= dst_end_transition_start { - Ok(crate::LocalResult::Single(self.dst)) + Ok(crate::LocalResult::Single(self.dst.offset()?)) } else if local_time > dst_end_transition_start && local_time < dst_end_transition_end { @@ -334,13 +335,13 @@ impl AlternateTime { } else if local_time >= dst_end_transition_end && local_time < dst_start_transition_end { - Ok(crate::LocalResult::Single(self.std)) + Ok(crate::LocalResult::Single(self.std.offset()?)) } else if local_time >= dst_start_transition_end && local_time <= dst_start_transition_start { - Ok(crate::LocalResult::Ambiguous(self.dst, self.std)) + Ok(crate::LocalResult::Ambiguous(self.dst.offset()?, self.std.offset()?)) } else { - Ok(crate::LocalResult::Single(self.dst)) + Ok(crate::LocalResult::Single(self.dst.offset()?)) } } } @@ -924,7 +925,7 @@ mod tests { #[test] fn test_transition_rule() -> Result<(), Error> { let transition_rule_fixed = TransitionRule::from(LocalTimeType::new(-36000, false, None)?); - assert_eq!(transition_rule_fixed.find_local_time_type(0)?.offset(), -36000); + assert_eq!(transition_rule_fixed.find_local_time_type(0)?.ut_offset, -36000); let transition_rule_dst = TransitionRule::from(AlternateTime::new( LocalTimeType::new(43200, false, Some(b"NZST"))?, @@ -935,10 +936,10 @@ mod tests { 7200, )?); - assert_eq!(transition_rule_dst.find_local_time_type(953384399)?.offset(), 46800); - assert_eq!(transition_rule_dst.find_local_time_type(953384400)?.offset(), 43200); - assert_eq!(transition_rule_dst.find_local_time_type(970322399)?.offset(), 43200); - assert_eq!(transition_rule_dst.find_local_time_type(970322400)?.offset(), 46800); + assert_eq!(transition_rule_dst.find_local_time_type(953384399)?.ut_offset, 46800); + assert_eq!(transition_rule_dst.find_local_time_type(953384400)?.ut_offset, 43200); + assert_eq!(transition_rule_dst.find_local_time_type(970322399)?.ut_offset, 43200); + assert_eq!(transition_rule_dst.find_local_time_type(970322400)?.ut_offset, 46800); let transition_rule_negative_dst = TransitionRule::from(AlternateTime::new( LocalTimeType::new(3600, false, Some(b"IST"))?, @@ -949,10 +950,10 @@ mod tests { 3600, )?); - assert_eq!(transition_rule_negative_dst.find_local_time_type(954032399)?.offset(), 0); - assert_eq!(transition_rule_negative_dst.find_local_time_type(954032400)?.offset(), 3600); - assert_eq!(transition_rule_negative_dst.find_local_time_type(972781199)?.offset(), 3600); - assert_eq!(transition_rule_negative_dst.find_local_time_type(972781200)?.offset(), 0); + assert_eq!(transition_rule_negative_dst.find_local_time_type(954032399)?.ut_offset, 0); + assert_eq!(transition_rule_negative_dst.find_local_time_type(954032400)?.ut_offset, 3600); + assert_eq!(transition_rule_negative_dst.find_local_time_type(972781199)?.ut_offset, 3600); + assert_eq!(transition_rule_negative_dst.find_local_time_type(972781200)?.ut_offset, 0); let transition_rule_negative_time_1 = TransitionRule::from(AlternateTime::new( LocalTimeType::new(0, false, None)?, @@ -978,19 +979,19 @@ mod tests { )?); assert_eq!( - transition_rule_negative_time_2.find_local_time_type(954032399)?.offset(), + transition_rule_negative_time_2.find_local_time_type(954032399)?.ut_offset, -10800 ); assert_eq!( - transition_rule_negative_time_2.find_local_time_type(954032400)?.offset(), + transition_rule_negative_time_2.find_local_time_type(954032400)?.ut_offset, -7200 ); assert_eq!( - transition_rule_negative_time_2.find_local_time_type(972781199)?.offset(), + transition_rule_negative_time_2.find_local_time_type(972781199)?.ut_offset, -7200 ); assert_eq!( - transition_rule_negative_time_2.find_local_time_type(972781200)?.offset(), + transition_rule_negative_time_2.find_local_time_type(972781200)?.ut_offset, -10800 ); @@ -1003,8 +1004,8 @@ mod tests { 90000, )?); - assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702799)?.offset(), -14400); - assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702800)?.offset(), -14400); + assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702799)?.ut_offset, -14400); + assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702800)?.ut_offset, -14400); Ok(()) } diff --git a/src/offset/local/tz_info/timezone.rs b/src/offset/local/tz_info/timezone.rs index 2a5ced20ca..b07430288a 100644 --- a/src/offset/local/tz_info/timezone.rs +++ b/src/offset/local/tz_info/timezone.rs @@ -7,6 +7,7 @@ use std::{cmp::Ordering, fmt, str}; use super::rule::{AlternateTime, TransitionRule}; use super::{parser, Error, DAYS_PER_WEEK, SECONDS_PER_DAY}; +use crate::FixedOffset; /// Time zone #[derive(Debug, Clone, Eq, PartialEq)] @@ -118,12 +119,12 @@ impl TimeZone { } // should we pass NaiveDateTime all the way through to this fn? - pub(crate) fn find_local_time_type_from_local( + pub(crate) fn find_local_offset_from_local( &self, local_time: i64, year: i32, - ) -> Result, Error> { - self.as_ref().find_local_time_type_from_local(local_time, year) + ) -> Result, Error> { + self.as_ref().find_local_offset_from_local(local_time, year) } /// Returns a reference to the time zone @@ -200,11 +201,11 @@ impl<'a> TimeZoneRef<'a> { } } - pub(crate) fn find_local_time_type_from_local( + pub(crate) fn find_local_offset_from_local( &self, local_time: i64, year: i32, - ) -> Result, Error> { + ) -> Result, Error> { // #TODO: this is wrong as we need 'local_time_to_local_leap_time ? // but ... does the local time even include leap seconds ?? // let unix_leap_time = match self.unix_time_to_unix_leap_time(local_time) { @@ -225,34 +226,45 @@ impl<'a> TimeZoneRef<'a> { // the end and start here refers to where the time starts prior to the transition // and where it ends up after. not the temporal relationship. let transition_end = transition.unix_leap_time + i64::from(after_ltt.ut_offset); - let transition_start = - transition.unix_leap_time + i64::from(prev.ut_offset); + let transition_start = transition.unix_leap_time + i64::from(prev.ut_offset); match transition_start.cmp(&transition_end) { Ordering::Greater => { // bakwards transition, eg from DST to regular // this means a given local time could have one of two possible offsets if local_leap_time < transition_end { - return Ok(crate::LocalResult::Single(prev)); + return Ok(crate::LocalResult::Single(prev.offset()?)); } else if local_leap_time >= transition_end && local_leap_time <= transition_start { if prev.ut_offset < after_ltt.ut_offset { - return Ok(crate::LocalResult::Ambiguous(prev, after_ltt)); + return Ok(crate::LocalResult::Ambiguous( + prev.offset()?, + after_ltt.offset()?, + )); } else { - return Ok(crate::LocalResult::Ambiguous(after_ltt, prev)); + return Ok(crate::LocalResult::Ambiguous( + after_ltt.offset()?, + prev.offset()?, + )); } } } Ordering::Equal => { // should this ever happen? presumably we have to handle it anyway. if local_leap_time < transition_start { - return Ok(crate::LocalResult::Single(prev)); + return Ok(crate::LocalResult::Single(prev.offset()?)); } else if local_leap_time == transition_end { if prev.ut_offset < after_ltt.ut_offset { - return Ok(crate::LocalResult::Ambiguous(prev, after_ltt)); + return Ok(crate::LocalResult::Ambiguous( + prev.offset()?, + after_ltt.offset()?, + )); } else { - return Ok(crate::LocalResult::Ambiguous(after_ltt, prev)); + return Ok(crate::LocalResult::Ambiguous( + after_ltt.offset()?, + prev.offset()?, + )); } } } @@ -260,11 +272,11 @@ impl<'a> TimeZoneRef<'a> { // forwards transition, eg from regular to DST // this means that times that are skipped are invalid local times if local_leap_time <= transition_start { - return Ok(crate::LocalResult::Single(prev)); + return Ok(crate::LocalResult::Single(prev.offset()?)); } else if local_leap_time < transition_end { return Ok(crate::LocalResult::None); } else if local_leap_time == transition_end { - return Ok(crate::LocalResult::Single(after_ltt)); + return Ok(crate::LocalResult::Single(after_ltt.offset()?)); } } } @@ -275,13 +287,13 @@ impl<'a> TimeZoneRef<'a> { }; if let Some(extra_rule) = self.extra_rule { - match extra_rule.find_local_time_type_from_local(local_time, year) { - Ok(local_time_type) => Ok(local_time_type), + match extra_rule.find_local_offset_from_local(local_time, year) { + Ok(offset) => Ok(offset), Err(Error::OutOfRange(error)) => Err(Error::FindLocalTimeType(error)), err => err, } } else { - Ok(crate::LocalResult::Single(self.local_time_types[0])) + Ok(crate::LocalResult::Single(self.local_time_types[0].offset()?)) } } @@ -572,8 +584,11 @@ impl LocalTimeType { } /// Returns offset from UTC in seconds - pub(crate) const fn offset(&self) -> i32 { - self.ut_offset + pub(crate) const fn offset(&self) -> Result { + match FixedOffset::east_opt(self.ut_offset) { + Some(offset) => Ok(offset), + None => Err(Error::OutOfRange("offset out of bounds")), + } } /// Returns daylight saving time indicator @@ -818,7 +833,7 @@ mod tests { } let time_zone_utc = TimeZone::from_posix_tz("UTC")?; - assert_eq!(time_zone_utc.find_local_time_type(0)?.offset(), 0); + assert_eq!(time_zone_utc.find_local_time_type(0)?.ut_offset, 0); } assert!(TimeZone::from_posix_tz("EST5EDT,0/0,J365/25").is_err()); diff --git a/src/offset/local/unix.rs b/src/offset/local/unix.rs index 32aa31618b..12a5c9861a 100644 --- a/src/offset/local/unix.rs +++ b/src/offset/local/unix.rs @@ -11,7 +11,7 @@ use std::{cell::RefCell, collections::hash_map, env, fs, hash::Hasher, time::SystemTime}; use super::tz_info::TimeZone; -use super::{DateTime, FixedOffset, Local, NaiveDateTime}; +use super::{DateTime, Local, NaiveDateTime}; use crate::{Datelike, LocalResult, Utc}; pub(super) fn now() -> DateTime { @@ -153,33 +153,17 @@ impl Cache { .expect("unable to select local time type") .offset(); - return match FixedOffset::east_opt(offset) { - Some(offset) => LocalResult::Single(DateTime::from_utc(d, offset)), - None => LocalResult::None, + return match offset { + Ok(offset) => LocalResult::Single(DateTime::from_utc(d, offset)), + Err(_) => LocalResult::None, }; } // we pass through the year as the year of a local point in time must either be valid in that locale, or // the entire time was skipped in which case we will return LocalResult::None anywa. - match self - .zone - .find_local_time_type_from_local(d.timestamp(), d.year()) + self.zone + .find_local_offset_from_local(d.timestamp(), d.year()) .expect("unable to select local time type") - { - LocalResult::None => LocalResult::None, - LocalResult::Ambiguous(early, late) => { - let early_offset = FixedOffset::east_opt(early.offset()).unwrap(); - let late_offset = FixedOffset::east_opt(late.offset()).unwrap(); - - LocalResult::Ambiguous( - DateTime::from_utc(d - early_offset, early_offset), - DateTime::from_utc(d - late_offset, late_offset), - ) - } - LocalResult::Single(tt) => { - let offset = FixedOffset::east_opt(tt.offset()).unwrap(); - LocalResult::Single(DateTime::from_utc(d - offset, offset)) - } - } + .map(|offset| DateTime::from_utc(d - offset, offset)) } }