use std::cmp;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::time::{SystemTime, Duration as SystemDuration, UNIX_EPOCH};
use std::u32;
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
use crate::{
Error,
Result,
};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp(u32);
assert_send_and_sync!(Timestamp);
impl From<Timestamp> for u32 {
fn from(t: Timestamp) -> Self {
t.0
}
}
impl From<u32> for Timestamp {
fn from(t: u32) -> Self {
Timestamp(t)
}
}
impl TryFrom<SystemTime> for Timestamp {
type Error = anyhow::Error;
fn try_from(t: SystemTime) -> Result<Self> {
match t.duration_since(std::time::UNIX_EPOCH) {
Ok(d) if d.as_secs() <= std::u32::MAX as u64 =>
Ok(Timestamp(d.as_secs() as u32)),
_ => Err(Error::InvalidArgument(
format!("Time exceeds u32 epoch: {:?}", t))
.into()),
}
}
}
impl From<Timestamp> for SystemTime {
fn from(t: Timestamp) -> Self {
UNIX_EPOCH.checked_add(SystemDuration::new(t.0 as u64, 0))
.unwrap_or_else(|| UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0))
}
}
impl From<Timestamp> for Option<SystemTime> {
fn from(t: Timestamp) -> Self {
Some(t.into())
}
}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", crate::fmt::time(&SystemTime::from(*self)))
}
}
impl fmt::Debug for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Timestamp {
pub fn now() -> Timestamp {
crate::now().try_into()
.expect("representable for the next hundred years")
}
pub fn checked_add(&self, d: Duration) -> Option<Timestamp> {
self.0.checked_add(d.0).map(Self)
}
pub fn checked_sub(&self, d: Duration) -> Option<Timestamp> {
self.0.checked_sub(d.0).map(Self)
}
pub fn round_down<P, F>(&self, precision: P, floor: F) -> Result<Timestamp>
where P: Into<Option<u8>>,
F: Into<Option<SystemTime>>
{
let p = precision.into().unwrap_or(21) as u32;
if p < 32 {
let rounded = Self(self.0 & !((1 << p) - 1));
match floor.into() {
Some(floor) => {
Ok(cmp::max(rounded, floor.try_into()?))
}
None => { Ok(rounded) }
}
} else {
Err(Error::InvalidArgument(
format!("Invalid precision {}", p)).into())
}
}
}
#[cfg(test)]
impl Arbitrary for Timestamp {
fn arbitrary(g: &mut Gen) -> Self {
Timestamp(u32::arbitrary(g))
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Duration(u32);
assert_send_and_sync!(Duration);
impl From<Duration> for u32 {
fn from(d: Duration) -> Self {
d.0
}
}
impl From<u32> for Duration {
fn from(d: u32) -> Self {
Duration(d)
}
}
impl TryFrom<SystemDuration> for Duration {
type Error = anyhow::Error;
fn try_from(d: SystemDuration) -> Result<Self> {
if d.as_secs() <= std::u32::MAX as u64 {
Ok(Duration(d.as_secs() as u32))
} else {
Err(Error::InvalidArgument(
format!("Duration exceeds u32: {:?}", d))
.into())
}
}
}
impl From<Duration> for SystemDuration {
fn from(d: Duration) -> Self {
SystemDuration::new(d.0 as u64, 0)
}
}
impl From<Duration> for Option<SystemDuration> {
fn from(d: Duration) -> Self {
Some(d.into())
}
}
impl fmt::Debug for Duration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", SystemDuration::from(*self))
}
}
impl Duration {
pub const fn seconds(n: u32) -> Duration {
Self(n)
}
pub fn minutes(n: u32) -> Result<Duration> {
match 60u32.checked_mul(n) {
Some(val) => Ok(Self::seconds(val)),
None => {
Err(Error::InvalidArgument(format!(
"Not representable: {} minutes in seconds exceeds u32",
n
)).into())
}
}
}
pub fn hours(n: u32) -> Result<Duration> {
match 60u32.checked_mul(n) {
Some(val) => Self::minutes(val),
None => {
Err(Error::InvalidArgument(format!(
"Not representable: {} hours in seconds exceeds u32",
n
)).into())
}
}
}
pub fn days(n: u32) -> Result<Duration> {
match 24u32.checked_mul(n) {
Some(val) => Self::hours(val),
None => {
Err(Error::InvalidArgument(format!(
"Not representable: {} days in seconds exceeds u32",
n
)).into())
}
}
}
pub fn weeks(n: u32) -> Result<Duration> {
match 7u32.checked_mul(n) {
Some(val) => Self::days(val),
None => {
Err(Error::InvalidArgument(format!(
"Not representable: {} weeks in seconds exceeds u32",
n
)).into())
}
}
}
pub fn years(n: u32) -> Result<Duration> {
let s = (365.2425 * n as f64).trunc();
if s > u32::MAX as f64 {
Err(Error::InvalidArgument(
format!("Not representable: {} years in seconds exceeds u32",
n))
.into())
} else {
Ok((s as u32).into())
}
}
pub fn as_secs(self) -> u64 {
self.0 as u64
}
pub fn round_up<P, C>(&self, precision: P, ceil: C) -> Result<Duration>
where P: Into<Option<u8>>,
C: Into<Option<SystemDuration>>
{
let p = precision.into().unwrap_or(21) as u32;
if p < 32 {
if let Some(sum) = self.0.checked_add((1 << p) - 1) {
let rounded = Self(sum & !((1 << p) - 1));
match ceil.into() {
Some(ceil) => {
Ok(cmp::min(rounded, ceil.try_into()?))
},
None => Ok(rounded)
}
} else {
Ok(Self(std::u32::MAX))
}
} else {
Err(Error::InvalidArgument(
format!("Invalid precision {}", p)).into())
}
}
}
#[allow(unused)]
impl Timestamp {
pub(crate) const UNIX_EPOCH : Timestamp = Timestamp(0);
pub(crate) const MAX : Timestamp = Timestamp(u32::MAX);
pub(crate) const Y1970 : Timestamp = Timestamp(0);
pub(crate) const Y1970M2 : Timestamp = Timestamp(2678400);
pub(crate) const Y1971M2 : Timestamp = Timestamp(34214400);
pub(crate) const Y1972M2 : Timestamp = Timestamp(65750400);
pub(crate) const Y1973M2 : Timestamp = Timestamp(97372800);
pub(crate) const Y1974M2 : Timestamp = Timestamp(128908800);
pub(crate) const Y1975M2 : Timestamp = Timestamp(160444800);
pub(crate) const Y1976M2 : Timestamp = Timestamp(191980800);
pub(crate) const Y1977M2 : Timestamp = Timestamp(223603200);
pub(crate) const Y1978M2 : Timestamp = Timestamp(255139200);
pub(crate) const Y1979M2 : Timestamp = Timestamp(286675200);
pub(crate) const Y1980M2 : Timestamp = Timestamp(318211200);
pub(crate) const Y1981M2 : Timestamp = Timestamp(349833600);
pub(crate) const Y1982M2 : Timestamp = Timestamp(381369600);
pub(crate) const Y1983M2 : Timestamp = Timestamp(412905600);
pub(crate) const Y1984M2 : Timestamp = Timestamp(444441600);
pub(crate) const Y1985M2 : Timestamp = Timestamp(476064000);
pub(crate) const Y1986M2 : Timestamp = Timestamp(507600000);
pub(crate) const Y1987M2 : Timestamp = Timestamp(539136000);
pub(crate) const Y1988M2 : Timestamp = Timestamp(570672000);
pub(crate) const Y1989M2 : Timestamp = Timestamp(602294400);
pub(crate) const Y1990M2 : Timestamp = Timestamp(633830400);
pub(crate) const Y1991M2 : Timestamp = Timestamp(665366400);
pub(crate) const Y1992M2 : Timestamp = Timestamp(696902400);
pub(crate) const Y1993M2 : Timestamp = Timestamp(728524800);
pub(crate) const Y1994M2 : Timestamp = Timestamp(760060800);
pub(crate) const Y1995M2 : Timestamp = Timestamp(791596800);
pub(crate) const Y1996M2 : Timestamp = Timestamp(823132800);
pub(crate) const Y1997M2 : Timestamp = Timestamp(854755200);
pub(crate) const Y1998M2 : Timestamp = Timestamp(886291200);
pub(crate) const Y1999M2 : Timestamp = Timestamp(917827200);
pub(crate) const Y2000M2 : Timestamp = Timestamp(949363200);
pub(crate) const Y2001M2 : Timestamp = Timestamp(980985600);
pub(crate) const Y2002M2 : Timestamp = Timestamp(1012521600);
pub(crate) const Y2003M2 : Timestamp = Timestamp(1044057600);
pub(crate) const Y2004M2 : Timestamp = Timestamp(1075593600);
pub(crate) const Y2005M2 : Timestamp = Timestamp(1107216000);
pub(crate) const Y2006M2 : Timestamp = Timestamp(1138752000);
pub(crate) const Y2007M2 : Timestamp = Timestamp(1170288000);
pub(crate) const Y2008M2 : Timestamp = Timestamp(1201824000);
pub(crate) const Y2009M2 : Timestamp = Timestamp(1233446400);
pub(crate) const Y2010M2 : Timestamp = Timestamp(1264982400);
pub(crate) const Y2011M2 : Timestamp = Timestamp(1296518400);
pub(crate) const Y2012M2 : Timestamp = Timestamp(1328054400);
pub(crate) const Y2013M2 : Timestamp = Timestamp(1359676800);
pub(crate) const Y2014M2 : Timestamp = Timestamp(1391212800);
pub(crate) const Y2015M2 : Timestamp = Timestamp(1422748800);
pub(crate) const Y2016M2 : Timestamp = Timestamp(1454284800);
pub(crate) const Y2017M2 : Timestamp = Timestamp(1485907200);
pub(crate) const Y2018M2 : Timestamp = Timestamp(1517443200);
pub(crate) const Y2019M2 : Timestamp = Timestamp(1548979200);
pub(crate) const Y2020M2 : Timestamp = Timestamp(1580515200);
pub(crate) const Y2021M2 : Timestamp = Timestamp(1612137600);
pub(crate) const Y2022M2 : Timestamp = Timestamp(1643673600);
pub(crate) const Y2023M2 : Timestamp = Timestamp(1675209600);
pub(crate) const Y2024M2 : Timestamp = Timestamp(1706745600);
pub(crate) const Y2025M2 : Timestamp = Timestamp(1738368000);
pub(crate) const Y2026M2 : Timestamp = Timestamp(1769904000);
pub(crate) const Y2027M2 : Timestamp = Timestamp(1801440000);
pub(crate) const Y2028M2 : Timestamp = Timestamp(1832976000);
pub(crate) const Y2029M2 : Timestamp = Timestamp(1864598400);
pub(crate) const Y2030M2 : Timestamp = Timestamp(1896134400);
pub(crate) const Y2031M2 : Timestamp = Timestamp(1927670400);
pub(crate) const Y2032M2 : Timestamp = Timestamp(1959206400);
pub(crate) const Y2033M2 : Timestamp = Timestamp(1990828800);
pub(crate) const Y2034M2 : Timestamp = Timestamp(2022364800);
pub(crate) const Y2035M2 : Timestamp = Timestamp(2053900800);
pub(crate) const Y2036M2 : Timestamp = Timestamp(2085436800);
pub(crate) const Y2037M2 : Timestamp = Timestamp(2117059200);
pub(crate) const Y2038M2 : Timestamp = Timestamp(2148595200);
pub(crate) const Y2039M2 : Timestamp = Timestamp(2180131200);
pub(crate) const Y2040M2 : Timestamp = Timestamp(2211667200);
pub(crate) const Y2041M2 : Timestamp = Timestamp(2243289600);
pub(crate) const Y2042M2 : Timestamp = Timestamp(2274825600);
pub(crate) const Y2043M2 : Timestamp = Timestamp(2306361600);
pub(crate) const Y2044M2 : Timestamp = Timestamp(2337897600);
pub(crate) const Y2045M2 : Timestamp = Timestamp(2369520000);
pub(crate) const Y2046M2 : Timestamp = Timestamp(2401056000);
pub(crate) const Y2047M2 : Timestamp = Timestamp(2432592000);
pub(crate) const Y2048M2 : Timestamp = Timestamp(2464128000);
pub(crate) const Y2049M2 : Timestamp = Timestamp(2495750400);
pub(crate) const Y2050M2 : Timestamp = Timestamp(2527286400);
pub(crate) const Y2051M2 : Timestamp = Timestamp(2558822400);
pub(crate) const Y2052M2 : Timestamp = Timestamp(2590358400);
pub(crate) const Y2053M2 : Timestamp = Timestamp(2621980800);
pub(crate) const Y2054M2 : Timestamp = Timestamp(2653516800);
pub(crate) const Y2055M2 : Timestamp = Timestamp(2685052800);
pub(crate) const Y2056M2 : Timestamp = Timestamp(2716588800);
pub(crate) const Y2057M2 : Timestamp = Timestamp(2748211200);
pub(crate) const Y2058M2 : Timestamp = Timestamp(2779747200);
pub(crate) const Y2059M2 : Timestamp = Timestamp(2811283200);
pub(crate) const Y2060M2 : Timestamp = Timestamp(2842819200);
pub(crate) const Y2061M2 : Timestamp = Timestamp(2874441600);
pub(crate) const Y2062M2 : Timestamp = Timestamp(2905977600);
pub(crate) const Y2063M2 : Timestamp = Timestamp(2937513600);
pub(crate) const Y2064M2 : Timestamp = Timestamp(2969049600);
pub(crate) const Y2065M2 : Timestamp = Timestamp(3000672000);
pub(crate) const Y2066M2 : Timestamp = Timestamp(3032208000);
pub(crate) const Y2067M2 : Timestamp = Timestamp(3063744000);
pub(crate) const Y2068M2 : Timestamp = Timestamp(3095280000);
pub(crate) const Y2069M2 : Timestamp = Timestamp(3126902400);
pub(crate) const Y2070M2 : Timestamp = Timestamp(3158438400);
pub(crate) const Y2071M2 : Timestamp = Timestamp(3189974400);
pub(crate) const Y2072M2 : Timestamp = Timestamp(3221510400);
pub(crate) const Y2073M2 : Timestamp = Timestamp(3253132800);
pub(crate) const Y2074M2 : Timestamp = Timestamp(3284668800);
pub(crate) const Y2075M2 : Timestamp = Timestamp(3316204800);
pub(crate) const Y2076M2 : Timestamp = Timestamp(3347740800);
pub(crate) const Y2077M2 : Timestamp = Timestamp(3379363200);
pub(crate) const Y2078M2 : Timestamp = Timestamp(3410899200);
pub(crate) const Y2079M2 : Timestamp = Timestamp(3442435200);
pub(crate) const Y2080M2 : Timestamp = Timestamp(3473971200);
pub(crate) const Y2081M2 : Timestamp = Timestamp(3505593600);
pub(crate) const Y2082M2 : Timestamp = Timestamp(3537129600);
pub(crate) const Y2083M2 : Timestamp = Timestamp(3568665600);
pub(crate) const Y2084M2 : Timestamp = Timestamp(3600201600);
pub(crate) const Y2085M2 : Timestamp = Timestamp(3631824000);
pub(crate) const Y2086M2 : Timestamp = Timestamp(3663360000);
pub(crate) const Y2087M2 : Timestamp = Timestamp(3694896000);
pub(crate) const Y2088M2 : Timestamp = Timestamp(3726432000);
pub(crate) const Y2089M2 : Timestamp = Timestamp(3758054400);
pub(crate) const Y2090M2 : Timestamp = Timestamp(3789590400);
pub(crate) const Y2091M2 : Timestamp = Timestamp(3821126400);
pub(crate) const Y2092M2 : Timestamp = Timestamp(3852662400);
pub(crate) const Y2093M2 : Timestamp = Timestamp(3884284800);
pub(crate) const Y2094M2 : Timestamp = Timestamp(3915820800);
pub(crate) const Y2095M2 : Timestamp = Timestamp(3947356800);
pub(crate) const Y2096M2 : Timestamp = Timestamp(3978892800);
pub(crate) const Y2097M2 : Timestamp = Timestamp(4010515200);
pub(crate) const Y2098M2 : Timestamp = Timestamp(4042051200);
pub(crate) const Y2099M2 : Timestamp = Timestamp(4073587200);
pub(crate) const Y2100M2 : Timestamp = Timestamp(4105123200);
pub(crate) const Y2101M2 : Timestamp = Timestamp(4136659200);
pub(crate) const Y2102M2 : Timestamp = Timestamp(4168195200);
pub(crate) const Y2103M2 : Timestamp = Timestamp(4199731200);
pub(crate) const Y2104M2 : Timestamp = Timestamp(4231267200);
pub(crate) const Y2105M2 : Timestamp = Timestamp(4262889600);
pub(crate) const Y2106M2 : Timestamp = Timestamp(4294425600);
}
#[cfg(test)]
impl Arbitrary for Duration {
fn arbitrary(g: &mut Gen) -> Self {
Duration(u32::arbitrary(g))
}
}
pub(crate) fn normalize_systemtime(t: SystemTime) -> SystemTime {
UNIX_EPOCH + SystemDuration::new(
t.duration_since(UNIX_EPOCH).unwrap().as_secs(), 0)
}
#[cfg(test)]
mod tests {
use super::*;
quickcheck! {
fn timestamp_round_down(t: Timestamp) -> bool {
let u = t.round_down(None, None).unwrap();
assert!(u <= t);
assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
assert!(u32::from(t) - u32::from(u) < 2_u32.pow(21));
true
}
}
#[test]
fn timestamp_round_down_floor() -> Result<()> {
let t = Timestamp(1585753307);
let floor = t.checked_sub(Duration::weeks(1).unwrap()).unwrap();
let u = t.round_down(21, floor).unwrap();
assert!(u < t);
assert!(floor < u);
assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
let floor = t.checked_sub(Duration::days(1).unwrap()).unwrap();
let u = t.round_down(21, floor).unwrap();
assert_eq!(u, floor);
Ok(())
}
quickcheck! {
fn duration_round_up(d: Duration) -> bool {
let u = d.round_up(None, None).unwrap();
assert!(d <= u);
assert!(u32::from(u) & 0b1_1111_1111_1111_1111_1111 == 0
|| u32::from(u) == u32::MAX
);
assert!(u32::from(u) - u32::from(d) < 2_u32.pow(21));
true
}
}
#[test]
fn duration_round_up_ceil() -> Result<()> {
let d = Duration(123);
let ceil = Duration(2_u32.pow(23));
let u = d.round_up(21, ceil)?;
assert!(d < u);
assert!(u < ceil);
assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
let ceil = Duration::days(1).unwrap();
let u = d.round_up(21, ceil)?;
assert!(d < u);
assert_eq!(u, ceil);
Ok(())
}
#[test]
fn system_time_32_bit() -> Result<()> {
let is_system_time_too_small = UNIX_EPOCH
.checked_add(SystemDuration::new(i32::MAX as u64 + 1, 0))
.is_none();
let t1 = Timestamp::from(i32::MAX as u32 - 1);
let t2 = Timestamp::from(i32::MAX as u32);
let t3 = Timestamp::from(i32::MAX as u32 + 1);
let t4 = Timestamp::from(u32::MAX);
if is_system_time_too_small {
assert_eq!(SystemTime::from(t1),
UNIX_EPOCH + SystemDuration::new(i32::MAX as u64 - 1, 0));
assert_eq!(SystemTime::from(t2),
UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
assert_eq!(SystemTime::from(t3),
UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
assert_eq!(SystemTime::from(t4),
UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
} else {
assert_eq!(SystemTime::from(t1),
UNIX_EPOCH + SystemDuration::new(i32::MAX as u64 - 1, 0));
assert_eq!(SystemTime::from(t2),
UNIX_EPOCH + SystemDuration::new(i32::MAX as u64, 0));
assert_eq!(SystemTime::from(t3),
UNIX_EPOCH + SystemDuration::new(i32::MAX as u64 + 1, 0));
assert_eq!(SystemTime::from(t4),
UNIX_EPOCH + SystemDuration::new(u32::MAX as u64, 0));
}
Ok(())
}
}