use std::{fmt, str::FromStr}; use allocative::{Allocative, Visitor}; use bincode::{ de::{BorrowDecoder, Decoder}, enc::Encoder, error::{DecodeError, EncodeError}, BorrowDecode, Decode, Encode, }; use chrono::{Datelike, Days, NaiveDate, TimeZone, Utc}; use derive_deref::{Deref, DerefMut}; use serde::{Deserialize, Serialize}; use super::{DateMapChunkId, MapKey}; const NUMBER_OF_UNSAFE_DATES: usize = 2; const MIN_YEAR: i32 = 2009; const APPROX_MAX_YEAR: i32 = 2100; #[derive( Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default, Serialize, Deserialize, )] pub struct Date(NaiveDate); impl Date { pub fn wrap(date: NaiveDate) -> Self { Self(date) } pub fn from_timestamp(timestamp: u32) -> Self { Self( Utc.timestamp_opt(i64::from(timestamp), 0) .unwrap() .date_naive(), ) } pub fn today() -> Self { Self(chrono::offset::Utc::now().date_naive()) } pub fn yesterday() -> Self { Self(Self::today().checked_sub_days(Days::new(1)).unwrap()) } } impl MapKey for Date { fn to_chunk_id(&self) -> DateMapChunkId { DateMapChunkId::new(self) } fn to_first_unsafe(&self) -> Option { let offset = NUMBER_OF_UNSAFE_DATES - 1; self.checked_sub_days(Days::new(offset as u64)) .map(Date::wrap) } fn to_serialized_key(&self) -> Self { *self } fn is_out_of_bounds(&self) -> bool { !(MIN_YEAR..=APPROX_MAX_YEAR).contains(&self.year()) } fn is_first(&self) -> bool { let day = self.day(); if self.year() == 2009 && self.month() == 1 { day == 3 } else { day == 1 } } fn checked_sub(&self, days: usize) -> Option { self.checked_sub_days(Days::new(days as u64)) .map(Self::wrap) } fn min_percentile_key() -> Self { Self::wrap(NaiveDate::from_ymd_opt(2012, 1, 1).unwrap()) } fn iter_up_to(&self, other: &Self) -> impl Iterator { self.iter_days().take_while(|d| d <= other).map(Date::wrap) } fn map_name<'a>() -> &'a str { "date" } } impl fmt::Display for Date { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) } } impl Encode for Date { fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { Encode::encode(&self.to_string(), encoder) } } impl Decode for Date { fn decode(decoder: &mut D) -> core::result::Result { let str: String = Decode::decode(decoder)?; Ok(Self(NaiveDate::from_str(&str).unwrap())) } } impl<'de> BorrowDecode<'de> for Date { fn borrow_decode>(decoder: &mut D) -> Result { let str: String = BorrowDecode::borrow_decode(decoder)?; Ok(Self(NaiveDate::from_str(&str).unwrap())) } } impl Allocative for Date { fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) { visitor.visit_simple_sized::(); } }