//! DateTime types with specific serialization invariants. use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use chrono::{TimeZone, Utc}; use std::{ convert::{TryFrom, TryInto}, fmt, num::TryFromIntError, }; use super::{SerializationError, ZcashDeserialize, ZcashSerialize}; /// A date and time, represented by a 32-bit number of seconds since the UNIX epoch. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct DateTime32 { timestamp: u32, } impl DateTime32 { /// Returns the number of seconds since the UNIX epoch. pub fn timestamp(&self) -> u32 { self.timestamp } /// Returns the equivalent [`chrono::DateTime`]. pub fn to_chrono(self) -> chrono::DateTime { self.into() } /// Returns the current time pub fn now() -> DateTime32 { Utc::now() .try_into() .expect("unexpected out of range chrono::DateTime") } } impl fmt::Debug for DateTime32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DateTime32") .field("timestamp", &self.timestamp) .field("calendar", &chrono::DateTime::::from(*self)) .finish() } } impl From for DateTime32 { fn from(value: u32) -> Self { DateTime32 { timestamp: value } } } impl From<&u32> for DateTime32 { fn from(value: &u32) -> Self { (*value).into() } } impl From for chrono::DateTime { fn from(value: DateTime32) -> Self { // chrono::DateTime is guaranteed to hold 32-bit values Utc.timestamp(value.timestamp.into(), 0) } } impl From<&DateTime32> for chrono::DateTime { fn from(value: &DateTime32) -> Self { (*value).into() } } impl TryFrom> for DateTime32 { type Error = TryFromIntError; /// Convert from a [`chrono::DateTime`] to a [`DateTime32`], discarding any nanoseconds. /// /// Conversion fails if the number of seconds is outside the `u32` range. fn try_from(value: chrono::DateTime) -> Result { Ok(Self { timestamp: value.timestamp().try_into()?, }) } } impl TryFrom<&chrono::DateTime> for DateTime32 { type Error = TryFromIntError; /// Convert from a [`chrono::DateTime`] to a [`DateTime32`], discarding any nanoseconds. /// /// Conversion fails if the number of seconds is outside the `u32` range. fn try_from(value: &chrono::DateTime) -> Result { (*value).try_into() } } impl ZcashSerialize for DateTime32 { fn zcash_serialize(&self, mut writer: W) -> Result<(), std::io::Error> { writer.write_u32::(self.timestamp) } } impl ZcashDeserialize for DateTime32 { fn zcash_deserialize(mut reader: R) -> Result { Ok(DateTime32 { timestamp: reader.read_u32::()?, }) } }