diff options
| author | Botahamec <botahamec@outlook.com> | 2022-03-18 14:57:13 -0400 |
|---|---|---|
| committer | Botahamec <botahamec@outlook.com> | 2022-03-18 14:57:13 -0400 |
| commit | db9f20681f90f206d393d2b06a6a8401515ff562 (patch) | |
| tree | 48c16c94a4bf0da27c7cde56a247703b7794600c /src | |
| parent | e0e0a18a4bc873583e973d771669e88a92b20d92 (diff) | |
Addition methods for NaiveDateTime
Diffstat (limited to 'src')
| -rw-r--r-- | src/date.rs | 53 | ||||
| -rw-r--r-- | src/datetime.rs | 108 | ||||
| -rw-r--r-- | src/month.rs | 7 | ||||
| -rw-r--r-- | src/time.rs | 8 | ||||
| -rw-r--r-- | src/timestamp.rs | 24 |
5 files changed, 168 insertions, 32 deletions
diff --git a/src/date.rs b/src/date.rs index 3691a7b..89ca721 100644 --- a/src/date.rs +++ b/src/date.rs @@ -86,25 +86,32 @@ impl Date { self.year.is_leap_year() } - // TODO overflow handling - pub const fn add_years(self, years: i16) -> Result<Self, LeapDayNotInLeapYearError> { - let year = self.year + years; + pub const fn add_years_overflowing( + self, + years: i16, + ) -> Result<(Self, bool), LeapDayNotInLeapYearError> { + let (year, overflow) = self.year.overflowing_add(years); - if self.day == 29 && self.month == Month::February && !year.is_leap_year() { + if self.day == 29 && (self.month as u8) == (Month::February as u8) && !year.is_leap_year() { Err(LeapDayNotInLeapYearError(self.year)) } else { - Ok(Self { - year, - month: self.month, - day: self.day, - }) + Ok(( + Self { + year, + month: self.month, + day: self.day, + }, + overflow, + )) } } - // TODO overflow handling - pub const fn add_months(self, months: i8) -> Result<Self, DayGreaterThanMaximumForMonthError> { + pub const fn add_months_overflowing( + self, + months: i8, + ) -> Result<(Self, bool), DayGreaterThanMaximumForMonthError> { let (month, years_to_add) = self.month.add_overflowing(months); - let year = self.year + years_to_add; + let (year, overflow) = self.year.overflowing_add(years_to_add as i16); let max_days_for_month = month.days(year.is_leap_year()); if self.day > max_days_for_month { @@ -114,11 +121,14 @@ impl Date { month_max_day: max_days_for_month, }) } else { - Ok(Self { - year, - month, - day: self.day, - }) + Ok(( + Self { + year, + month, + day: self.day, + }, + overflow, + )) } } @@ -151,9 +161,12 @@ impl Date { } #[must_use] - pub const fn add_days(self, days: i64) -> Self { - let total_days_since_ce = self.days_after_common_era() + days; - Self::from_days_after_common_era(total_days_since_ce) + pub const fn add_days_overflowing(self, days: i64) -> (Self, bool) { + let (total_days_since_ce, overflow) = self.days_after_common_era().overflowing_add(days); + ( + Self::from_days_after_common_era(total_days_since_ce), + overflow, + ) } } diff --git a/src/datetime.rs b/src/datetime.rs index 9a43a4b..931d023 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,4 +1,5 @@ use crate::{ + date::{DayGreaterThanMaximumForMonthError, LeapDayNotInLeapYearError}, timezone::{Utc, UtcOffset}, Date, Month, Time, TimeZone, UnixTimestamp, Year, }; @@ -50,6 +51,22 @@ impl NaiveDateTime { Self { date, time } } + pub const fn from_timestamp(timestamp: UnixTimestamp) -> Self { + const UNIX_EPOCH_DAYS_AFTER_CE: i64 = Date::UNIX_EPOCH.days_after_common_era(); + let days_after_unix_epoch = timestamp.seconds_since_unix_epoch() / 86_400; + let days_after_ce = days_after_unix_epoch + UNIX_EPOCH_DAYS_AFTER_CE as i64; + let date = Date::from_days_after_common_era(days_after_ce); + let seconds_after_midnight = timestamp.seconds_since_unix_epoch() % 86_400; + let nanoseconds = timestamp.nanosecond(); + let time = Time::MIDNIGHT + .add_seconds_overflowing(seconds_after_midnight as isize) + .0 + .add_nanoseconds_overflowing(nanoseconds as isize) + .0; + + Self::new(date, time) + } + #[must_use] pub const fn date(self) -> Date { self.date @@ -106,19 +123,97 @@ impl NaiveDateTime { } #[must_use] - pub fn add_seconds_overflowing(self, seconds: i64) -> (Self, bool) { - let timestamp: UnixTimestamp = self.into(); + pub const fn timestamp(self) -> UnixTimestamp { + const UNIX_EPOCH_DAYS: i64 = Date::UNIX_EPOCH.days_after_common_era(); + // TODO don't require the .date() + let days = (self.date.days_after_common_era() - UNIX_EPOCH_DAYS) as i64; + let seconds = days * 86_400 + self.time().seconds_from_midnight() as i64; + let nanoseconds = self.nanosecond(); + + UnixTimestamp::new(seconds, nanoseconds) + } + + pub const fn add_years_overflowing( + self, + years: i16, + ) -> Result<(Self, bool), LeapDayNotInLeapYearError> { + let (date, overflow) = match self.date.add_years_overflowing(years) { + Ok(v) => v, + Err(e) => return Err(e), + }; + + Ok(( + Self { + date, + time: self.time, + }, + overflow, + )) + } + + pub const fn add_months_overflowing( + self, + months: i8, + ) -> Result<(Self, bool), DayGreaterThanMaximumForMonthError> { + let (date, overflow) = match self.date.add_months_overflowing(months) { + Ok(v) => v, + Err(e) => return Err(e), + }; + + Ok(( + Self { + date, + time: self.time, + }, + overflow, + )) + } + + #[must_use] + pub const fn add_days_overflowing(self, days: i64) -> (Self, bool) { + let (date, overflow) = self.date.add_days_overflowing(days); + + ( + Self { + date, + time: self.time, + }, + overflow, + ) + } + + #[must_use] + pub const fn add_hours_overflowing(self, hours: i64) -> (Self, bool) { + let timestamp: UnixTimestamp = self.timestamp(); + let (timestamp, overflow) = timestamp.add_hours_overflowing(hours); + let datetime: NaiveDateTime = Self::from_timestamp(timestamp); + + (datetime, overflow) + } + + #[must_use] + pub const fn add_minutes_overflowing(self, minutes: i64) -> (Self, bool) { + let timestamp: UnixTimestamp = self.timestamp(); + let (timestamp, overflow) = timestamp.add_minutes_overflowing(minutes); + let datetime: NaiveDateTime = Self::from_timestamp(timestamp); + + (datetime, overflow) + } + + #[must_use] + pub const fn add_seconds_overflowing(self, seconds: i64) -> (Self, bool) { + let timestamp: UnixTimestamp = self.timestamp(); let (timestamp, overflow) = timestamp.add_seconds_overflowing(seconds); - let datetime: NaiveDateTime = timestamp.into(); + let datetime: NaiveDateTime = Self::from_timestamp(timestamp); (datetime, overflow) } #[must_use] - pub fn add_nanoseconds_overflowing(self, nanoseconds: i64) -> (Self, bool) { - let timestamp: UnixTimestamp = self.into(); + pub const fn add_nanoseconds_overflowing(self, nanoseconds: i64) -> (Self, bool) { + let timestamp: UnixTimestamp = self.timestamp(); let (timestamp, overflow) = timestamp.add_nanoseconds_overflowing(nanoseconds); - let datetime: NaiveDateTime = timestamp.into(); + let datetime: NaiveDateTime = Self::from_timestamp(timestamp); (datetime, overflow) } @@ -191,6 +286,7 @@ impl<Tz: TimeZone> Display for DateTime<Tz> { } } +// TODO there's a lossy cast somewhere here or in the into(). Where is it? impl From<UnixTimestamp> for NaiveDateTime { fn from(timestamp: UnixTimestamp) -> Self { const UNIX_EPOCH_DAYS_AFTER_CE: i64 = Date::UNIX_EPOCH.days_after_common_era(); diff --git a/src/month.rs b/src/month.rs index 9582957..1419717 100644 --- a/src/month.rs +++ b/src/month.rs @@ -399,11 +399,14 @@ impl Month { } } - pub const fn add_overflowing(self, months: u8) -> (Self, u8) { + pub const fn add_overflowing(self, months: i8) -> (Self, u8) { let zero_indexed_num = ((self as u16) - 1) + months as u16; let wraps = (zero_indexed_num as u8) / 12; let zero_indexed_month = zero_indexed_num % 12; - let month = Self::from_u8((zero_indexed_month as u8) + 1).unwrap(); + let month = match Self::from_u8((zero_indexed_month as u8) + 1) { + Some(month) => month, + None => unsafe { std::hint::unreachable_unchecked() }, + }; (month, wraps) } diff --git a/src/time.rs b/src/time.rs index 3777c1a..fcb6434 100644 --- a/src/time.rs +++ b/src/time.rs @@ -365,10 +365,10 @@ impl Time { /// Gets the number of seconds since midnight #[must_use] - pub fn seconds_from_midnight(self) -> u32 { - u32::from(self.hour) * 3_600_000_000 - + u32::from(self.minute) * 60_000_000 - + u32::from(self.second) * 1_000_000 + pub const fn seconds_from_midnight(self) -> u32 { + self.hour as u32 * 3_600_000_000 + + self.minute as u32 * 60_000_000 + + self.second as u32 * 1_000_000 } /// Gets the number of nanoseconds since midnight diff --git a/src/timestamp.rs b/src/timestamp.rs index ea8f2e1..c223ccb 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -26,6 +26,30 @@ impl UnixTimestamp { } #[must_use] + pub const fn add_days_overflowing(self, days: i64) -> (Self, bool) { + let (seconds, overflowing) = self.seconds.overflowing_add(days as i64 * 3600 * 24); + + let timestamp = Self::new(seconds, self.nanoseconds); + (timestamp, overflowing) + } + + #[must_use] + pub const fn add_hours_overflowing(self, hours: i64) -> (Self, bool) { + let (seconds, overflowing) = self.seconds.overflowing_add(hours as i64 * 3600); + + let timestamp = Self::new(seconds, self.nanoseconds); + (timestamp, overflowing) + } + + #[must_use] + pub const fn add_minutes_overflowing(self, minutes: i64) -> (Self, bool) { + let (seconds, overflowing) = self.seconds.overflowing_add(minutes as i64 * 60); + + let timestamp = Self::new(seconds, self.nanoseconds); + (timestamp, overflowing) + } + + #[must_use] pub const fn add_seconds_overflowing(self, seconds: i64) -> (Self, bool) { // TODO overflowing goes first let (seconds, overflowing) = self.seconds.overflowing_add(seconds as i64); |
