use anyhow::bail; use std::{ fmt::{self, Display, Write as _}, ops::{Add, Sub}, str::FromStr }; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Date { pub year: u16, pub month: u8, pub day: u8 } impl FromStr for Date { type Err = anyhow::Error; fn from_str(s: &str) -> anyhow::Result { parse_date(s) } } impl Display for Date { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&format_date(*self)) } } /// Parse a date in YYMMDD format. pub fn parse_date(s: &str) -> anyhow::Result { let int: u32 = s.parse()?; let year = int / 10000 + 2000; if year / 10 != 202 { bail!("Expected a date in 202X"); } let month = int / 100 % 100; if month < 1 || month > 12 { bail!("Invalid month: {month}"); } let day = int % 100; if day < 1 || day > 31 { bail!("Invalid day: {day}"); } Ok(Date { year: year as _, month: month as _, day: day as _ }) } /// Format a date in YYMMDD format. pub fn format_date(d: Date) -> String { format!("{:02}{:02}{:02}", d.year % 100, d.month, d.day) } /// Format a date in DD. MMMM YYYY format. pub fn format_date_long(d: Date) -> String { let month = match d.month { 1 => "Januar", 2 => "Februar", 3 => "März", 4 => "April", 5 => "Mai", 6 => "Juni", 7 => "Juli", 8 => "August", 9 => "September", 10 => "Oktober", 11 => "November", 12 => "Dezember", _ => unreachable!() }; format!("{:02}. {month} {:04}", d.day, d.year) } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Time { pub seconds: u32, pub micros: u32 } impl Add for Time { type Output = Self; fn add(self, rhs: Self) -> Self { let mut seconds = self.seconds + rhs.seconds; let mut micros = self.micros + rhs.micros; if micros >= 1_000_000 { seconds += 1; micros -= 1_000_000; } Self { seconds, micros } } } impl Sub for Time { type Output = Self; fn sub(mut self, rhs: Self) -> Self { if rhs.micros > self.micros { self.seconds -= 1; self.micros += 1_000_000; } Self { seconds: self.seconds - rhs.seconds, micros: self.micros - rhs.micros } } } impl FromStr for Time { type Err = anyhow::Error; fn from_str(s: &str) -> anyhow::Result { parse_time(s) } } impl Display for Time { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&format_time(*self)) } } /// Parse a time in hh:mm:ss:sub or hh:mm:ss.micros format. pub fn parse_time(s: &str) -> anyhow::Result