use core::fmt::{self, Debug, Formatter};
use core::ops::Sub;
use crate::{AsTime, Calendar, Date, PlainDateTime, TimeInterval};
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PlainTime {
pub(crate) secs: u32,
pub(crate) nsec: u32,
}
impl PlainTime {
pub const MIDNIGHT: Self = Self::of(0, 0, 0);
}
impl PlainTime {
#[must_use]
pub const fn of(hour: u8, minute: u8, second: u8) -> Self {
let mut secs = (hour as u32) * 3_600;
secs += (minute as u32) * 60;
secs += second as u32;
Self { secs, nsec: 0 }
}
#[must_use]
pub const fn of_nanosecond(hour: u8, minute: u8, second: u8, nanoseconds: u32) -> Self {
let mut time = Self::of(hour, minute, second);
time.nsec = nanoseconds;
time
}
}
impl PlainTime {
#[must_use]
pub const fn from_seconds_since_midnight(seconds: u32) -> Self {
Self {
secs: seconds.rem_euclid(86_400),
nsec: 0,
}
}
#[must_use]
pub const fn from_nanoseconds_since_midnight(nanoseconds: u64) -> Self {
let secs = nanoseconds.div_euclid(1_000_000_000) as u32;
let nsec = nanoseconds.rem_euclid(1_000_000_000) as u32;
let mut time = Self::from_seconds_since_midnight(secs);
time.nsec = nsec;
time
}
#[must_use]
pub const fn from_unix_timestamp(seconds: i64, nanoseconds: u32) -> Self {
Self {
secs: seconds.rem_euclid(86_400) as u32,
nsec: nanoseconds,
}
}
}
impl PlainTime {
#[must_use]
pub const fn hour(self) -> u8 {
((self.secs % 86_400) / 3_600) as u8
}
#[must_use]
pub const fn minute(self) -> u8 {
((self.secs % 3_600) / 60) as u8
}
#[must_use]
pub const fn second(self) -> u8 {
(self.secs % 60) as u8
}
#[must_use]
pub const fn millisecond(self) -> u16 {
(self.nsec / 1_000_000) as u16
}
#[must_use]
pub const fn microsecond(self) -> u32 {
self.nsec / 1_000
}
#[must_use]
pub const fn nanosecond(self) -> u32 {
self.nsec
}
#[must_use]
pub const fn components(self) -> (u8, u8, u8, u32) {
let mut second = self.secs % 86_400;
let hour = second / 3_600;
second -= hour * 3_600;
let minute = second / 60;
second -= minute * 60;
(hour as u8, minute as u8, second as u8, self.nsec)
}
}
impl PlainTime {
#[must_use]
pub const fn with_date<C: Calendar>(self, date: Date<C>) -> PlainDateTime<C> {
PlainDateTime { date, time: self }
}
}
impl PlainTime {
#[must_use]
pub const fn as_seconds_since_midnight(self) -> u32 {
self.secs
}
#[must_use]
pub const fn as_nanoseconds_since_midnight(self) -> u64 {
let mut nanos = (self.secs as u64) * 1_000_000_000;
nanos += self.nsec as u64;
nanos
}
pub(crate) const fn as_timestamp(self) -> (i64, u32) {
(self.secs as i64, self.nsec)
}
}
impl Debug for PlainTime {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad(&self.format_rfc3339())
}
}
impl From<(u8, u8, u8)> for PlainTime {
fn from((hour, minute, second): (u8, u8, u8)) -> Self {
Self::of(hour, minute, second)
}
}
impl From<(u8, u8, u8, u32)> for PlainTime {
fn from((hour, minute, second, nanosecond): (u8, u8, u8, u32)) -> Self {
Self::of_nanosecond(hour, minute, second, nanosecond)
}
}
impl AsTime for PlainTime {
fn as_time(&self) -> PlainTime {
*self
}
fn as_timestamp(&self) -> (i64, u32) {
(*self).as_timestamp()
}
}
impl Sub<Self> for PlainTime {
type Output = TimeInterval;
fn sub(self, rhs: Self) -> Self::Output {
TimeInterval::between(rhs, self)
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use test_case::test_case;
use crate::PlainTime;
#[test]
fn expect_clock_valid() {
for hour in 0..24 {
for minute in 0..60 {
for second in 0..60 {
let time = PlainTime::of(hour, minute, second);
assert_eq!(time.components(), (hour, minute, second, 0));
assert_eq!(
PlainTime::from_seconds_since_midnight(time.as_seconds_since_midnight()),
time
);
assert_eq!(
time.format_rfc3339(),
format!("{:02}:{:02}:{:02}", hour, minute, second)
);
}
}
}
}
#[test_case((0, 0, 0), 0)] #[test_case((4, 0, 10), 14410)] #[test_case((23, 59, 59), 86399)] fn expect_time_as_timestamp(clock: (u8, u8, u8), timestamp: i64) {
assert_eq!(
PlainTime::of(clock.0, clock.1, clock.2).as_timestamp().0,
timestamp
);
}
#[test_case((5, 10, 0), (12, 3, 0), (6, 53, 0))]
fn time_sub_time(start: (u8, u8, u8), end: (u8, u8, u8), expected: (i8, i8, i8)) {
let time_start = PlainTime::of(start.0, start.1, start.2);
let time_end = PlainTime::of(end.0, end.1, end.2);
let ti = time_end - time_start;
assert_eq!(ti.hours(), expected.0);
assert_eq!(ti.minutes(), expected.1);
assert_eq!(ti.seconds(), expected.2);
}
}