diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/date.rs | 14 | ||||
| -rw-r--r-- | src/datetime.rs | 14 | ||||
| -rw-r--r-- | src/time.rs | 39 | ||||
| -rw-r--r-- | src/timezone.rs | 100 |
4 files changed, 146 insertions, 21 deletions
diff --git a/src/date.rs b/src/date.rs index ff1805b..49a7642 100644 --- a/src/date.rs +++ b/src/date.rs @@ -1,6 +1,7 @@ use crate::{Month, Year}; use core::cmp::Ordering; +use core::fmt::Display; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct Date { @@ -91,3 +92,16 @@ impl Ord for Date { } // TODO addition + +impl Display for Date { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{:0width$}-{:02}-{:02}", + self.year, + self.month as u8, + self.day, + width = 4 + (self.year() < 0.into()) as usize + ) + } +} diff --git a/src/datetime.rs b/src/datetime.rs index 282a56b..f856e74 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -3,7 +3,7 @@ use crate::{ Date, Month, Time, TimeZone, Year, }; -use core::{cmp::Ordering, hash::Hash}; +use core::{cmp::Ordering, fmt::Display, hash::Hash}; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct NaiveDateTime { @@ -150,3 +150,15 @@ 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) + } +} + +impl<Tz: TimeZone> Display for DateTime<Tz> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} {}", self.utc_datetime, self.timezone) + } +} diff --git a/src/time.rs b/src/time.rs index e80e532..e0a919c 100644 --- a/src/time.rs +++ b/src/time.rs @@ -168,3 +168,42 @@ impl Ord for Time { } // TODO addition + +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); + if self.nanosecond() == 0 { + write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second) + } else if self.second < 10 { + write!(f, "{:02}:{:02}:0{}", self.hour, self.minute, seconds) + } else { + write!(f, "{:02}:{:02}:{}", self.hour, self.minute, seconds) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn display_without_nanos() { + let time = unsafe { Time::from_hms_nano_unchecked(0, 0, 1, 0) }; + let time_str = format!("{time}"); + assert_eq!(time_str, "00:00:01"); + } + + #[test] + fn display_with_nanos_lt_10() { + let time = unsafe { Time::from_hms_nano_unchecked(0, 0, 1, 1_000_000) }; + let time_str = format!("{time}"); + assert_eq!(time_str, "00:00:01.001"); + } + + #[test] + fn display_with_nanos_gt_10() { + let time = unsafe { Time::from_hms_nano_unchecked(0, 0, 10, 1_000_000) }; + let time_str = format!("{time}"); + assert_eq!(time_str, "00:00:10.001"); + } +} diff --git a/src/timezone.rs b/src/timezone.rs index 5284892..3319b12 100644 --- a/src/timezone.rs +++ b/src/timezone.rs @@ -1,13 +1,14 @@ -use crate::NaiveDateTime; +use crate::{DateTime, NaiveDateTime}; use core::convert::Infallible; +use core::fmt::Display; /// A type that can be used to represent a TimeZone -pub trait 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 - fn utc_offset(&self, date_time: NaiveDateTime) -> UtcOffset; + fn utc_offset(&self, date_time: DateTime<Utc>) -> UtcOffset; /// Given the local date and time, figure out the offset from UTC fn offset_from_local_time(&self, date_time: NaiveDateTime) -> Result<UtcOffset, Self::Err>; @@ -20,7 +21,7 @@ pub struct Utc; impl TimeZone for Utc { type Err = Infallible; - fn utc_offset(&self, _: NaiveDateTime) -> UtcOffset { + fn utc_offset(&self, _: DateTime<Utc>) -> UtcOffset { UtcOffset::UTC } @@ -29,10 +30,16 @@ impl TimeZone for Utc { } } +impl Display for Utc { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "UTC") + } +} + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] /// A timezone with a fixed offset from UTC pub struct UtcOffset { - offset_seconds: i32, + offset_seconds: isize, } impl UtcOffset { @@ -44,12 +51,7 @@ 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. - /// - /// # Safety - /// - /// A value with an absolute value greater than or equal to 86,400 results - /// in undefined behavior - pub const unsafe fn from_seconds_unchecked(seconds: i32) -> Self { + pub const fn from_seconds(seconds: isize) -> Self { Self { offset_seconds: seconds, } @@ -58,13 +60,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. - /// - /// # Safety - /// - /// A value with an absolute value greater than or equal to 24 results - /// in undefined behavior - pub const unsafe fn from_hours_unchecked(hours: i8) -> Self { - Self::from_seconds_unchecked(hours as i32 * 3600) + pub const fn from_hours(hours: isize) -> Self { + Self::from_seconds(hours * 3600) } /// The number of hours this timezone is ahead of UTC. THis number is @@ -75,15 +72,45 @@ impl UtcOffset { /// 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) -> i32 { + pub const fn seconds_ahead(self) -> isize { self.offset_seconds } } +impl Display for UtcOffset { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let hours = self.offset_seconds / 3600; + let minutes = ((self.offset_seconds % 3600) / 60).abs(); + let seconds = (self.offset_seconds % 60).abs(); + let sign = if self.offset_seconds.is_negative() { + '-' + } else { + '+' + }; + + if self.offset_seconds == 0 { + write!(f, "UTC") + } else if self.offset_seconds % 3600 == 0 { + write!(f, "UTC{:+}", hours) + } else if self.offset_seconds % 60 == 0 { + write!(f, "UTC{}{:02}:{:02}", sign, hours.abs(), minutes) + } else { + write!( + f, + "UTC{}{:02}:{:02}:{:02}", + sign, + hours.abs(), + minutes, + seconds + ) + } + } +} + impl TimeZone for UtcOffset { type Err = Infallible; - fn utc_offset(&self, _: NaiveDateTime) -> UtcOffset { + fn utc_offset(&self, _: DateTime<Utc>) -> UtcOffset { *self } @@ -91,3 +118,36 @@ impl TimeZone for UtcOffset { Ok(*self) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn utc_offset_display_no_offset() { + let offset = UtcOffset::UTC; + let offset_str = offset.to_string(); + 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") + } + + #[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") + } + + #[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") + } +} |
