use crate::Error;
use chrono::TimeZone;
pub trait Git2TimeChronoExt {
fn to_date_time(&self) -> Result<chrono::DateTime<chrono::FixedOffset>, Error>;
fn to_date_time_in<Tz: chrono::TimeZone>(&self, tz: &Tz)
-> Result<chrono::DateTime<Tz>, Error>;
fn to_local_date_time(&self) -> Result<chrono::DateTime<chrono::Local>, Error>;
fn to_date_time_opt(
&self,
) -> Result<chrono::MappedLocalTime<chrono::DateTime<chrono::FixedOffset>>, Error>;
}
impl Git2TimeChronoExt for git2::Time {
fn to_date_time_opt(
&self,
) -> Result<chrono::MappedLocalTime<chrono::DateTime<chrono::FixedOffset>>, Error> {
let Some(tz) = chrono::FixedOffset::east_opt(self.offset_minutes() * 60) else {
return Err(Error::InvalidTimeZone {
offset_minutes: self.offset_minutes(),
});
};
Ok(tz.timestamp_opt(self.seconds(), 0))
}
fn to_date_time(&self) -> Result<chrono::DateTime<chrono::FixedOffset>, Error> {
match self.to_date_time_opt()? {
chrono::MappedLocalTime::Single(datetime) => Ok(datetime),
chrono::MappedLocalTime::Ambiguous(_, latest) => Ok(latest),
chrono::MappedLocalTime::None => Err(Error::TimeNotMappable { time: *self }),
}
}
fn to_date_time_in<Tz: chrono::TimeZone>(
&self,
tz: &Tz,
) -> Result<chrono::DateTime<Tz>, Error> {
self.to_date_time()
.map(|datetime| datetime.with_timezone(tz))
}
fn to_local_date_time(&self) -> Result<chrono::DateTime<chrono::Local>, Error> {
self.to_date_time_in(&chrono::Local)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_date_time_offset_invalid() {
let time = git2::Time::new(0, 100_000);
let result = time.to_date_time();
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(
err,
Error::InvalidTimeZone {
offset_minutes: 100_000
}
));
}
#[test]
fn to_date_time_not_mappable() {
let time = git2::Time::new(i64::MAX, 0);
let result = time.to_date_time();
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, Error::TimeNotMappable { .. }));
}
#[test]
fn to_date_time_opt_none() {
use chrono::MappedLocalTime;
let time = git2::Time::new(i64::MAX, 0);
let mapped = time.to_date_time_opt().unwrap();
assert!(matches!(mapped, MappedLocalTime::None));
}
}