use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
use super::{DateTimeParseError, ErroredDateTimeComponent, TimeStamp, TimeStampOffset};
impl TryFrom<TimeStamp> for Date {
type Error = DateTimeParseError;
fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
let TimeStamp {
year, month, day, ..
} = value;
match (year, month, day) {
(year, Some(month), Some(day)) => {
let month = Month::try_from(month).map_err(|_| {
DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Month)
})?;
Ok(
Date::from_calendar_date(year.into(), month, day).map_err(|_| {
DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Date)
})?,
)
}
(_year, Some(_), None) => Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Day,
)),
(_year, None, _) => Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Month,
)),
}
}
}
impl TryFrom<super::Date> for Date {
type Error = DateTimeParseError;
fn try_from(value: super::Date) -> Result<Self, Self::Error> {
let super::Date { year, month, day } = value;
match (year, month, day) {
(year, Some(month), Some(day)) => {
let month = Month::try_from(month).map_err(|_| {
DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Month)
})?;
Ok(
Date::from_calendar_date(year.into(), month, day).map_err(|_| {
DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Date)
})?,
)
}
(_year, Some(_), None) => Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Day,
)),
(_year, None, _) => Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Month,
)),
}
}
}
impl From<Date> for TimeStamp {
fn from(value: Date) -> Self {
let (year, month, day) = value.to_calendar_date();
TimeStamp {
year: year as u16,
month: Some(month as u8),
day: Some(day),
hour: None,
minute: None,
second: None,
microsecond: None,
offset: None,
}
}
}
impl From<Date> for super::Date {
fn from(value: Date) -> Self {
let (year, month, day) = value.to_calendar_date();
super::Date {
year: year as u16,
month: Some(month as u8),
day: Some(day),
}
}
}
impl TryFrom<TimeStamp> for PrimitiveDateTime {
type Error = DateTimeParseError;
fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
let date = Date::try_from(value)?;
if value.hour.is_none() {
return Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Hour,
));
}
if value.minute.is_none() {
return Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Minute,
));
}
if value.second.is_none() {
return Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Second,
));
}
let TimeStamp {
hour,
minute,
second,
microsecond,
..
} = value;
let time = Time::from_hms_micro(
hour.unwrap(),
minute.unwrap(),
second.unwrap(),
microsecond.unwrap_or(0),
)
.map_err(|_| DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Time))?;
Ok(PrimitiveDateTime::new(date, time))
}
}
impl TryFrom<super::Time> for Time {
type Error = DateTimeParseError;
fn try_from(value: super::Time) -> Result<Self, Self::Error> {
if value.minute.is_none() {
return Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Minute,
));
}
if value.second.is_none() {
return Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Second,
));
}
let super::Time {
hour,
minute,
second,
microsecond,
..
} = value;
Time::from_hms_micro(
hour,
minute.unwrap(),
second.unwrap(),
microsecond.unwrap_or(0),
)
.map_err(|_| DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Time))
}
}
impl From<PrimitiveDateTime> for TimeStamp {
fn from(value: PrimitiveDateTime) -> Self {
let date = value.date();
let time = value.time();
TimeStamp {
year: date.year() as u16,
month: Some(date.month().into()),
day: Some(date.day()),
hour: Some(time.hour()),
minute: Some(time.minute()),
second: Some(time.second()),
microsecond: Some(time.microsecond()),
offset: None,
}
}
}
impl From<Time> for super::Time {
fn from(value: Time) -> Self {
super::Time {
hour: value.hour(),
minute: Some(value.minute()),
second: Some(value.second()),
microsecond: Some(value.microsecond()),
offset: None,
}
}
}
impl TryFrom<TimeStamp> for OffsetDateTime {
type Error = DateTimeParseError;
fn try_from(value: TimeStamp) -> Result<Self, Self::Error> {
if value.offset.is_none() {
return Err(DateTimeParseError::MissingComponent(
ErroredDateTimeComponent::Offset,
));
}
let datetime = PrimitiveDateTime::try_from(value)?;
let offset = value.offset.unwrap();
let offset = UtcOffset::from_hms(offset.hours, offset.minutes as i8, 0).map_err(|_| {
DateTimeParseError::InvalidComponentRange(ErroredDateTimeComponent::Offset)
})?;
let date = datetime.date();
let time = datetime.time();
Ok(OffsetDateTime::new_in_offset(date, time, offset))
}
}
impl From<OffsetDateTime> for TimeStamp {
fn from(value: OffsetDateTime) -> Self {
let date = value.date();
let time = value.time();
let offset = value.offset();
TimeStamp {
year: date.year() as u16,
month: Some(date.month().into()),
day: Some(date.day()),
hour: Some(time.hour()),
minute: Some(time.minute()),
second: Some(time.second()),
microsecond: Some(time.microsecond()),
offset: Some(TimeStampOffset {
hours: offset.whole_hours(),
minutes: (offset.whole_minutes() % 60).unsigned_abs() as u8,
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_convert_timestamp_to_date() {
let ts = TimeStamp {
year: 2023,
month: Some(3),
day: Some(12),
hour: Some(19),
minute: Some(59),
second: None,
microsecond: None,
offset: None,
};
let actual = Date::try_from(ts).unwrap();
assert_eq!(
actual,
Date::from_calendar_date(2023, Month::March, 12).unwrap()
);
}
#[test]
fn can_convert_timestamp_to_offsetdateime() {
let ts = TimeStamp {
year: 2023,
month: Some(3),
day: Some(12),
hour: Some(19),
minute: Some(59),
second: Some(5),
microsecond: Some(1234),
offset: Some(TimeStampOffset {
hours: -7,
minutes: 0,
}),
};
let actual = OffsetDateTime::try_from(ts).unwrap();
assert_eq!(actual.year(), 2023);
assert_eq!(actual.month(), Month::March);
assert_eq!(actual.day(), 12);
assert_eq!(actual.hour(), 19);
assert_eq!(actual.minute(), 59);
assert_eq!(actual.second(), 5);
assert_eq!(actual.microsecond(), 1234);
assert_eq!(actual.offset(), UtcOffset::from_hms(-7, 0, 0).unwrap());
}
}