use serde::{Deserialize, Serialize};
use std::fmt;
use filetime::FileTime;
use time::{
format_description::{
self,
well_known::{Rfc2822, Rfc3339},
},
Date, Duration, Month, OffsetDateTime, Time, UtcOffset,
};
use crate::Result;
#[derive(
Debug, Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq,
)]
pub struct Timestamp(
#[serde(with = "time::serde::rfc3339")] pub(crate) OffsetDateTime,
);
impl Default for Timestamp {
fn default() -> Self {
Self(OffsetDateTime::now_utc())
}
}
impl Timestamp {
pub fn from_calendar_date(
year: i32,
month: Month,
day: u8,
) -> Result<Self> {
let date = Date::from_calendar_date(year, month, day)?;
let offset_date_time = OffsetDateTime::now_utc();
let offset_date_time = offset_date_time.replace_date(date);
let offset_date_time = offset_date_time.replace_time(Time::MIDNIGHT);
Ok(Self(offset_date_time))
}
pub fn parse_simple_date(s: &str) -> Result<Self> {
let date_separator =
format_description::parse("[year]-[month]-[day]")?;
let date = Date::parse(s, &date_separator)?;
let offset_date_time = OffsetDateTime::now_utc();
let offset_date_time = offset_date_time.replace_date(date);
let offset_date_time = offset_date_time.replace_time(Time::MIDNIGHT);
Ok(Self(offset_date_time))
}
pub fn format_simple_date(&self) -> Result<String> {
let format = format_description::parse("[year]-[month]-[day]")?;
Ok(self.0.format(&format)?)
}
pub fn parse_rfc3339(value: &str) -> Result<Self> {
Ok(Self(OffsetDateTime::parse(value, &Rfc3339)?))
}
pub fn to_date_time(&self) -> Result<String> {
let format = format_description::parse(
"[day] [month repr:short] [year] [hour]:[minute]:[second]",
)?;
Ok(self.0.format(&format)?)
}
pub fn to_rfc2822(&self) -> Result<String> {
Timestamp::rfc2822(&self.0)
}
fn rfc2822(datetime: &OffsetDateTime) -> Result<String> {
Ok(datetime.format(&Rfc2822)?)
}
pub fn to_rfc3339(&self) -> Result<String> {
Timestamp::rfc3339(&self.0)
}
pub fn rfc3339(datetime: &OffsetDateTime) -> Result<String> {
Ok(datetime.format(&Rfc3339)?)
}
pub fn into_date(self) -> Date {
self.0.date()
}
pub fn into_time(self) -> Time {
self.0.time()
}
}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match UtcOffset::current_local_offset() {
Ok(local_offset) => {
let datetime = self.0;
datetime.to_offset(local_offset);
match Timestamp::rfc2822(&datetime) {
Ok(value) => {
write!(f, "{}", value)
}
Err(_) => {
write!(f, "{}", datetime)
}
}
}
Err(_) => match self.to_rfc2822() {
Ok(value) => {
write!(f, "{}", value)
}
Err(_) => {
write!(f, "{}", self.0)
}
},
}
}
}
impl From<OffsetDateTime> for Timestamp {
fn from(value: OffsetDateTime) -> Self {
Self(value)
}
}
impl From<Timestamp> for OffsetDateTime {
fn from(value: Timestamp) -> Self {
value.0
}
}
impl TryFrom<FileTime> for Timestamp {
type Error = crate::Error;
fn try_from(value: FileTime) -> std::result::Result<Self, Self::Error> {
let time = OffsetDateTime::from_unix_timestamp(value.seconds())?
+ Duration::nanoseconds(value.nanoseconds() as i64);
Ok(time.into())
}
}
#[cfg(test)]
mod test {
use super::Timestamp;
use crate::{decode, encode};
use anyhow::Result;
#[tokio::test]
async fn timestamp_encode() -> Result<()> {
let timestamp: Timestamp = Default::default();
let buffer = encode(×tamp).await?;
let decoded: Timestamp = decode(&buffer).await?;
assert_eq!(timestamp, decoded);
Ok(())
}
}