diff options
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | src/date.rs | 29 | ||||
| -rw-r--r-- | src/datetime.rs | 56 | ||||
| -rw-r--r-- | src/lib.rs | 5 | ||||
| -rw-r--r-- | src/month.rs | 14 | ||||
| -rw-r--r-- | src/tai.rs | 16 | ||||
| -rw-r--r-- | src/time.rs | 29 | ||||
| -rw-r--r-- | src/timestamp.rs | 79 | ||||
| -rw-r--r-- | src/timezone.rs | 31 | ||||
| -rw-r--r-- | src/weekday.rs | 7 | ||||
| -rw-r--r-- | src/year.rs | 15 |
11 files changed, 237 insertions, 48 deletions
@@ -2,7 +2,11 @@ name = "botic" version = "0.1.0" edition = "2021" +description = "A time crate that handles leap seconds" +repository = "https://github.com/botahamec/botic" license = "Unlicense" +keywords = ["date", "time", "calendar"] +categories = ["date-and-time", "parser-implementations", "value-formatting", "data-structures"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/date.rs b/src/date.rs index 43355ab..3691a7b 100644 --- a/src/date.rs +++ b/src/date.rs @@ -39,6 +39,9 @@ impl Date { /// The latest date which can be represented pub const MAX: Self = unsafe { Self::from_ymd_unchecked(Year::MAX, Month::December, 31) }; + pub const UNIX_EPOCH: Self = + unsafe { Self::from_ymd_unchecked(Year::from_i16(1970), Month::January, 1) }; + // TODO validated from_calendar_date /// Creates a date without checking to make sure that it's valid. @@ -56,24 +59,29 @@ impl Date { /// # Safety /// /// This function results in undefined behavior if the given date is not a real date + #[must_use] pub const unsafe fn from_ymd_unchecked(year: Year, month: Month, day: u8) -> Self { Self { year, month, day } } // TODO docs + #[must_use] pub const fn year(self) -> Year { self.year } + #[must_use] pub const fn month(self) -> Month { self.month } + #[must_use] pub const fn day(self) -> u8 { self.day } + #[must_use] pub const fn is_leap_year(self) -> bool { self.year.is_leap_year() } @@ -115,18 +123,21 @@ impl Date { } // TODO handle BCE properly - pub const fn days_after_common_era(self) -> isize { + #[must_use] + pub const fn days_after_common_era(self) -> i64 { let year = self.year.wrapping_sub(1); - let leap_years = (year.as_i16() / 4 - year.as_i16() / 100 + year.as_i16() / 400) as isize; - let month_last_day_ordinal = self.month.last_day_ordinal(self.is_leap_year()) as isize; + let leap_years = (year.as_i16() / 4 - year.as_i16() / 100 + year.as_i16() / 400) as i64; + let month_last_day_ordinal = + self.month.previous().last_day_ordinal(self.is_leap_year()) as i64; - year.as_i16() as isize * 365 + leap_years + month_last_day_ordinal + self.day as isize - 1 + year.as_i16() as i64 * 365 + leap_years + month_last_day_ordinal + self.day as i64 - 1 } // TODO test - pub const fn from_days_after_common_era(days: isize) -> Self { - let era = days / 146097; // an era is a period of 400 year - let day_of_era = days - (era * 146097); + #[must_use] + pub const fn from_days_after_common_era(days: i64) -> Self { + let era = days / 146_097; // an era is a period of 400 year + let day_of_era = days - (era * 146_097); let year_of_era = day_of_era / 365; let year = year_of_era + (era * 400); let ordinal = day_of_era - (365 * year + year / 4 - year / 100); @@ -140,7 +151,7 @@ impl Date { } #[must_use] - pub const fn add_days(self, days: isize) -> Self { + 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) } @@ -192,7 +203,7 @@ impl Display for Date { self.year, self.month as u8, self.day, - width = 4 + (self.year() < 0.into()) as usize + width = 4 + usize::from(self.year() < 0.into()) ) } } diff --git a/src/datetime.rs b/src/datetime.rs index df4e637..9a43a4b 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,6 +1,6 @@ use crate::{ timezone::{Utc, UtcOffset}, - Date, Month, Time, TimeZone, Year, + Date, Month, Time, TimeZone, UnixTimestamp, Year, }; use core::{cmp::Ordering, fmt::Display, hash::Hash}; @@ -45,72 +45,82 @@ impl<Tz: TimeZone> DateTime<Tz> { impl NaiveDateTime { // TODO docs + #[must_use] pub const fn new(date: Date, time: Time) -> Self { Self { date, time } } + #[must_use] pub const fn date(self) -> Date { self.date } + #[must_use] pub const fn time(self) -> Time { self.time } + #[must_use] pub const fn year(self) -> Year { self.date.year() } + #[must_use] pub const fn month(self) -> Month { self.date.month() } + #[must_use] pub const fn day(self) -> u8 { self.date.day() } + #[must_use] pub const fn hour(self) -> u8 { self.time.hour() } + #[must_use] pub const fn minute(self) -> u8 { self.time.minute() } + #[must_use] pub const fn second(self) -> u8 { self.time.second() } + #[must_use] pub const fn millisecond(self) -> u16 { self.time.millisecond() } + #[must_use] pub const fn microsecond(self) -> u32 { self.time.microsecond() } + #[must_use] pub const fn nanosecond(self) -> u32 { self.time.nanosecond() } #[must_use] - pub fn add_seconds(self, seconds: isize) -> Self { - let time = self.time.add_seconds(seconds); + pub fn add_seconds_overflowing(self, seconds: i64) -> (Self, bool) { + let timestamp: UnixTimestamp = self.into(); + let (timestamp, overflow) = timestamp.add_seconds_overflowing(seconds); + let datetime: NaiveDateTime = timestamp.into(); - Self { - date: self.date, - time, - } + (datetime, overflow) } #[must_use] - pub fn add_nanoseconds(self, nanoseconds: isize) -> Self { - let time = self.time.add_nanoseconds(nanoseconds); + pub fn add_nanoseconds_overflowing(self, nanoseconds: i64) -> (Self, bool) { + let timestamp: UnixTimestamp = self.into(); + let (timestamp, overflow) = timestamp.add_nanoseconds_overflowing(nanoseconds); + let datetime: NaiveDateTime = timestamp.into(); - Self { - date: self.date, - time, - } + (datetime, overflow) } } @@ -153,7 +163,7 @@ impl<Tz: TimeZone, Other: TimeZone> PartialEq<DateTime<Other>> for DateTime<Tz> impl<Tz: TimeZone> Hash for DateTime<Tz> { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { - self.utc_datetime.hash(state) + self.utc_datetime.hash(state); } } @@ -169,8 +179,6 @@ impl<Tz: TimeZone> Ord for DateTime<Tz> { } } -// TODO addition - impl Display for NaiveDateTime { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{} {}", self.date, self.time) @@ -182,3 +190,19 @@ impl<Tz: TimeZone> Display for DateTime<Tz> { write!(f, "{} {}", self.utc_datetime, self.timezone) } } + +impl From<UnixTimestamp> for NaiveDateTime { + fn from(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(seconds_after_midnight as isize) + .add_nanoseconds(nanoseconds as isize); + + Self::new(date, time) + } +} @@ -1,13 +1,13 @@ #![doc = include_str!("../README.md")] -// TODO must uses // TODO serde support mod date; mod datetime; mod month; -mod tai; +pub mod tai; mod time; +mod timestamp; pub mod timezone; mod weekday; mod year; @@ -17,6 +17,7 @@ pub use datetime::DateTime; pub use datetime::NaiveDateTime; pub use month::Month; pub use time::Time; +pub use timestamp::UnixTimestamp; pub use timezone::TimeZone; pub use weekday::Weekday; pub use year::Year; diff --git a/src/month.rs b/src/month.rs index c5407f7..9582957 100644 --- a/src/month.rs +++ b/src/month.rs @@ -36,6 +36,7 @@ impl Month { /// assert_eq!(Some(Month::January), Month::from_u8(1)); /// assert_eq!(None, Month::from_u8(0)); /// assert_eq!(None, Month::from_u8(13)); + #[must_use] pub const fn from_u8(num: u8) -> Option<Self> { match num { 1 => Some(January), @@ -66,6 +67,7 @@ impl Month { /// assert_eq!(Some(Month::January), Month::from_abbreviation("Jan")); /// assert_eq!(None, Month::from_abbreviation("Janu")); /// ``` + #[must_use] pub fn from_abbreviation(abbreviation: &str) -> Option<Self> { match abbreviation { "Jan" => Some(January), @@ -96,6 +98,7 @@ impl Month { /// assert_eq!(Some(Month::January), Month::from_name("January")); /// assert_eq!(None, Month::from_name("Janu")); /// ``` + #[must_use] pub fn from_name(name: &str) -> Option<Self> { match name { "January" => Some(January), @@ -123,6 +126,7 @@ impl Month { /// /// assert_eq!(1, Month::January.number()); /// ``` + #[must_use] pub const fn number(self) -> u8 { self as u8 } @@ -136,6 +140,7 @@ impl Month { /// /// assert_eq!("January", Month::January.name()); /// ``` + #[must_use] pub const fn name(self) -> &'static str { match self { January => "January", @@ -162,6 +167,7 @@ impl Month { /// /// assert_eq!("Jan", Month::January.abbreviation()); /// ``` + #[must_use] pub const fn abbreviation(self) -> &'static str { match self { January => "Jan", @@ -182,6 +188,7 @@ impl Month { // TODO docs // TODO handle ordinals greater than 365 + #[must_use] pub const fn from_ordinal_common(ordinal: u16) -> Self { if ordinal < 31 { January @@ -210,6 +217,7 @@ impl Month { } } + #[must_use] pub const fn from_ordinal_leap(ordinal: u16) -> Self { if ordinal < 31 { January @@ -238,6 +246,7 @@ impl Month { } } + #[must_use] pub const fn from_ordinal(ordinal: u16, leap_year: bool) -> Self { if leap_year { Self::from_ordinal_leap(ordinal) @@ -253,6 +262,7 @@ impl Month { /// /// assert_eq!(Month::January.next(), Month::February); /// ``` + #[must_use] pub const fn next(self) -> Self { match self { January => February, @@ -277,6 +287,7 @@ impl Month { /// /// assert_eq!(Month::January.previous(), Month::December); /// ``` + #[must_use] pub const fn previous(self) -> Self { match self { January => December, @@ -298,6 +309,7 @@ impl Month { /// Returns the number of days up to the end of the month in a year. /// This doesn't account for leap day + #[must_use] pub const fn last_day_ordinal_common(self) -> u16 { match self { January => 31, @@ -358,6 +370,7 @@ impl Month { } /// Returns the number of days up to the end of the month in a leap year. + #[must_use] pub const fn last_day_ordinal_leap(self) -> u16 { match self { January => 31, @@ -377,6 +390,7 @@ impl Month { /// Returns the number of days up to the end of the month. /// Whether or not it's a leap year must be indicated + #[must_use] pub const fn last_day_ordinal(self, leap_year: bool) -> u16 { if leap_year { self.last_day_ordinal_leap() @@ -11,6 +11,7 @@ use crate::{ static GLOBAL_LEAP_SECONDS: RwLock<LeapSeconds> = const_rwlock(LeapSeconds::empty()); +#[derive(Debug)] struct LeapSeconds(Vec<DateTime<Utc>>); impl LeapSeconds { @@ -78,7 +79,7 @@ impl TimeZone for Tai { fn utc_offset(&self, date_time: DateTime<Utc>) -> UtcOffset { let leap_seconds = GLOBAL_LEAP_SECONDS.read(); let past_leap_seconds = leap_seconds.leap_seconds_before_inclusive(date_time); - UtcOffset::from_seconds(-(past_leap_seconds as isize + 10)) + UtcOffset::from_seconds(-(past_leap_seconds as i32 + 10)) } // TODO optimize @@ -93,19 +94,20 @@ impl TimeZone for Tai { // calculate the number of seconds that have passed since date_time in UTC let leap_seconds = GLOBAL_LEAP_SECONDS.read(); let utc_dt = DateTime::from_utc(date_time, Utc); - let mut past_leap_seconds = leap_seconds.leap_seconds_before_inclusive(utc_dt); + let mut past_leap_seconds = dbg!(leap_seconds.leap_seconds_before_inclusive(utc_dt)); let mut prev_pls = 0; // use this to see if the number of leap seconds has been updated // check if any leap seconds were found because of this calculation // keep checking until there is no longer a change in the total leap seconds while past_leap_seconds != prev_pls { prev_pls = past_leap_seconds; - let ndt = date_time.add_seconds(past_leap_seconds as isize); + // TODO think about this discard + let (ndt, _) = dbg!(date_time.add_seconds_overflowing(past_leap_seconds as i64)); let utc_dt = DateTime::from_utc(ndt, Utc); - past_leap_seconds = leap_seconds.leap_seconds_before_inclusive(utc_dt); + past_leap_seconds = dbg!(leap_seconds.leap_seconds_before_inclusive(utc_dt)); } - Ok(UtcOffset::from_seconds(-(past_leap_seconds as isize + 10))) + Ok(UtcOffset::from_seconds(-(past_leap_seconds as i32 + 10))) } } @@ -125,7 +127,7 @@ mod tests { .unwrap() }; - assert_eq!(offset, UtcOffset::from_seconds(-10)) + assert_eq!(offset, UtcOffset::from_seconds(-10)); } #[test] @@ -139,6 +141,6 @@ mod tests { .unwrap() }; - assert_eq!(offset, UtcOffset::from_seconds(-11)) + assert_eq!(offset, UtcOffset::from_seconds(-11)); } } diff --git a/src/time.rs b/src/time.rs index 49dba51..3777c1a 100644 --- a/src/time.rs +++ b/src/time.rs @@ -23,6 +23,7 @@ impl Time { /// /// Creating a time where the hour is greater than 23, minute is greater than 59, or second is /// greater than 60 results in undefined behavior + #[must_use] pub const unsafe fn from_hms_unchecked(hour: u8, minute: u8, second: u8) -> Self { Self { hour, @@ -38,6 +39,7 @@ impl Time { /// /// Creating a time where the hour is greater than 23, minute is greater than 59, second is /// greater than 60, or millisecond is greater than 999 results in undefined behavior + #[must_use] pub const unsafe fn from_hms_milli_unchecked( hour: u8, minute: u8, @@ -58,6 +60,7 @@ impl Time { /// /// Creating a time where the hour is greater than 23, minute is greater than 59, second is /// greater than 60, or microsecond is greater than 999,999 results in undefined behavior + #[must_use] pub const unsafe fn from_hms_micro_unchecked( hour: u8, minute: u8, @@ -78,6 +81,7 @@ impl Time { /// /// Creating a time where the hour is greater than 23, minute is greater than 59, second is /// greater than 60, or nanosecond is greater than 999,999,999 results in undefined behavior + #[must_use] pub const unsafe fn from_hms_nano_unchecked( hour: u8, minute: u8, @@ -93,34 +97,40 @@ impl Time { } /// Get the clock hour. The returned value will always be in the range `0..24` + #[must_use] pub const fn hour(self) -> u8 { self.hour } /// Get the minute within the hour. The returned value will always be in the range `0..60` + #[must_use] pub const fn minute(self) -> u8 { self.minute } // Get the second within the minute. The returned value will always be in the range `0..=60` + #[must_use] pub const fn second(self) -> u8 { self.second } // Get the millisecond within the second. // The returned value will always be in the range `0..1_000` + #[must_use] pub const fn millisecond(self) -> u16 { (self.nanosecond / 1_000_000) as u16 } // Get the microsecond within the second. // The returned value will always be in the range `0..1_000_000` + #[must_use] pub const fn microsecond(self) -> u32 { (self.nanosecond / 1_000) as u32 } // Get the nanosecond within the second. // The returned value will always be in the range `0..1_000_000` + #[must_use] pub const fn nanosecond(self) -> u32 { self.nanosecond } @@ -352,6 +362,23 @@ impl Time { self.add_nanoseconds_checked(nanoseconds) .unwrap_or_else(|| panic!("Overflow when adding {nanoseconds} nanoseconds to {self}")) } + + /// 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 + } + + /// Gets the number of nanoseconds since midnight + #[must_use] + pub fn nanoseconds_from_midnight(self) -> u64 { + u64::from(self.hour) * 3_600_000_000_000 + + u64::from(self.minute) * 60_000_000_000 + + u64::from(self.second) * 1_000_000_000 + + u64::from(self.nanosecond) + } } impl PartialOrd for Time { @@ -398,7 +425,7 @@ impl Ord for Time { impl Display for Time { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let seconds = self.second as f64 + (self.nanosecond as f64 / 1_000_000_000.0); + let seconds = f64::from(self.second) + (f64::from(self.nanosecond) / 1_000_000_000.0); if self.nanosecond() == 0 { write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second) } else if self.second < 10 { diff --git a/src/timestamp.rs b/src/timestamp.rs new file mode 100644 index 0000000..ea8f2e1 --- /dev/null +++ b/src/timestamp.rs @@ -0,0 +1,79 @@ +use crate::{Date, NaiveDateTime}; + +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub struct UnixTimestamp { + seconds: i64, + nanoseconds: u32, +} + +impl UnixTimestamp { + #[must_use] + pub const fn new(seconds: i64, nanoseconds: u32) -> Self { + Self { + seconds, + nanoseconds, + } + } + + #[must_use] + pub const fn seconds_since_unix_epoch(self) -> i64 { + self.seconds + } + + #[must_use] + pub const fn nanosecond(self) -> u32 { + self.nanoseconds + } + + #[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); + + let timestamp = Self::new(seconds, self.nanoseconds); + (timestamp, overflowing) + } + + #[must_use] + pub const fn add_nanoseconds_overflowing(self, nanoseconds: i64) -> (Self, bool) { + let total_nanos = (self.nanoseconds as i64 + nanoseconds) % 1_000_000_000; + let total_nanos = total_nanos + (1_000_000_000 * total_nanos.is_negative() as i64); + let added_seconds = (self.nanoseconds as i64 + nanoseconds) / 1_000_000_000; + let total_seconds = (self.seconds as i64 + added_seconds) % 60; + let overflow = 0 > total_seconds; + let total_seconds = total_seconds + (60 * total_seconds.is_negative() as i64); + + let timestamp = Self::new(total_seconds, total_nanos as u32); + (timestamp, overflow) + } +} + +impl From<NaiveDateTime> for UnixTimestamp { + fn from(ndt: NaiveDateTime) -> Self { + const UNIX_EPOCH_DAYS: i64 = Date::UNIX_EPOCH.days_after_common_era(); + // TODO don't require the .date() + let days = (ndt.date().days_after_common_era() - UNIX_EPOCH_DAYS) as i64; + let seconds = days * 86_400 + i64::from(ndt.time().seconds_from_midnight()); + let nanoseconds = ndt.nanosecond(); + + Self::new(seconds, nanoseconds) + } +} + +impl PartialOrd for UnixTimestamp { + fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { + match self.seconds.partial_cmp(&other.seconds) { + Some(core::cmp::Ordering::Equal) => self.nanoseconds.partial_cmp(&other.nanoseconds), + ord => ord, + } + } +} + +impl Ord for UnixTimestamp { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + match self.seconds.cmp(&other.seconds) { + core::cmp::Ordering::Equal => self.nanoseconds.cmp(&other.nanoseconds), + ord => ord, + } + } +} diff --git a/src/timezone.rs b/src/timezone.rs index 20405bf..6f356ca 100644 --- a/src/timezone.rs +++ b/src/timezone.rs @@ -2,15 +2,20 @@ use crate::{DateTime, NaiveDateTime}; use core::convert::Infallible; use core::fmt::Display; -/// A type that can be used to represent a TimeZone +/// A type that can be used to represent a `TimeZone` pub trait TimeZone: Sized + Eq + Display { /// The error to return in case of a failure to convert the local time to UTC type Err; - /// Given the time in the UTC timezone, determine the UtcOffset + /// Given the time in the UTC timezone, determine the `UtcOffset` fn utc_offset(&self, date_time: DateTime<Utc>) -> UtcOffset; /// Given the local date and time, figure out the offset from UTC + /// + /// # Errors + /// + /// This returns an Err if the given `NaiveDateTime` cannot exist in this timezone. + /// For example, the time may have been skipped because of daylight savings time. fn offset_from_local_time(&self, date_time: NaiveDateTime) -> Result<UtcOffset, Self::Err>; } @@ -39,7 +44,7 @@ impl Display for Utc { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] /// A timezone with a fixed offset from UTC pub struct UtcOffset { - offset_seconds: isize, + offset_seconds: i32, } impl UtcOffset { @@ -49,7 +54,8 @@ impl UtcOffset { /// Makes a new `UtcOffset` timezone with the given timezone difference. /// A positive number is the Eastern hemisphere. A negative number is the /// Western hemisphere. - pub const fn from_seconds(seconds: isize) -> Self { + #[must_use] + pub const fn from_seconds(seconds: i32) -> Self { Self { offset_seconds: seconds, } @@ -58,19 +64,22 @@ impl UtcOffset { /// Makes a new `UtcOffset` timezone with the given timezone difference. /// A positive number is the Eastern hemisphere. A negative number is the /// Western hemisphere. - pub const fn from_hours(hours: isize) -> Self { + #[must_use] + pub const fn from_hours(hours: i32) -> Self { Self::from_seconds(hours * 3600) } - /// The number of hours this timezone is ahead of UTC. THis number is + /// The number of hours this timezone is ahead of UTC. This number is /// negative if the timezone is in the Western hemisphere + #[must_use] pub fn hours_ahead(self) -> f32 { self.offset_seconds as f32 / 3600.0 } /// The number of seconds this timezone is ahead of UTC. This number is /// negative if the timezone is in the Western hemisphere - pub const fn seconds_ahead(self) -> isize { + #[must_use] + pub const fn seconds_ahead(self) -> i32 { self.offset_seconds } } @@ -125,27 +134,27 @@ mod tests { fn utc_offset_display_no_offset() { let offset = UtcOffset::UTC; let offset_str = offset.to_string(); - assert_eq!(offset_str, "UTC") + assert_eq!(offset_str, "UTC"); } #[test] fn utc_offset_display_positive_offset() { let offset = UtcOffset::from_hours(1); let offset_str = offset.to_string(); - assert_eq!(offset_str, "UTC+1") + assert_eq!(offset_str, "UTC+1"); } #[test] fn utc_offset_display_minute_offset() { let offset = UtcOffset::from_seconds(60); let offset_str = offset.to_string(); - assert_eq!(offset_str, "UTC+00:01") + assert_eq!(offset_str, "UTC+00:01"); } #[test] fn utc_offset_display_second_offset() { let offset = UtcOffset::from_seconds(-32); let offset_str = offset.to_string(); - assert_eq!(offset_str, "UTC-00:00:32") + assert_eq!(offset_str, "UTC-00:00:32"); } } diff --git a/src/weekday.rs b/src/weekday.rs index d678b9f..d194a1c 100644 --- a/src/weekday.rs +++ b/src/weekday.rs @@ -30,6 +30,7 @@ impl Weekday { /// assert_eq!(Weekday::Monday, Weekday::from_name("Monday").unwrap()); /// assert_eq!(None, Weekday::from_name("monday")); /// ``` + #[must_use] pub fn from_name(name: &str) -> Option<Self> { match name { "Monday" => Some(Monday), @@ -52,6 +53,7 @@ impl Weekday { /// /// assert_eq!(Weekday::Tuesday, Weekday::Monday.next()); /// ``` + #[must_use] pub const fn next(self) -> Self { match self { Monday => Tuesday, @@ -73,6 +75,7 @@ impl Weekday { /// /// assert_eq!(Weekday::Sunday, Weekday::Monday.previous()); /// ``` + #[must_use] pub const fn previous(self) -> Self { match self { Monday => Sunday, @@ -97,6 +100,7 @@ impl Weekday { /// assert_eq!(0, Weekday::Monday.number_days_from_monday()); /// assert_eq!(6, Weekday::Sunday.number_days_from_monday()); /// ``` + #[must_use] pub const fn number_days_from_monday(self) -> u8 { self as u8 } @@ -113,6 +117,7 @@ impl Weekday { /// assert_eq!(1, Weekday::Monday.number_from_monday()); /// assert_eq!(7, Weekday::Sunday.number_from_monday()); /// ``` + #[must_use] pub const fn number_from_monday(self) -> u8 { self.number_days_from_monday() + 1 } @@ -130,6 +135,7 @@ impl Weekday { /// assert_eq!(1, Weekday::Monday.number_days_from_sunday()); /// ``` // TODO benchmark this + #[must_use] pub const fn number_days_from_sunday(self) -> u8 { match self { Sunday => 0, @@ -149,6 +155,7 @@ impl Weekday { /// assert_eq!(1, Weekday::Sunday.number_from_sunday()); /// assert_eq!(2, Weekday::Monday.number_from_sunday()); /// ``` + #[must_use] pub const fn number_from_sunday(self) -> u8 { self.number_days_from_sunday() + 1 } diff --git a/src/year.rs b/src/year.rs index 284821c..c2d86ce 100644 --- a/src/year.rs +++ b/src/year.rs @@ -23,6 +23,7 @@ impl Year { /// const YEAR: Year = Year::from_i16(2021); /// assert_eq!(2021, YEAR.as_i16()); /// ``` + #[must_use] pub const fn from_i16(i: i16) -> Self { Self(i) } @@ -38,6 +39,7 @@ impl Year { /// const YEAR_INT: i16 = YEAR.as_i16(); /// assert_eq!(2021, YEAR_INT); /// ``` + #[must_use] pub const fn as_i16(self) -> i16 { self.0 } @@ -53,6 +55,7 @@ impl Year { /// assert_eq!(Some(Year::from(2022)), Year::from_i16(2021).checked_add(1)); /// assert_eq!(None, Year::MAX.checked_add(1)); /// ``` + #[must_use] pub const fn checked_add(self, rhs: i16) -> Option<Year> { match self.0.checked_add(rhs) { Some(year) => Some(Self(year)), @@ -74,6 +77,7 @@ impl Year { /// assert_eq!((Year::from(2022), false), Year::from(2021).overflowing_add(1)); /// assert_eq!((Year::MIN, true), Year::MAX.overflowing_add(1)); /// ``` + #[must_use] pub const fn overflowing_add(self, rhs: i16) -> (Year, bool) { let int_result = self.0.overflowing_add(rhs); (Year(int_result.0), int_result.1) @@ -90,6 +94,7 @@ impl Year { /// assert_eq!(Year::from(2022), Year::from(2021).saturating_add(1)); /// assert_eq!(Year::MAX, Year::MAX.saturating_add(1)); /// ``` + #[must_use] pub const fn saturating_add(self, rhs: i16) -> Year { Year(self.0.saturating_add(rhs)) } @@ -104,6 +109,7 @@ impl Year { /// /// assert_eq!(Year::from(2022), Year::from(2021).wrapping_add(1)); /// assert_eq!(Year::MIN, Year::MAX.wrapping_add(1)); + #[must_use] pub const fn wrapping_add(self, rhs: i16) -> Year { Year(self.0.wrapping_add(rhs)) } @@ -119,6 +125,7 @@ impl Year { /// assert_eq!(Some(Year::from(2020)), Year::from_i16(2021).checked_sub(1)); /// assert_eq!(None, Year::MIN.checked_sub(1)); /// ``` + #[must_use] pub const fn checked_sub(self, rhs: i16) -> Option<Year> { match self.0.checked_sub(rhs) { Some(year) => Some(Self(year)), @@ -140,6 +147,7 @@ impl Year { /// assert_eq!((Year::from(2020), false), Year::from(2021).overflowing_sub(1)); /// assert_eq!((Year::MAX, true), Year::MIN.overflowing_sub(1)); /// ``` + #[must_use] pub const fn overflowing_sub(self, rhs: i16) -> (Year, bool) { let int_result = self.0.overflowing_sub(rhs); (Year(int_result.0), int_result.1) @@ -156,6 +164,7 @@ impl Year { /// assert_eq!(Year::from(2020), Year::from(2021).saturating_sub(1)); /// assert_eq!(Year::MIN, Year::MIN.saturating_sub(1)); /// ``` + #[must_use] pub const fn saturating_sub(self, rhs: i16) -> Year { Year(self.0.saturating_sub(rhs)) } @@ -170,6 +179,7 @@ impl Year { /// /// assert_eq!(Year::from(2020), Year::from(2021).wrapping_sub(1)); /// assert_eq!(Year::MAX, Year::MIN.wrapping_sub(1)); + #[must_use] pub const fn wrapping_sub(self, rhs: i16) -> Year { Year(self.0.wrapping_sub(rhs)) } @@ -186,6 +196,7 @@ impl Year { /// assert!(Year::from(2000).is_leap_year()); /// assert!(!Year::from(2100).is_leap_year()); /// ``` + #[must_use] pub const fn is_leap_year(self) -> bool { (self.0 % 4 == 0) && ((self.0 % 100 != 0) || (self.0 % 400 == 0)) } @@ -221,12 +232,12 @@ impl<I: Into<i16>> Sub<I> for Year { impl AddAssign<i16> for Year { fn add_assign(&mut self, rhs: i16) { - self.0 = self.0 + rhs + self.0 = self.0 + rhs; } } impl SubAssign<i16> for Year { fn sub_assign(&mut self, rhs: i16) { - self.0 = self.0 - rhs + self.0 = self.0 - rhs; } } |
