#[cfg(feature = "serde-support")]
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
pub struct UtcTimeStamp(i64);
impl Display for UtcTimeStamp {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
chrono::DateTime::<chrono::Utc>::from(*self).fmt(f)
}
}
impl From<chrono::DateTime<chrono::Utc>> for UtcTimeStamp {
fn from(other: chrono::DateTime<chrono::Utc>) -> Self {
Self(other.timestamp_millis())
}
}
impl From<UtcTimeStamp> for chrono::DateTime<chrono::Utc> {
fn from(other: UtcTimeStamp) -> Self {
let sec = other.0 / 1000;
let ns = (other.0 % 1000 * 1_000_000) as u32;
let naive = chrono::NaiveDateTime::from_timestamp(sec, ns);
chrono::DateTime::<chrono::Utc>::from_utc(naive, chrono::Utc)
}
}
impl UtcTimeStamp {
pub const fn zero() -> Self {
UtcTimeStamp(0)
}
pub fn now() -> Self {
chrono::Utc::now().into()
}
pub const fn from_milliseconds(int: i64) -> Self {
UtcTimeStamp(int)
}
pub const fn as_milliseconds(self) -> i64 {
self.0
}
pub const fn align_to(self, freq: TimeDelta) -> UtcTimeStamp {
UtcTimeStamp(self.0 / freq.0 * freq.0)
}
}
impl std::ops::Add<TimeDelta> for UtcTimeStamp {
type Output = UtcTimeStamp;
fn add(self, rhs: TimeDelta) -> Self::Output {
UtcTimeStamp(self.0 + rhs.0)
}
}
impl std::ops::AddAssign<TimeDelta> for UtcTimeStamp {
fn add_assign(&mut self, rhs: TimeDelta) {
*self = *self + rhs;
}
}
impl std::ops::Sub<TimeDelta> for UtcTimeStamp {
type Output = UtcTimeStamp;
fn sub(self, rhs: TimeDelta) -> Self::Output {
UtcTimeStamp(self.0 - rhs.0)
}
}
impl std::ops::SubAssign<TimeDelta> for UtcTimeStamp {
fn sub_assign(&mut self, rhs: TimeDelta) {
*self = *self - rhs;
}
}
impl std::ops::Sub<UtcTimeStamp> for UtcTimeStamp {
type Output = TimeDelta;
fn sub(self, rhs: UtcTimeStamp) -> Self::Output {
TimeDelta(self.0 - rhs.0)
}
}
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
pub struct TimeDelta(i64);
impl Display for TimeDelta {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
chrono::Duration::from(*self).fmt(f)
}
}
impl From<chrono::Duration> for TimeDelta {
fn from(other: chrono::Duration) -> Self {
Self(other.num_milliseconds())
}
}
impl From<TimeDelta> for chrono::Duration {
fn from(other: TimeDelta) -> Self {
chrono::Duration::milliseconds(other.0)
}
}
impl std::ops::Add<TimeDelta> for TimeDelta {
type Output = TimeDelta;
fn add(self, rhs: TimeDelta) -> Self::Output {
TimeDelta(self.0 + rhs.0)
}
}
impl std::ops::Sub<TimeDelta> for TimeDelta {
type Output = TimeDelta;
fn sub(self, rhs: TimeDelta) -> Self::Output {
TimeDelta(self.0 - rhs.0)
}
}
impl std::ops::Mul<i64> for TimeDelta {
type Output = TimeDelta;
fn mul(self, rhs: i64) -> Self::Output {
TimeDelta(self.0 * rhs)
}
}
impl std::ops::Div<i64> for TimeDelta {
type Output = TimeDelta;
fn div(self, rhs: i64) -> Self::Output {
TimeDelta(self.0 / rhs)
}
}
impl std::ops::Div<TimeDelta> for TimeDelta {
type Output = i64;
fn div(self, rhs: TimeDelta) -> Self::Output {
self.0 / rhs.0
}
}
impl std::ops::Rem<TimeDelta> for TimeDelta {
type Output = TimeDelta;
fn rem(self, rhs: TimeDelta) -> Self::Output {
TimeDelta(self.0 % rhs.0)
}
}
impl TimeDelta {
pub const fn zero() -> Self {
TimeDelta(0)
}
pub const fn from_milliseconds(int: i64) -> Self {
TimeDelta(int)
}
pub const fn as_milliseconds(self) -> i64 {
self.0
}
}
#[derive(Debug)]
pub struct TimeRange {
cur: UtcTimeStamp,
end: UtcTimeStamp,
step: TimeDelta,
right_closed: bool,
}
impl TimeRange {
pub fn right_closed(
start: impl Into<UtcTimeStamp>,
end: impl Into<UtcTimeStamp>,
step: impl Into<TimeDelta>,
) -> Self {
TimeRange {
cur: start.into(),
end: end.into(),
step: step.into(),
right_closed: true,
}
}
pub fn right_open(
start: impl Into<UtcTimeStamp>,
end: impl Into<UtcTimeStamp>,
step: impl Into<TimeDelta>,
) -> Self {
TimeRange {
cur: start.into(),
end: end.into(),
step: step.into(),
right_closed: false,
}
}
}
impl Iterator for TimeRange {
type Item = UtcTimeStamp;
fn next(&mut self) -> Option<Self::Item> {
let exhausted = if self.right_closed {
self.cur > self.end
} else {
self.cur >= self.end
};
if exhausted {
None
} else {
let cur = self.cur;
self.cur += self.step;
Some(cur)
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
use chrono::{offset::TimeZone, Duration, Utc};
#[test]
fn mod_div() {
let a = Utc.ymd(2019, 4, 14).and_hms(0, 0, 0);
let b = Utc.ymd(2019, 4, 16).and_hms(0, 0, 0);
}
#[test]
fn open_time_range() {
let start = Utc.ymd(2019, 4, 14).and_hms(0, 0, 0);
let end = Utc.ymd(2019, 4, 16).and_hms(0, 0, 0);
let step = Duration::hours(12);
let tr: Vec<_> = Iterator::collect(TimeRange::right_closed(start, end, step));
assert_eq!(
tr,
vec![
Utc.ymd(2019, 4, 14).and_hms(0, 0, 0).into(),
Utc.ymd(2019, 4, 14).and_hms(12, 0, 0).into(),
Utc.ymd(2019, 4, 15).and_hms(0, 0, 0).into(),
Utc.ymd(2019, 4, 15).and_hms(12, 0, 0).into(),
Utc.ymd(2019, 4, 16).and_hms(0, 0, 0).into(),
]
);
}
#[test]
fn timestamp_and_delta_vs_chrono() {
let c_dt = Utc.ymd(2019, 3, 13).and_hms(16, 14, 9);
let c_td = Duration::milliseconds(123456);
let my_dt = UtcTimeStamp::from(c_dt.clone());
let my_td = TimeDelta::from_milliseconds(123456);
assert_eq!(TimeDelta::from(c_td.clone()), my_td);
let c_result = c_dt + c_td * 555;
let my_result = my_dt + my_td * 555;
assert_eq!(UtcTimeStamp::from(c_result.clone()), my_result);
}
#[test]
fn timestamp_ord_eq() {
let ts1: UtcTimeStamp = UtcTimeStamp::from_milliseconds(111);
let ts2: UtcTimeStamp = UtcTimeStamp::from_milliseconds(222);
let ts3: UtcTimeStamp = UtcTimeStamp::from_milliseconds(222);
assert!(ts1 < ts2);
assert!(ts2 > ts1);
assert!(ts1 <= ts2);
assert!(ts2 >= ts3);
assert!(ts2 <= ts3);
assert!(ts2 >= ts3);
assert_eq!(ts2, ts3);
assert_ne!(ts1, ts3);
}
}