use chrono::{DateTime, Datelike, Utc}; use diesel::{ backend::Backend, deserialize::{self, FromSql, FromSqlRow}, expression::AsExpression, serialize::{self, ToSql}, sql_types::Text, sqlite::Sqlite, }; use serde::{Deserialize, Serialize}; #[derive( Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Copy, Clone, Debug, AsExpression, FromSqlRow, )] #[diesel(sql_type=Text)] pub struct DateTimeUtc(DateTime); impl DateTimeUtc { pub fn day_diff(&self, other: &DateTimeUtc) -> u32 { self.0 .num_days_from_ce() .abs_diff(other.0.num_days_from_ce()) } pub fn new(date_time_utc: DateTime) -> Self { Self(date_time_utc) } pub fn now() -> Self { Self(Utc::now()) } } impl ToSql for DateTimeUtc { fn to_sql<'b>( &'b self, out: &mut diesel::serialize::Output<'b, '_, Sqlite>, ) -> serialize::Result { out.set_value(self.0.to_rfc3339()); Ok(diesel::serialize::IsNull::No) } } impl FromSql for DateTimeUtc { fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { let str: String = FromSql::::from_sql(bytes)?; let date_time = str.as_str().parse::>()?; Ok(DateTimeUtc(date_time)) } } #[cfg(test)] mod test { use chrono::TimeZone; use super::*; #[test] fn given_midnight_when_day_diff_next_midnight_less_1s_then_return_0() { let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc()); let end = DateTimeUtc::new( Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59) .unwrap() .to_utc(), ); let day_diff = start.day_diff(&end); let neg_day_diff = end.day_diff(&start); assert_eq!(day_diff, 0); assert_eq!(day_diff, neg_day_diff); } #[test] fn given_midnight_when_day_diff_next_midnight_then_return_1() { let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc()); let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc()); let day_diff = start.day_diff(&end); let neg_day_diff = end.day_diff(&start); assert_eq!(day_diff, 1); assert_eq!(day_diff, neg_day_diff); } #[test] fn given_midnight_less_1s_when_day_diff_midnight_then_return_1() { let start = DateTimeUtc::new( Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59) .unwrap() .to_utc(), ); let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc()); let day_diff = start.day_diff(&end); let neg_day_diff = end.day_diff(&start); assert_eq!(day_diff, 1); assert_eq!(day_diff, neg_day_diff); } }