use crate::str::Str;
use crate::macros::{
impl_common,impl_const,
impl_traits,impl_usize,impl_math,
impl_impl_math,handle_over_u32,
};
use crate::time::{TimeUnit,Military};
#[cfg(feature = "num")]
use crate::num::Unsigned;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Time(pub(super) u32, pub(super) Str<{ Time::MAX_LEN }>);
impl_traits!(Time, u32);
impl_math!(Time, u32);
impl Time {
pub const MAX_LEN: usize = 11;
pub const UNKNOWN: Self = Self(0, Str::from_static_str("??:??:??"));
pub const ZERO: Self = Self(0, Str::from_static_str("12:00:00 AM"));
pub const MAX: Self = Self(86399, Str::from_static_str("11:59:59 PM"));
}
impl Time {
impl_common!(u32);
impl_const!();
impl_usize!();
#[inline]
#[must_use]
pub const fn new(total_seconds: u32) -> Self {
Self::priv_from(total_seconds)
}
#[inline]
#[must_use]
pub const fn new_specified(
hours: u8,
minutes: u8,
seconds: u8,
) -> Self {
Self::priv_from(
(seconds as u32) +
(minutes as u32 * 60) +
(hours as u32 * 3600)
)
}
#[inline]
#[must_use]
pub const fn is_unknown(&self) -> bool {
matches!(self.1.as_bytes(), b"??:??:??")
}
}
impl Time {
pub(super) const fn priv_from(total_seconds: u32) -> Self {
const C: u8 = b':';
const S: u8 = b' ';
const M: u8 = b'M';
let total_seconds = total_seconds % 86400;
if total_seconds == 0 {
return Self::ZERO;
}
let (hours, minutes, seconds) = crate::time::secs_to_clock(total_seconds);
let h = Self::str_0_23(hours);
let m = Self::str_0_59(minutes);
let s = Self::str_0_59(seconds);
let marker = if hours > 11 { b'P' } else { b'A' };
let (buf, len): ([u8; Self::MAX_LEN], u8) = if h.len() == 1 {
([
h[0],
C,
m[0],
m[1],
C,
s[0],
s[1],
S,
marker,
M,
0,
], Self::MAX_LEN as u8 - 1)
} else {
([
h[0],
h[1],
C,
m[0],
m[1],
C,
s[0],
s[1],
S,
marker,
M,
], Self::MAX_LEN as u8)
};
Self(total_seconds, unsafe { Str::from_raw(buf,len) })
}
#[inline]
const fn str_0_23(u: u8) -> &'static [u8] {
match u {
0|12 => b"12",
1|13 => b"1",
2|14 => b"2",
3|15 => b"3",
4|16 => b"4",
5|17 => b"5",
6|18 => b"6",
7|19 => b"7",
8|20 => b"8",
9|21 => b"9",
10|22 => b"10",
11|23 => b"11",
_ => unreachable!(),
}
}
#[inline]
pub(super) const fn str_0_59(u: u8) -> &'static [u8] {
match u {
0 => b"00",
1 => b"01",
2 => b"02",
3 => b"03",
4 => b"04",
5 => b"05",
6 => b"06",
7 => b"07",
8 => b"08",
9 => b"09",
10 => b"10",
11 => b"11",
12 => b"12",
13 => b"13",
14 => b"14",
15 => b"15",
16 => b"16",
17 => b"17",
18 => b"18",
19 => b"19",
20 => b"20",
21 => b"21",
22 => b"22",
23 => b"23",
24 => b"24",
25 => b"25",
26 => b"26",
27 => b"27",
28 => b"28",
29 => b"29",
30 => b"30",
31 => b"31",
32 => b"32",
33 => b"33",
34 => b"34",
35 => b"35",
36 => b"36",
37 => b"37",
38 => b"38",
39 => b"39",
40 => b"40",
41 => b"41",
42 => b"42",
43 => b"43",
44 => b"44",
45 => b"45",
46 => b"46",
47 => b"47",
48 => b"48",
49 => b"49",
50 => b"50",
51 => b"51",
52 => b"52",
53 => b"53",
54 => b"54",
55 => b"55",
56 => b"56",
57 => b"57",
58 => b"58",
59 => b"59",
_ => unreachable!(),
}
}
}
macro_rules! impl_f {
($from:ty) => {
impl From<$from> for Time {
#[inline]
fn from(f: $from) -> Self {
$crate::macros::return_bad_float!(f, Self::UNKNOWN, Self::UNKNOWN);
Self::priv_from(f as u32)
}
}
impl From<&$from> for Time {
#[inline]
fn from(f: &$from) -> Self {
$crate::macros::return_bad_float!(f, Self::UNKNOWN, Self::UNKNOWN);
Self::priv_from(*f as u32)
}
}
}
}
impl_f!(f32);
impl_f!(f64);
macro_rules! impl_u {
($from:ty) => {
impl From<$from> for Time {
#[inline]
fn from(seconds: $from) -> Self {
Self::priv_from(seconds as u32)
}
}
impl From<&$from> for Time {
#[inline]
fn from(seconds: &$from) -> Self {
Self::from(*seconds)
}
}
}
}
impl_u!(u8);
impl_u!(u16);
impl_u!(u32);
impl_u!(u64);
impl_u!(u128);
impl_u!(usize);
macro_rules! impl_i {
($from:ty) => {
impl From<$from> for Time {
#[inline]
fn from(seconds: $from) -> Self {
if seconds.is_negative() {
return Self::UNKNOWN;
}
Self::priv_from(seconds as u32)
}
}
impl From<&$from> for Time {
#[inline]
fn from(seconds: &$from) -> Self {
if seconds.is_negative() {
return Self::UNKNOWN;
}
Self::priv_from(*seconds as u32)
}
}
}
}
impl_i!(i8);
impl_i!(i16);
impl_i!(i32);
impl_i!(i64);
impl_i!(i128);
impl_i!(isize);
macro_rules! impl_other {
($($from:ty),* $(,)?) => {
$(
impl From<$from> for Time {
#[inline]
fn from(other: $from) -> Self {
if other.is_unknown() {
return Self::UNKNOWN;
}
Self::priv_from(other.inner() as u32)
}
}
impl From<&$from> for Time {
#[inline]
fn from(other: &$from) -> Self {
if other.is_unknown() {
return Self::UNKNOWN;
}
Self::priv_from(other.inner() as u32)
}
}
)*
}
}
impl_other!(Military, TimeUnit);
#[cfg(feature = "num")]
impl_other!(Unsigned);
impl From<std::time::Duration> for Time {
#[inline]
fn from(duration: std::time::Duration) -> Self {
let u = duration.as_secs();
handle_over_u32!(u, u64);
Self::new(u as u32)
}
}
impl From<&std::time::Duration> for Time {
#[inline]
fn from(duration: &std::time::Duration) -> Self {
let u = duration.as_secs();
handle_over_u32!(u, u64);
Self::new(u as u32)
}
}
impl From<Time> for std::time::Duration {
#[inline]
fn from(value: Time) -> Self {
Self::from_secs(value.inner().into())
}
}
impl From<&Time> for std::time::Duration {
#[inline]
fn from(value: &Time) -> Self {
Self::from_secs(value.inner().into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "serde")]
fn serde() {
let this: Time = Time::from(3599);
let json = serde_json::to_string(&this).unwrap();
assert_eq!(json, r#"[3599,"12:59:59 AM"]"#);
let this: Time = serde_json::from_str(&json).unwrap();
assert_eq!(this, 3599);
assert_eq!(this, "12:59:59 AM");
assert!(serde_json::from_str::<Time>(&"---").is_err());
let json = serde_json::to_string(&Time::UNKNOWN).unwrap();
assert_eq!(json, r#"[0,"??:??:??"]"#);
assert!(serde_json::from_str::<Time>(&json).unwrap().is_unknown());
}
#[test]
#[cfg(feature = "bincode")]
fn bincode() {
let this: Time = Time::from(3599);
let config = bincode::config::standard();
let bytes = bincode::encode_to_vec(&this, config).unwrap();
let this: Time = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert_eq!(this, 3599);
assert_eq!(this, "12:59:59 AM");
let bytes = bincode::encode_to_vec(&Time::UNKNOWN, config).unwrap();
let this: Time = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert!(this.is_unknown());
}
#[test]
#[cfg(feature = "borsh")]
fn borsh() {
let this: Time = Time::from(3599);
let bytes = borsh::to_vec(&this).unwrap();
let this: Time = borsh::from_slice(&bytes).unwrap();
assert_eq!(this, 3599);
assert_eq!(this, "12:59:59 AM");
assert!(borsh::from_slice::<Time>(b"bad .-;[]124/ bytes").is_err());
let bytes = borsh::to_vec(&Time::UNKNOWN).unwrap();
let this: Time = borsh::from_slice(&bytes).unwrap();
assert!(this.is_unknown());
}
}