use std::fmt;
use std::ops::Sub;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{DateTime, Duration, ParseDateTimeError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Time {
hour: u8,
minute: u8,
second: u8,
millisecond: u16,
}
impl Time {
#[inline(always)]
pub(crate) const fn from_validated(hour: u8, minute: u8, second: u8, millisecond: u16) -> Self {
Self {
hour,
minute,
second,
millisecond,
}
}
#[inline(always)]
pub fn now() -> Self {
DateTime::now().time()
}
#[inline(always)]
pub fn parse(value: &str) -> Result<Self, ParseDateTimeError> {
DateTime::parse(value).map(|date_time| date_time.time())
}
#[inline(always)]
pub fn add_duration(self, duration: Duration) -> DateTime {
DateTime::new(
1970,
1,
1,
self.hour,
self.minute,
self.second,
self.millisecond,
)
.expect("time fields must be valid")
.add_duration(duration)
}
#[inline(always)]
pub fn sub_duration(self, duration: Duration) -> DateTime {
DateTime::new(
1970,
1,
1,
self.hour,
self.minute,
self.second,
self.millisecond,
)
.expect("time fields must be valid")
.sub_duration(duration)
}
#[inline(always)]
pub fn hour(self) -> u8 {
self.hour
}
#[inline(always)]
pub fn minute(self) -> u8 {
self.minute
}
#[inline(always)]
pub fn second(self) -> u8 {
self.second
}
#[inline(always)]
pub fn millisecond(self) -> u16 {
self.millisecond
}
#[inline(always)]
pub fn to_string_secs(self) -> String {
format!("{:02}:{:02}:{:02}", self.hour, self.minute, self.second)
}
#[inline(always)]
pub fn to_string_mins(self) -> String {
format!("{:02}:{:02}", self.hour, self.minute)
}
#[inline(always)]
fn as_day_millis(self) -> i64 {
self.hour as i64 * 3_600_000
+ self.minute as i64 * 60_000
+ self.second as i64 * 1_000
+ self.millisecond as i64
}
}
impl Sub for Time {
type Output = Duration;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Duration::from_millis(self.as_day_millis() - rhs.as_day_millis())
}
}
impl fmt::Display for Time {
#[inline(always)]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{:02}:{:02}:{:02}.{:03}",
self.hour, self.minute, self.second, self.millisecond
)
}
}
impl FromStr for Time {
type Err = ParseDateTimeError;
#[inline(always)]
fn from_str(value: &str) -> Result<Self, Self::Err> {
Self::parse(value)
}
}
impl Serialize for Time {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
} else {
serializer.serialize_i64(self.as_day_millis())
}
}
}
impl<'de> Deserialize<'de> for Time {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let value = String::deserialize(deserializer)?;
Self::parse(&value).map_err(serde::de::Error::custom)
} else {
let milliseconds = i64::deserialize(deserializer)?;
if !(0..86_400_000).contains(&milliseconds) {
return Err(serde::de::Error::custom(
"time milliseconds must be in 0..86400000",
));
}
let hour = (milliseconds / 3_600_000) as u8;
let minute = (milliseconds % 3_600_000 / 60_000) as u8;
let second = (milliseconds % 60_000 / 1_000) as u8;
let millisecond = (milliseconds % 1_000) as u16;
Ok(Self::from_validated(hour, minute, second, millisecond))
}
}
}