use std::cmp;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::time::{SystemTime, Duration as SystemDuration, UNIX_EPOCH};
use std::u32;
use quickcheck::{Arbitrary, Gen};
use crate::{
Error,
Result,
};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp(u32);
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 + SystemDuration::new(t.0 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 {
SystemTime::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(|v| Self(v))
}
pub fn checked_sub(&self, d: Duration) -> Option<Timestamp> {
self.0.checked_sub(d.0).map(|v| Self(v))
}
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())
}
}
}
impl Arbitrary for Timestamp {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Timestamp(u32::arbitrary(g))
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Duration(u32);
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 fn seconds(n: u32) -> Duration {
n.into()
}
pub fn minutes(n: u32) -> Result<Duration> {
60u32.checked_mul(n).ok_or(())
.map(Self::seconds)
.map_err(|_| Error::InvalidArgument(
format!("Not representable: {} minutes in seconds exceeds u32",
n)).into())
}
pub fn hours(n: u32) -> Result<Duration> {
60u32.checked_mul(n)
.ok_or(Error::InvalidArgument("".into()).into())
.and_then(Self::minutes)
.map_err(|_| Error::InvalidArgument(
format!("Not representable: {} hours in seconds exceeds u32",
n)).into())
}
pub fn days(n: u32) -> Result<Duration> {
24u32.checked_mul(n)
.ok_or(Error::InvalidArgument("".into()).into())
.and_then(Self::hours)
.map_err(|_| Error::InvalidArgument(
format!("Not representable: {} days in seconds exceeds u32",
n)).into())
}
pub fn weeks(n: u32) -> Result<Duration> {
7u32.checked_mul(n)
.ok_or(Error::InvalidArgument("".into()).into())
.and_then(Self::days)
.map_err(|_| Error::InvalidArgument(
format!("Not representable: {} weeks in seconds exceeds u32",
n)).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 Y1971 : Timestamp = Timestamp(31536000);
pub(crate) const Y1972 : Timestamp = Timestamp(63072000);
pub(crate) const Y1973 : Timestamp = Timestamp(94694400);
pub(crate) const Y1974 : Timestamp = Timestamp(126230400);
pub(crate) const Y1975 : Timestamp = Timestamp(157766400);
pub(crate) const Y1976 : Timestamp = Timestamp(189302400);
pub(crate) const Y1977 : Timestamp = Timestamp(220924800);
pub(crate) const Y1978 : Timestamp = Timestamp(252460800);
pub(crate) const Y1979 : Timestamp = Timestamp(283996800);
pub(crate) const Y1980 : Timestamp = Timestamp(315532800);
pub(crate) const Y1981 : Timestamp = Timestamp(347155200);
pub(crate) const Y1982 : Timestamp = Timestamp(378691200);
pub(crate) const Y1983 : Timestamp = Timestamp(410227200);
pub(crate) const Y1984 : Timestamp = Timestamp(441763200);
pub(crate) const Y1985 : Timestamp = Timestamp(473385600);
pub(crate) const Y1986 : Timestamp = Timestamp(504921600);
pub(crate) const Y1987 : Timestamp = Timestamp(536457600);
pub(crate) const Y1988 : Timestamp = Timestamp(567993600);
pub(crate) const Y1989 : Timestamp = Timestamp(599616000);
pub(crate) const Y1990 : Timestamp = Timestamp(631152000);
pub(crate) const Y1991 : Timestamp = Timestamp(662688000);
pub(crate) const Y1992 : Timestamp = Timestamp(694224000);
pub(crate) const Y1993 : Timestamp = Timestamp(725846400);
pub(crate) const Y1994 : Timestamp = Timestamp(757382400);
pub(crate) const Y1995 : Timestamp = Timestamp(788918400);
pub(crate) const Y1996 : Timestamp = Timestamp(820454400);
pub(crate) const Y1997 : Timestamp = Timestamp(852076800);
pub(crate) const Y1998 : Timestamp = Timestamp(883612800);
pub(crate) const Y1999 : Timestamp = Timestamp(915148800);
pub(crate) const Y2000 : Timestamp = Timestamp(946684800);
pub(crate) const Y2001 : Timestamp = Timestamp(978307200);
pub(crate) const Y2002 : Timestamp = Timestamp(1009843200);
pub(crate) const Y2003 : Timestamp = Timestamp(1041379200);
pub(crate) const Y2004 : Timestamp = Timestamp(1072915200);
pub(crate) const Y2005 : Timestamp = Timestamp(1104537600);
pub(crate) const Y2006 : Timestamp = Timestamp(1136073600);
pub(crate) const Y2007 : Timestamp = Timestamp(1167609600);
pub(crate) const Y2008 : Timestamp = Timestamp(1199145600);
pub(crate) const Y2009 : Timestamp = Timestamp(1230768000);
pub(crate) const Y2010 : Timestamp = Timestamp(1262304000);
pub(crate) const Y2011 : Timestamp = Timestamp(1293840000);
pub(crate) const Y2012 : Timestamp = Timestamp(1325376000);
pub(crate) const Y2013 : Timestamp = Timestamp(1356998400);
pub(crate) const Y2014 : Timestamp = Timestamp(1388534400);
pub(crate) const Y2015 : Timestamp = Timestamp(1420070400);
pub(crate) const Y2016 : Timestamp = Timestamp(1451606400);
pub(crate) const Y2017 : Timestamp = Timestamp(1483228800);
pub(crate) const Y2018 : Timestamp = Timestamp(1514764800);
pub(crate) const Y2019 : Timestamp = Timestamp(1546300800);
pub(crate) const Y2020 : Timestamp = Timestamp(1577836800);
pub(crate) const Y2021 : Timestamp = Timestamp(1609459200);
pub(crate) const Y2022 : Timestamp = Timestamp(1640995200);
pub(crate) const Y2023 : Timestamp = Timestamp(1672531200);
pub(crate) const Y2024 : Timestamp = Timestamp(1704067200);
pub(crate) const Y2025 : Timestamp = Timestamp(1735689600);
pub(crate) const Y2026 : Timestamp = Timestamp(1767225600);
pub(crate) const Y2027 : Timestamp = Timestamp(1798761600);
pub(crate) const Y2028 : Timestamp = Timestamp(1830297600);
pub(crate) const Y2029 : Timestamp = Timestamp(1861920000);
pub(crate) const Y2030 : Timestamp = Timestamp(1893456000);
pub(crate) const Y2031 : Timestamp = Timestamp(1924992000);
pub(crate) const Y2032 : Timestamp = Timestamp(1956528000);
pub(crate) const Y2033 : Timestamp = Timestamp(1988150400);
pub(crate) const Y2034 : Timestamp = Timestamp(2019686400);
pub(crate) const Y2035 : Timestamp = Timestamp(2051222400);
pub(crate) const Y2036 : Timestamp = Timestamp(2082758400);
pub(crate) const Y2037 : Timestamp = Timestamp(2114380800);
pub(crate) const Y2038 : Timestamp = Timestamp(2145916800);
pub(crate) const Y2039 : Timestamp = Timestamp(2177452800);
pub(crate) const Y2040 : Timestamp = Timestamp(2208988800);
pub(crate) const Y2041 : Timestamp = Timestamp(2240611200);
pub(crate) const Y2042 : Timestamp = Timestamp(2272147200);
pub(crate) const Y2043 : Timestamp = Timestamp(2303683200);
pub(crate) const Y2044 : Timestamp = Timestamp(2335219200);
pub(crate) const Y2045 : Timestamp = Timestamp(2366841600);
pub(crate) const Y2046 : Timestamp = Timestamp(2398377600);
pub(crate) const Y2047 : Timestamp = Timestamp(2429913600);
pub(crate) const Y2048 : Timestamp = Timestamp(2461449600);
pub(crate) const Y2049 : Timestamp = Timestamp(2493072000);
pub(crate) const Y2050 : Timestamp = Timestamp(2524608000);
pub(crate) const Y2051 : Timestamp = Timestamp(2556144000);
pub(crate) const Y2052 : Timestamp = Timestamp(2587680000);
pub(crate) const Y2053 : Timestamp = Timestamp(2619302400);
pub(crate) const Y2054 : Timestamp = Timestamp(2650838400);
pub(crate) const Y2055 : Timestamp = Timestamp(2682374400);
pub(crate) const Y2056 : Timestamp = Timestamp(2713910400);
pub(crate) const Y2057 : Timestamp = Timestamp(2745532800);
pub(crate) const Y2058 : Timestamp = Timestamp(2777068800);
pub(crate) const Y2059 : Timestamp = Timestamp(2808604800);
pub(crate) const Y2060 : Timestamp = Timestamp(2840140800);
pub(crate) const Y2061 : Timestamp = Timestamp(2871763200);
pub(crate) const Y2062 : Timestamp = Timestamp(2903299200);
pub(crate) const Y2063 : Timestamp = Timestamp(2934835200);
pub(crate) const Y2064 : Timestamp = Timestamp(2966371200);
pub(crate) const Y2065 : Timestamp = Timestamp(2997993600);
pub(crate) const Y2066 : Timestamp = Timestamp(3029529600);
pub(crate) const Y2067 : Timestamp = Timestamp(3061065600);
pub(crate) const Y2068 : Timestamp = Timestamp(3092601600);
pub(crate) const Y2069 : Timestamp = Timestamp(3124224000);
pub(crate) const Y2070 : Timestamp = Timestamp(3155760000);
pub(crate) const Y2071 : Timestamp = Timestamp(3187296000);
pub(crate) const Y2072 : Timestamp = Timestamp(3218832000);
pub(crate) const Y2073 : Timestamp = Timestamp(3250454400);
pub(crate) const Y2074 : Timestamp = Timestamp(3281990400);
pub(crate) const Y2075 : Timestamp = Timestamp(3313526400);
pub(crate) const Y2076 : Timestamp = Timestamp(3345062400);
pub(crate) const Y2077 : Timestamp = Timestamp(3376684800);
pub(crate) const Y2078 : Timestamp = Timestamp(3408220800);
pub(crate) const Y2079 : Timestamp = Timestamp(3439756800);
pub(crate) const Y2080 : Timestamp = Timestamp(3471292800);
pub(crate) const Y2081 : Timestamp = Timestamp(3502915200);
pub(crate) const Y2082 : Timestamp = Timestamp(3534451200);
pub(crate) const Y2083 : Timestamp = Timestamp(3565987200);
pub(crate) const Y2084 : Timestamp = Timestamp(3597523200);
pub(crate) const Y2085 : Timestamp = Timestamp(3629145600);
pub(crate) const Y2086 : Timestamp = Timestamp(3660681600);
pub(crate) const Y2087 : Timestamp = Timestamp(3692217600);
pub(crate) const Y2088 : Timestamp = Timestamp(3723753600);
pub(crate) const Y2089 : Timestamp = Timestamp(3755376000);
pub(crate) const Y2090 : Timestamp = Timestamp(3786912000);
pub(crate) const Y2091 : Timestamp = Timestamp(3818448000);
pub(crate) const Y2092 : Timestamp = Timestamp(3849984000);
pub(crate) const Y2093 : Timestamp = Timestamp(3881606400);
pub(crate) const Y2094 : Timestamp = Timestamp(3913142400);
pub(crate) const Y2095 : Timestamp = Timestamp(3944678400);
pub(crate) const Y2096 : Timestamp = Timestamp(3976214400);
pub(crate) const Y2097 : Timestamp = Timestamp(4007836800);
pub(crate) const Y2098 : Timestamp = Timestamp(4039372800);
pub(crate) const Y2099 : Timestamp = Timestamp(4070908800);
pub(crate) const Y2100 : Timestamp = Timestamp(4102444800);
pub(crate) const Y2101 : Timestamp = Timestamp(4133980800);
pub(crate) const Y2102 : Timestamp = Timestamp(4165516800);
pub(crate) const Y2103 : Timestamp = Timestamp(4197052800);
pub(crate) const Y2104 : Timestamp = Timestamp(4228588800);
pub(crate) const Y2105 : Timestamp = Timestamp(4260211200);
pub(crate) const Y2106 : Timestamp = Timestamp(4291747200);
}
impl Arbitrary for Duration {
fn arbitrary<G: Gen>(g: &mut G) -> 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_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
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(())
}
}