diff --git a/src/items/builder.rs b/src/items/builder.rs index 391baea..dc07d11 100644 --- a/src/items/builder.rs +++ b/src/items/builder.rs @@ -3,7 +3,7 @@ use chrono::{DateTime, Datelike, FixedOffset, NaiveDate, TimeZone, Timelike}; -use super::{date, relative, time, weekday, year}; +use super::{date, relative, time, timezone, weekday, year}; /// The builder is used to construct a DateTime object from various components. /// The parser creates a `DateTimeBuilder` object with the parsed components, @@ -17,7 +17,7 @@ pub(crate) struct DateTimeBuilder { date: Option, time: Option, weekday: Option, - timezone: Option, + timezone: Option, relative: Vec, } @@ -86,7 +86,7 @@ impl DateTimeBuilder { Ok(self) } - pub(super) fn set_timezone(mut self, timezone: time::Offset) -> Result { + pub(super) fn set_timezone(mut self, timezone: timezone::Offset) -> Result { if self.timestamp.is_some() { return Err("timestamp cannot be combined with other date/time items"); } else if self.timezone.is_some() { @@ -338,7 +338,7 @@ fn new_date( /// Restores year, month, day, etc after applying the timezone /// returns None if timezone overflows the date fn with_timezone_restore( - offset: time::Offset, + offset: timezone::Offset, at: DateTime, ) -> Option> { let offset: FixedOffset = chrono::FixedOffset::try_from(offset).ok()?; diff --git a/src/items/mod.rs b/src/items/mod.rs index b405825..b04100e 100644 --- a/src/items/mod.rs +++ b/src/items/mod.rs @@ -64,7 +64,7 @@ pub(crate) enum Item { Time(time::Time), Weekday(weekday::Weekday), Relative(relative::Relative), - TimeZone(time::Offset), + TimeZone(timezone::Offset), Pure(String), } diff --git a/src/items/primitive.rs b/src/items/primitive.rs index cd8bd4d..5c351db 100644 --- a/src/items/primitive.rs +++ b/src/items/primitive.rs @@ -132,6 +132,14 @@ where .parse_next(input) } +/// Parse a colon preceded by whitespace +pub(super) fn colon<'a, E>(input: &mut &'a str) -> winnow::Result<(), E> +where + E: ParserError<&'a str>, +{ + s(':').void().parse_next(input) +} + /// Create a context error with a reason. pub(super) fn ctx_err(reason: &'static str) -> ContextError { let mut err = ContextError::new(); diff --git a/src/items/time.rs b/src/items/time.rs index 24064a0..01e0172 100644 --- a/src/items/time.rs +++ b/src/items/time.rs @@ -3,7 +3,7 @@ // spell-checker:ignore shhmm colonless -//! Parse a time item (without a date) +//! Parse a time item (without a date). //! //! The GNU docs state: //! @@ -37,124 +37,39 @@ //! > //! > Either ‘am’/‘pm’ or a time zone correction may be specified, but not both. -use std::fmt::Display; - -use chrono::FixedOffset; use winnow::{ - combinator::{alt, opt, peek, preceded}, - error::{ContextError, ErrMode}, - seq, - stream::AsChar, - token::take_while, - ModalResult, Parser, + combinator::{alt, opt, preceded}, + error::ErrMode, + seq, ModalResult, Parser, }; -use crate::ParseDateTimeError; - use super::{ - primitive::{ctx_err, dec_uint, float, s}, - relative, + primitive::{colon, ctx_err, dec_uint, float, s}, + timezone::{timezone_num, Offset}, }; -#[derive(PartialEq, Debug, Clone, Default)] -pub struct Offset { - pub(crate) negative: bool, - pub(crate) hours: u32, - pub(crate) minutes: u32, -} - #[derive(PartialEq, Clone, Debug, Default)] -pub struct Time { +pub(crate) struct Time { pub hour: u32, pub minute: u32, pub second: f64, pub offset: Option, } -impl Offset { - fn merge(self, offset: Offset) -> Offset { - fn combine(a: u32, neg_a: bool, b: u32, neg_b: bool) -> (u32, bool) { - if neg_a == neg_b { - (a + b, neg_a) - } else if a > b { - (a - b, neg_a) - } else { - (b - a, neg_b) - } - } - let (hours_minutes, negative) = combine( - self.hours * 60 + self.minutes, - self.negative, - offset.hours * 60 + offset.minutes, - offset.negative, - ); - let hours = hours_minutes / 60; - let minutes = hours_minutes % 60; - - Offset { - negative, - hours, - minutes, - } - } -} - -impl TryFrom for chrono::FixedOffset { - type Error = ParseDateTimeError; - - fn try_from( - Offset { - negative, - hours, - minutes, - }: Offset, - ) -> Result { - let secs = hours * 3600 + minutes * 60; - - let offset = if negative { - FixedOffset::west_opt( - secs.try_into() - .map_err(|_| ParseDateTimeError::InvalidInput)?, - ) - .ok_or(ParseDateTimeError::InvalidInput)? - } else { - FixedOffset::east_opt( - secs.try_into() - .map_err(|_| ParseDateTimeError::InvalidInput)?, - ) - .ok_or(ParseDateTimeError::InvalidInput)? - }; - - Ok(offset) - } -} - -impl Display for Offset { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!( - fmt, - "{}{:02}:{:02}", - if self.negative { "-" } else { "+" }, - self.hours, - self.minutes - ) - } -} - #[derive(Clone)] enum Meridiem { Am, Pm, } -pub fn parse(input: &mut &str) -> ModalResult