use structured_asn1::memory::{MemoryError, Work};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Xsd10Label {
pub year: i64,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub nanos: u32,
}
impl Xsd10Label {
pub fn at_utc<W: Work>(
mut self,
timezone_minutes: i16,
execution: &W,
) -> Result<Option<Self>, MemoryError> {
execution.charge_work(128)?;
if self.year == 0
|| !(1..=12).contains(&self.month)
|| self.day == 0
|| self.day > self.month_days()
|| self.hour > 24
|| self.minute > 59
|| self.second > 60
|| self.nanos >= 1_000_000_000
|| !(-840..=840).contains(&timezone_minutes)
{
return Ok(None);
}
if self.hour == 24 && (self.minute != 0 || self.second != 0 || self.nanos != 0) {
return Ok(None);
}
let seconds =
i32::from(self.hour) * 3600 + i32::from(self.minute) * 60 + i32::from(self.second)
- i32::from(timezone_minutes) * 60;
let carry = seconds.div_euclid(86_400);
let seconds = seconds.rem_euclid(86_400);
self.hour = u8::try_from(seconds / 3600).expect("bounded hour");
self.minute = u8::try_from(seconds / 60 % 60).expect("bounded minute");
self.second = u8::try_from(seconds % 60).expect("bounded second");
if carry < 0 {
if self.day > 1 {
self.day -= 1;
} else {
if self.month > 1 {
self.month -= 1;
} else {
self.month = 12;
let Some(year) = self.year.checked_sub(1) else {
return Ok(None);
};
self.year = if year == 0 { -1 } else { year };
}
self.day = self.month_days();
}
} else if carry > 0 {
if self.day < self.month_days() {
self.day += 1;
} else {
self.day = 1;
if self.month < 12 {
self.month += 1;
} else {
self.month = 1;
let Some(year) = self.year.checked_add(1) else {
return Ok(None);
};
self.year = if year == 0 { 1 } else { year };
}
}
}
Ok(Some(self))
}
fn month_days(self) -> u8 {
let year = u32::try_from(self.year.rem_euclid(400)).expect("year residue");
u8::try_from(super::days_in_month(year, u32::from(self.month))).expect("month length")
}
}