summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/date.rs53
-rw-r--r--src/datetime.rs108
-rw-r--r--src/month.rs7
-rw-r--r--src/time.rs8
-rw-r--r--src/timestamp.rs24
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);