1use std::{convert::TryFrom, fmt::Debug, ops::Deref};
2
3use time::{
4 error::ComponentRange, format_description::well_known::Iso8601, Date as _Date, OffsetDateTime,
5 PrimitiveDateTime, Time,
6};
7
8use crate::Error;
9
10use openlibspot_protocol as protocol;
11use protocol::metadata::Date as DateMessage;
12
13impl From<ComponentRange> for Error {
14 fn from(err: ComponentRange) -> Self {
15 Error::out_of_range(err)
16 }
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
20pub struct Date(pub OffsetDateTime);
21
22impl Deref for Date {
23 type Target = OffsetDateTime;
24 fn deref(&self) -> &Self::Target {
25 &self.0
26 }
27}
28
29impl Date {
30 pub fn as_timestamp_ms(&self) -> i64 {
31 (self.0.unix_timestamp_nanos() / 1_000_000) as i64
32 }
33
34 pub fn from_timestamp_ms(timestamp: i64) -> Result<Self, Error> {
35 let date_time = OffsetDateTime::from_unix_timestamp_nanos(timestamp as i128 * 1_000_000)?;
36 Ok(Self(date_time))
37 }
38
39 pub fn as_utc(&self) -> OffsetDateTime {
40 self.0
41 }
42
43 pub fn from_utc(date_time: PrimitiveDateTime) -> Self {
44 Self(date_time.assume_utc())
45 }
46
47 pub fn now_utc() -> Self {
48 Self(OffsetDateTime::now_utc())
49 }
50
51 pub fn from_iso8601(input: &str) -> Result<Self, Error> {
52 let date_time = OffsetDateTime::parse(input, &Iso8601::DEFAULT)?;
53 Ok(Self(date_time))
54 }
55}
56
57impl TryFrom<&DateMessage> for Date {
58 type Error = crate::Error;
59 fn try_from(msg: &DateMessage) -> Result<Self, Self::Error> {
60 let month = if msg.has_month() {
62 msg.month() as u8
63 } else {
64 1
65 };
66
67 let day = if msg.has_day() { msg.day() as u8 } else { 1 };
70
71 let date = _Date::from_calendar_date(msg.year(), month.try_into()?, day)?;
72 let time = Time::from_hms(msg.hour() as u8, msg.minute() as u8, 0)?;
73 Ok(Self::from_utc(PrimitiveDateTime::new(date, time)))
74 }
75}
76
77impl From<OffsetDateTime> for Date {
78 fn from(datetime: OffsetDateTime) -> Self {
79 Self(datetime)
80 }
81}