use chrono::{Duration,NaiveDateTime, DateTime, TimeZone, Utc};
pub fn naive_datetime_from_millis(istamp:i64)->NaiveDateTime{
let secs: i64 = istamp/1000;
let nsecs: u32 = ((istamp % 1000) * 1000000) as u32;
NaiveDateTime::from_timestamp(secs, nsecs)
}
pub trait DurationRoudable<T>{
fn get_utc_millis_since_epoch(&self) -> i64;
fn repr_from_utc_millis(&self, utc_milli_stamp: i64) -> T; }
impl DurationRoudable<NaiveDateTime> for NaiveDateTime{
fn get_utc_millis_since_epoch(&self) -> i64{
self.timestamp_millis()
}
fn repr_from_utc_millis(&self, utc_milli_stamp: i64) -> NaiveDateTime {
naive_datetime_from_millis(utc_milli_stamp)
}
}
impl<TZInfo: TimeZone> DurationRoudable<DateTime<TZInfo>> for DateTime<TZInfo>{
fn get_utc_millis_since_epoch(&self) -> i64{
let ndt = self.naive_utc();
ndt.timestamp_millis()
}
fn repr_from_utc_millis(&self, utc_milli_stamp: i64) -> DateTime<TZInfo> {
let ndt = naive_datetime_from_millis(utc_milli_stamp);
let utcdt = DateTime::<Utc>::from_utc(ndt,Utc);
utcdt.with_timezone(&self.timezone())
}
}
trait ModuloSignedExt {
fn modulo(&self, n: Self) -> Self;
}
macro_rules! modulo_signed_ext_impl {
($($t:ty)*) => ($(
impl ModuloSignedExt for $t {
#[inline]
fn modulo(&self, n: Self) -> Self {
(self % n + n) % n
}
}
)*)
}
modulo_signed_ext_impl! { i8 i16 i32 i64 i128 }
pub fn round_up_to_nearest_duration<TDate>(timestamp: &TDate, sample_size :&Duration) -> TDate
where TDate : DurationRoudable<TDate>
{
let mod_ticks = timestamp.get_utc_millis_since_epoch().modulo(sample_size.num_milliseconds());
let delta = std::cmp::max(sample_size.num_milliseconds() - mod_ticks,0);
let rs = timestamp.get_utc_millis_since_epoch() + delta;
timestamp.repr_from_utc_millis(rs)
}
pub fn round_down_to_nearest_duration<TDate>(timestamp: &TDate, sample_size :&Duration) -> TDate
where TDate : DurationRoudable<TDate>
{
let mod_ticks = timestamp.get_utc_millis_since_epoch().modulo(sample_size.num_milliseconds());
let rs = timestamp.get_utc_millis_since_epoch() - mod_ticks;
timestamp.repr_from_utc_millis(rs)
}
pub fn round_nearest_to_nearest_duration<TDate>(timestamp: &TDate, sample_size :&Duration) -> TDate
where TDate : DurationRoudable<TDate>
{
let mod_ticks = timestamp.get_utc_millis_since_epoch().modulo(sample_size.num_milliseconds());
let offset = match mod_ticks > (sample_size.num_milliseconds()/2) {
true => sample_size.num_milliseconds(),
false => 0
};
let rs = timestamp.get_utc_millis_since_epoch() + offset - mod_ticks;
timestamp.repr_from_utc_millis(rs)
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::NaiveDate;
#[test]
fn test_rounding_up() {
let date1 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 34, 56, 789);
let dur = Duration::minutes(1);
let rounded = round_up_to_nearest_duration(&date1, &dur);
let exp1 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 35, 0, 0);
assert_eq!(rounded,exp1);
let dur2 = Duration::minutes(15);
let rounded2 = round_up_to_nearest_duration(&date1, &dur2);
let exp2 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 45, 0, 0);
assert_eq!(rounded2,exp2);
}
#[test]
fn test_rounding_down() {
let date1 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 34, 56, 789);
let dur = Duration::minutes(1);
let rounded = round_down_to_nearest_duration(&date1, &dur);
let exp1 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 34, 0, 0);
assert_eq!(rounded,exp1);
let dur2 = Duration::minutes(15);
let rounded2 = round_down_to_nearest_duration(&date1, &dur2);
let exp2 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 30, 0, 0);
assert_eq!(rounded2,exp2);
}
#[test]
fn test_rounding_nearest() {
let date1 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 34, 30, 789);
let dur = Duration::minutes(1);
let rounded = round_nearest_to_nearest_duration(&date1, &dur);
let exp1 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 35, 0, 0);
assert_eq!(rounded,exp1);
let dur2 = Duration::minutes(15);
let rounded2 = round_nearest_to_nearest_duration(&date1, &dur2);
let exp2 = NaiveDate::from_ymd(2010,12,10).and_hms_milli(12, 30, 0, 0);
assert_eq!(rounded2,exp2);
}
}