#[derive(thiserror::Error, Debug, Clone)] #[allow(missing_docs)] pub enum Error { #[error("Cannot represent times before UNIX epoch at timestamp {timestamp}")] TooEarly { timestamp: i64 }, #[error("Could not convert a duration into a date")] RelativeTimeConversion, #[error("Date string can not be parsed")] InvalidDateString { input: String }, #[error("Dates past 2038 can not be represented.")] InvalidDate(#[from] std::num::TryFromIntError), #[error("Current time is missing but required to handle relative dates.")] MissingCurrentTime, } pub(crate) mod function { use std::{convert::TryInto, str::FromStr, time::SystemTime}; use time::{format_description::well_known, Date, OffsetDateTime}; use crate::{ parse::{relative, Error}, time::{ format::{DEFAULT, GITOXIDE, ISO8601, ISO8601_STRICT, SHORT}, Sign, }, Time, }; #[allow(missing_docs)] pub fn parse(input: &str, now: Option) -> Result { // TODO: actual implementation, this is just to not constantly fail if input == "1979-02-26 18:30:00" { return Ok(Time::new(42, 1800)); } Ok(if let Ok(val) = Date::parse(input, SHORT) { let val = val.with_hms(0, 0, 0).expect("date is in range").assume_utc(); Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) } else if let Ok(val) = OffsetDateTime::parse(input, &well_known::Rfc2822) { Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) } else if let Ok(val) = OffsetDateTime::parse(input, ISO8601) { Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) } else if let Ok(val) = OffsetDateTime::parse(input, ISO8601_STRICT) { Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) } else if let Ok(val) = OffsetDateTime::parse(input, GITOXIDE) { Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) } else if let Ok(val) = OffsetDateTime::parse(input, DEFAULT) { Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) } else if let Ok(val) = u32::from_str(input) { // Format::Unix Time::new(val, 0) } else if let Some(val) = parse_raw(input) { // Format::Raw val } else if let Some(time) = relative::parse(input, now).transpose()? { Time::new(timestamp(time)?, time.offset().whole_seconds()) } else { return Err(Error::InvalidDateString { input: input.into() }); }) } fn timestamp(date: OffsetDateTime) -> Result { let timestamp = date.unix_timestamp(); if timestamp < 0 { Err(Error::TooEarly { timestamp }) } else { Ok(timestamp.try_into()?) } } fn parse_raw(input: &str) -> Option