summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBotahamec <botahamec@outlook.com>2022-03-16 14:52:31 -0400
committerBotahamec <botahamec@outlook.com>2022-03-16 14:52:31 -0400
commite0e0a18a4bc873583e973d771669e88a92b20d92 (patch)
treeba060d810c4621ec7e489afb19ad180a6b2ab755
parent5ca69f1830763b689bae9c4873a2912b3f1e23b1 (diff)
parent954cfd1385709d41ef0ece9c78b8dcee236f55e4 (diff)
Merge branch 'master' of https://github.com/botahamec/botic
-rw-r--r--Cargo.toml4
-rw-r--r--src/date.rs29
-rw-r--r--src/datetime.rs56
-rw-r--r--src/lib.rs5
-rw-r--r--src/month.rs14
-rw-r--r--src/tai.rs16
-rw-r--r--src/time.rs29
-rw-r--r--src/timestamp.rs79
-rw-r--r--src/timezone.rs31
-rw-r--r--src/weekday.rs7
-rw-r--r--src/year.rs15
11 files changed, 237 insertions, 48 deletions
diff --git a/Cargo.toml b/Cargo.toml
index b614093..78fb1ae 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index d83185d..2f130aa 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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()
diff --git a/src/tai.rs b/src/tai.rs
index 147b51b..8dd91d2 100644
--- a/src/tai.rs
+++ b/src/tai.rs
@@ -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;
}
}