Last active
November 22, 2019 15:51
-
-
Save jstrong-tios/770a31c711f399155b0a40d045b98ad7 to your computer and use it in GitHub Desktop.
hourly/daily integer wrapper types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::fmt::{self, Debug, Display}; | |
use std::convert::TryFrom; | |
use chrono::{NaiveDate, NaiveTime, NaiveDateTime, Timelike}; | |
/// Represents number of hours since 1970 | |
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, | |
Hash, Copy, Serialize, Deserialize)] | |
pub struct DtHr(pub i32); | |
/// Represents number of days since 1970. Note: will break on June 6, 2149 | |
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, | |
Copy, Hash, Serialize, Deserialize)] | |
pub struct Dt(pub u16); | |
impl From<(NaiveDate, i8)> for DtHr { | |
fn from((dt, hr): (NaiveDate, i8)) -> Self { | |
let time: NaiveDateTime = dt.and_time(NaiveTime::from_hms(hr as u32, 0, 0)); | |
let seconds: i64 = time.timestamp(); | |
DtHr((seconds / 60 / 60) as i32) | |
} | |
} | |
impl From<NaiveDate> for DtHr { | |
fn from(date: NaiveDate) -> Self { | |
let time: NaiveDateTime = date.and_time(NaiveTime::from_hms(0, 0, 0)); | |
let seconds: i64 = time.timestamp(); | |
DtHr((seconds / 60 / 60) as i32) | |
} | |
} | |
impl From<NaiveDate> for Dt { | |
fn from(date: NaiveDate) -> Self { | |
let time: NaiveDateTime = date.and_time(NaiveTime::from_hms(0, 0, 0)); | |
let seconds: i64 = time.timestamp(); | |
Dt((seconds / 60 / 60 / 24) as u16) | |
} | |
} | |
impl From<i32> for DtHr { | |
fn from(hour: i32) -> Self { | |
DtHr(hour) | |
} | |
} | |
impl From<DtHr> for i32 { | |
fn from(dthr: DtHr) -> Self { | |
let DtHr(h) = dthr; | |
h | |
} | |
} | |
impl From<u16> for Dt { | |
fn from(day: u16) -> Self { | |
Dt(day) | |
} | |
} | |
impl DtHr { | |
pub fn to_datetime(&self) -> NaiveDateTime { | |
NaiveDateTime::from_timestamp(i64::from(self.0) * 60 * 60, 0) | |
} | |
pub fn from_datetime(dt: NaiveDateTime) -> Self { | |
DtHr(i32::try_from(dt.timestamp() / 60 / 60).unwrap()) | |
} | |
pub fn to_date(&self) -> NaiveDate { | |
self.to_datetime().date() | |
} | |
pub fn as_tuple(&self) -> (NaiveDate, i8) { | |
let time = self.to_datetime(); | |
(time.date(), time.hour() as i8) | |
} | |
pub fn dt(&self) -> Dt { | |
Dt(u16::try_from(self.0 / 24).unwrap()) // crash if somehow invalid data | |
} | |
pub fn hr(&self) -> u8 { | |
(self.0 % 24) as u8 | |
} | |
pub fn truncate_tm(&self) -> DtHr { | |
DtHr(self.0 - self.0 % 24) | |
} | |
} | |
impl Dt { | |
pub fn to_date(&self) -> NaiveDate { | |
NaiveDateTime::from_timestamp(i64::from(self.0) * 24 * 60 * 60, 0) | |
.date() | |
} | |
} | |
impl<'a> From<&'a DtHr> for Dt { | |
fn from(dthr: &'a DtHr) -> Self { | |
Dt(u16::try_from(dthr.0 / 24).unwrap()) // crash if somehow invalid data | |
} | |
} | |
impl<'a> From<&'a Dt> for DtHr { | |
fn from(dthr: &'a Dt) -> Self { | |
DtHr(i32::from(dthr.0) * 24) | |
} | |
} | |
impl From<Dt> for DtHr { | |
fn from(dthr: Dt) -> Self { | |
DtHr(i32::from(dthr.0) * 24) | |
} | |
} | |
impl Debug for DtHr { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
let time = self.to_datetime(); | |
write!(f, "<DtHr(raw value = {}, date = {}, hour = {})>", | |
self.0, time.date(), time.time()) | |
} | |
} | |
impl Display for DtHr { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
let time = self.to_datetime(); | |
write!(f, "{}", time) | |
} | |
} | |
impl Debug for Dt { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
let date = self.to_date(); | |
write!(f, "<DtHr(raw value = {}, date = {})>", | |
self.0, date) | |
} | |
} | |
impl Display for Dt { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
let date = self.to_date(); | |
write!(f, "{}", date) | |
} | |
} | |
impl std::ops::Add<Dt> for Dt { | |
type Output = Dt; | |
fn add(self, other: Dt) -> Dt { | |
Dt(self.0.saturating_add(other.0)) | |
} | |
} | |
impl std::ops::AddAssign<Dt> for Dt { | |
fn add_assign(&mut self, other: Dt) { | |
self.0 = self.0.saturating_add(other.0); | |
} | |
} | |
impl std::ops::Sub<Dt> for Dt { | |
type Output = Dt; | |
fn sub(self, other: Dt) -> Dt { | |
Dt(self.0.saturating_sub(other.0)) | |
} | |
} | |
impl std::ops::SubAssign<Dt> for Dt { | |
fn sub_assign(&mut self, other: Dt) { | |
self.0 = self.0.saturating_sub(other.0); | |
} | |
} | |
impl std::ops::Add<u16> for Dt { | |
type Output = Dt; | |
fn add(self, n_days: u16) -> Dt { | |
Dt(self.0.saturating_add(n_days)) | |
} | |
} | |
impl std::ops::AddAssign<u16> for Dt { | |
fn add_assign(&mut self, n_days: u16) { | |
self.0 = self.0.saturating_add(n_days); | |
} | |
} | |
impl std::ops::Sub<u16> for Dt { | |
type Output = Dt; | |
fn sub(self, n_days: u16) -> Dt { | |
Dt(self.0.saturating_sub(n_days)) | |
} | |
} | |
impl std::ops::SubAssign<u16> for Dt { | |
fn sub_assign(&mut self, n_days: u16) { | |
self.0 = self.0.saturating_sub(n_days); | |
} | |
} | |
impl std::ops::Add<i32> for DtHr { | |
type Output = DtHr; | |
fn add(self, n_hrs: i32) -> DtHr { | |
DtHr(self.0.saturating_add(n_hrs)) | |
} | |
} | |
impl std::ops::AddAssign<i32> for DtHr { | |
fn add_assign(&mut self, n_hrs: i32) { | |
self.0 = self.0.saturating_add(n_hrs); | |
} | |
} | |
impl std::ops::Sub<i32> for DtHr { | |
type Output = DtHr; | |
fn sub(self, n_hrs: i32) -> DtHr { | |
DtHr(self.0.saturating_sub(n_hrs)) | |
} | |
} | |
impl std::ops::SubAssign<i32> for DtHr { | |
fn sub_assign(&mut self, n_hrs: i32) { | |
self.0 = self.0.saturating_sub(n_hrs); | |
} | |
} | |
#[allow(unused)] | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
use chrono::{Utc, TimeZone}; | |
const RT_ITER: usize = 512; | |
#[test] | |
fn it_performs_round_trip_conversion_with_date_hour() { | |
let dt = NaiveDate::from_ymd(2019, 3, 18); | |
let hr = 11i8; | |
let dt_hr = DtHr::from((dt, hr)); | |
assert_eq!(dt_hr.as_tuple(), (dt, hr)); | |
} | |
#[test] | |
fn it_generates_date_hour_debug() { | |
let dt = NaiveDate::from_ymd(2019, 3, 18); | |
let hr = 11i8; | |
let dt_hr = DtHr::from((dt, hr)); | |
let debug = format!("{:?}", dt_hr); | |
assert!(debug.contains(&format!("raw value = {}", dt_hr.0)), "debug = {}", debug); | |
} | |
#[test] | |
fn check_lossy_round_trip_for_range() { | |
let mut cur = Utc.ymd(1983, 7, 27).and_hms(1, 0, 0); | |
let incr = chrono::Duration::hours(1_679); | |
for _ in 0..RT_ITER { | |
cur = cur + incr; | |
let naive = cur.naive_utc(); | |
println!("{}", naive); | |
let dthr = DtHr::from_datetime(naive); | |
assert_eq!(dthr.to_datetime(), naive); | |
assert_eq!(dthr.to_date(), naive.date()); | |
assert_eq!(dthr.to_date(), cur.naive_utc().date()); | |
let dt = dthr.dt(); | |
assert_eq!(dt.to_date(), dthr.to_date()); | |
assert_eq!(dt.to_date(), naive.date()); | |
assert_eq!(dt.to_date(), cur.naive_utc().date()); | |
let rt = DtHr::from(&dt); | |
assert_eq!(rt.to_date(), naive.date()); | |
assert_eq!(rt.to_date(), cur.naive_utc().date()); | |
assert_eq!(rt.to_datetime().date(), cur.naive_utc().date()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment