Skip to content

Instantly share code, notes, and snippets.

@jstrong-tios
Last active November 22, 2019 15:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jstrong-tios/770a31c711f399155b0a40d045b98ad7 to your computer and use it in GitHub Desktop.
Save jstrong-tios/770a31c711f399155b0a40d045b98ad7 to your computer and use it in GitHub Desktop.
hourly/daily integer wrapper types
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