use crate::{
Debug, Display, FmtResult, Formatter, Month, TimeSplit, TimeSplitYearSec, TryFromIntError, is,
is_leap_year,
};
#[doc = crate::_tags!(time)]
#[doc = crate::_doc_location!("phys/time")]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct TimeUnixI64 {
pub seconds: i64,
}
#[doc = crate::_tags!(time)]
#[doc = crate::_doc_location!("phys/time")]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct TimeUnixU32 {
pub seconds: u32,
}
impl TimeUnixI64 {
pub const fn new(seconds: i64) -> Self {
Self { seconds }
}
#[cfg(feature = "std")]
#[cfg_attr(nightly_doc, doc(cfg(feature = "std")))]
pub fn now() -> Self {
Self { seconds: Self::unix_time_64() }
}
pub const fn split(&self) -> TimeSplitYearSec<i32, u8, u8, u8, u8, u8> {
let secs_per_min: u32 = 60;
let mins_per_hour: u32 = 60;
let hours_per_day: u32 = 24;
let days_per_year: u32 = 365;
let mut secs_left = self.seconds.abs();
let mut year = if self.seconds >= 0 { 1970 } else { 1969 };
let mut leap = is_leap_year(year);
while secs_left >= (hours_per_day * mins_per_hour * secs_per_min * days_per_year) as i64 {
leap = is_leap_year(year);
let days_in_year = if leap { 366 } else { 365 };
secs_left -= (hours_per_day * mins_per_hour * secs_per_min * days_in_year) as i64;
is![self.seconds >= 0, year += 1, year -= 1];
}
let mut month = Month::January;
while secs_left
>= (hours_per_day * mins_per_hour * secs_per_min * month.len(leap) as u32) as i64
{
secs_left -=
(hours_per_day * mins_per_hour * secs_per_min * month.len(leap) as u32) as i64;
month = month.next();
}
let day = (secs_left / (hours_per_day * mins_per_hour * secs_per_min) as i64) as u8 + 1;
secs_left %= (hours_per_day * mins_per_hour * secs_per_min) as i64;
let hour = secs_left / (mins_per_hour * secs_per_min) as i64;
secs_left %= (mins_per_hour * secs_per_min) as i64;
let minute = secs_left / secs_per_min as i64;
let second = secs_left % secs_per_min as i64;
if self.seconds >= 0 {
TimeSplit::new_year_sec(
year,
month.number(),
day,
hour as u8,
minute as u8,
second as u8,
)
} else {
TimeSplit::new_year_sec(
year,
13 - month.number(),
Month::December.previous_nth(month.index()).len(leap) - day + 1,
23 - hour as u8,
59 - minute as u8,
60 - second as u8,
)
}
}
}
impl TimeUnixI64 {
#[cfg(feature = "std")]
fn unix_time_64() -> i64 {
use crate::SystemTime;
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.min(i64::MAX as u64) as i64
}
}
impl TimeUnixU32 {
pub const fn new(seconds: u32) -> Self {
Self { seconds }
}
#[cfg(feature = "std")]
#[cfg_attr(nightly_doc, doc(cfg(feature = "std")))]
pub fn now() -> Self {
Self { seconds: Self::unix_time_32() }
}
pub const fn split(&self) -> TimeSplitYearSec<u16, u8, u8, u8, u8, u8> {
let secs_per_min: u32 = 60;
let mins_per_hour: u32 = 60;
let hours_per_day: u32 = 24;
let days_per_year: u32 = 365;
let mut secs_left = self.seconds;
let mut year = 1970;
let mut leap = is_leap_year(year);
while secs_left >= (hours_per_day * mins_per_hour * secs_per_min * days_per_year) {
year += 1;
leap = is_leap_year(year);
let days_in_year = if leap { 366 } else { 365 };
secs_left -= hours_per_day * mins_per_hour * secs_per_min * days_in_year;
}
let mut month = Month::January;
while secs_left >= hours_per_day * mins_per_hour * secs_per_min * month.len(leap) as u32 {
secs_left -= hours_per_day * mins_per_hour * secs_per_min * month.len(leap) as u32;
month = month.next();
}
let day = (secs_left / (hours_per_day * mins_per_hour * secs_per_min)) + 1;
secs_left %= hours_per_day * mins_per_hour * secs_per_min;
let hour = secs_left / (mins_per_hour * secs_per_min);
secs_left %= mins_per_hour * secs_per_min;
let minute = secs_left / secs_per_min;
let second = secs_left % secs_per_min;
TimeSplit::new_year_sec(
year as u16,
month.number(),
day as u8,
hour as u8,
minute as u8,
second as u8,
)
}
}
impl TimeUnixU32 {
#[cfg(feature = "std")]
fn unix_time_32() -> u32 {
use crate::SystemTime;
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.min(u32::MAX as u64) as u32
}
}
impl Display for TimeUnixI64 {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult<()> {
let TimeSplit { y, mo, d, h, m, s, .. } = self.split();
write![f, "{y:04}-{mo:02}-{d:02}_{h:02}:{m:02}:{s:02}"]
}
}
impl Debug for TimeUnixI64 {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult<()> {
let TimeSplit { y, mo, d, h, m, s, .. } = self.split();
write![f, "TimeUnixI64 {{ {y:04}-{mo:02}-{d:02}_{h:02}:{m:02}:{s:02} }}"]
}
}
impl Display for TimeUnixU32 {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult<()> {
let TimeSplit { y, mo, d, h, m, s, .. } = self.split();
write![f, "{y:04}-{mo:02}-{d:02}_{h:02}:{m:02}:{s:02}"]
}
}
impl Debug for TimeUnixU32 {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult<()> {
let TimeSplit { y, mo, d, h, m, s, .. } = self.split();
write![f, "TimeUnixU32 {{ {y:04}-{mo:02}-{d:02}_{h:02}:{m:02}:{s:02} }}"]
}
}
impl From<TimeUnixU32> for TimeUnixI64 {
fn from(ut: TimeUnixU32) -> TimeUnixI64 {
TimeUnixI64 { seconds: ut.seconds.into() }
}
}
impl TryFrom<TimeUnixI64> for TimeUnixU32 {
type Error = TryFromIntError;
fn try_from(ut: TimeUnixI64) -> Result<TimeUnixU32, Self::Error> {
Ok(TimeUnixU32 { seconds: u32::try_from(ut.seconds)? })
}
}
#[cfg(feature = "std")]
mod std_impls {
use crate::{Cast, MismatchedCapacity, TimeUnixU32};
use crate::{SystemTime, SystemTimeError, TimeUnixI64};
impl TryFrom<SystemTime> for TimeUnixI64 {
type Error = SystemTimeError;
fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
let duration = time.duration_since(SystemTime::UNIX_EPOCH)?;
Ok(TimeUnixI64 {
seconds: duration.as_secs() as i64, })
}
}
impl TryFrom<SystemTime> for TimeUnixU32 {
type Error = crate::TimeError;
fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
let since = time.duration_since(SystemTime::UNIX_EPOCH)?;
let seconds = u32::try_from(since.as_secs()).map_err(|_| {
MismatchedCapacity::too_large_try(
Cast(since.as_secs()).checked_cast_to_usize(),
u32::MAX as usize,
)
})?;
Ok(TimeUnixU32 { seconds })
}
}
}
macro_rules! impl_from_prim {
($ut:ty, $($prim:ty),+) => { $( impl_from_prim![@ $ut, $prim]; )+ };
(@ $ut:ty, $prim:ty) => {
impl From<$prim> for $ut {
fn from(seconds: $prim) -> $ut {
Self { seconds: seconds.into() }
}
}
};
}
impl_from_prim![TimeUnixI64, i64, i32, i16, i8, u32, u16, u8];
impl_from_prim![TimeUnixU32, u32, u16, u8];
macro_rules! impl_try_from_prim {
($ut:ty, $($prim:ty),+) => { $( impl_try_from_prim![@ $ut, $prim]; )+ };
(@ $ut:ty, $prim:ty) => {
impl TryFrom<$prim> for $ut {
type Error = TryFromIntError;
fn try_from(seconds: $prim) -> Result<$ut, Self::Error> {
Ok(Self { seconds: seconds.try_into()? })
}
}
};
}
impl_try_from_prim![TimeUnixI64, u64, u128, usize, i128, isize];
#[rustfmt::skip]
impl_try_from_prim![TimeUnixU32, u64, u128, usize, i8, i16, i32, i64, i128, isize];