sos_core/
date_time.rs

1//! UTC date and time that can be encoded to and from binary.
2//!
3//! Encoded as an i64 of the seconds since the UNIX epoch and
4//! a u32 nanosecond offset from the second so the total size
5//! when encoded is 12 bytes.
6
7use crate::Result;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10use time::{
11    format_description::{
12        self,
13        well_known::{Rfc2822, Rfc3339},
14    },
15    Date, Month, OffsetDateTime, Time, UtcOffset,
16};
17use time_tz::{OffsetDateTimeExt, TimeZone};
18
19/// Date and time with binary encoding support.
20#[derive(
21    Debug, Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq,
22)]
23pub struct UtcDateTime(
24    #[serde(with = "time::serde::rfc3339")] pub(crate) OffsetDateTime,
25);
26
27impl Default for UtcDateTime {
28    fn default() -> Self {
29        Self(OffsetDateTime::now_utc())
30    }
31}
32
33impl UtcDateTime {
34    /// Create a UTC date time for now.
35    pub fn now() -> Self {
36        Default::default()
37    }
38
39    /// Convert this date time to the given timezone.
40    pub fn to_timezone<T: TimeZone>(&self, tz: &T) -> Self {
41        Self(self.clone().0.to_timezone(tz))
42    }
43
44    /// Create from a calendar date.
45    pub fn from_calendar_date(
46        year: i32,
47        month: Month,
48        day: u8,
49    ) -> Result<Self> {
50        let date = Date::from_calendar_date(year, month, day)?;
51        let offset_date_time = OffsetDateTime::now_utc();
52        let offset_date_time = offset_date_time.replace_date(date);
53        let offset_date_time = offset_date_time.replace_time(Time::MIDNIGHT);
54        Ok(Self(offset_date_time))
55    }
56
57    /// Parse from a simple date format YYYY-MM-DD.
58    pub fn parse_simple_date(s: &str) -> Result<Self> {
59        let date_separator =
60            format_description::parse("[year]-[month]-[day]")?;
61        let date = Date::parse(s, &date_separator)?;
62        let offset_date_time = OffsetDateTime::now_utc();
63        let offset_date_time = offset_date_time.replace_date(date);
64        let offset_date_time = offset_date_time.replace_time(Time::MIDNIGHT);
65        Ok(Self(offset_date_time))
66    }
67
68    /// Format as a simple date YYYY-MM-DD.
69    pub fn format_simple_date(&self) -> Result<String> {
70        let format = format_description::parse("[year]-[month]-[day]")?;
71        Ok(self.0.format(&format)?)
72    }
73
74    /// Format according to a format description.
75    pub fn format(&self, description: &str) -> Result<String> {
76        let format = format_description::parse(description)?;
77        Ok(self.0.format(&format)?)
78    }
79
80    /// Parse as RFC3339.
81    pub fn parse_rfc3339(value: &str) -> Result<Self> {
82        Ok(Self(OffsetDateTime::parse(value, &Rfc3339)?))
83    }
84
85    /// Convert to a short human-readable date and time without
86    /// the timezone offset.
87    pub fn to_date_time(&self) -> Result<String> {
88        let format = format_description::parse(
89            "[day] [month repr:short] [year] [hour]:[minute]:[second]",
90        )?;
91        Ok(self.0.format(&format)?)
92    }
93
94    /// Convert this timestamp to a RFC2822 formatted string.
95    pub fn to_rfc2822(&self) -> Result<String> {
96        UtcDateTime::rfc2822(&self.0)
97    }
98
99    /// Convert an offset date time to a RFC2822 formatted string.
100    fn rfc2822(datetime: &OffsetDateTime) -> Result<String> {
101        Ok(datetime.format(&Rfc2822)?)
102    }
103
104    /// Convert this date and time to a RFC3339 formatted string.
105    pub fn to_rfc3339(&self) -> Result<String> {
106        UtcDateTime::rfc3339(&self.0)
107    }
108
109    /// Convert an offset date time to a RFC3339 formatted string.
110    pub fn rfc3339(datetime: &OffsetDateTime) -> Result<String> {
111        Ok(datetime.format(&Rfc3339)?)
112    }
113
114    /// Convert to a date component.
115    pub fn into_date(self) -> Date {
116        self.0.date()
117    }
118
119    /// Convert to a time component.
120    pub fn into_time(self) -> Time {
121        self.0.time()
122    }
123}
124
125impl fmt::Display for UtcDateTime {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        match UtcOffset::current_local_offset() {
128            Ok(local_offset) => {
129                let datetime = self.0;
130                datetime.to_offset(local_offset);
131                match UtcDateTime::rfc2822(&datetime) {
132                    Ok(value) => {
133                        write!(f, "{}", value)
134                    }
135                    Err(_) => {
136                        write!(f, "{}", datetime)
137                    }
138                }
139            }
140            Err(_) => match self.to_rfc2822() {
141                Ok(value) => {
142                    write!(f, "{}", value)
143                }
144                Err(_) => {
145                    write!(f, "{}", self.0)
146                }
147            },
148        }
149    }
150}
151
152impl From<OffsetDateTime> for UtcDateTime {
153    fn from(value: OffsetDateTime) -> Self {
154        Self(value)
155    }
156}
157
158impl From<UtcDateTime> for OffsetDateTime {
159    fn from(value: UtcDateTime) -> Self {
160        value.0
161    }
162}