You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
fix: timestamp cannot be combined with other items
This commit is contained in:
@@ -33,13 +33,11 @@ impl DateTimeBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Timestamp value is exclusive to other date/time components. Caller of
|
||||
/// the builder must ensure that it is not combined with other items.
|
||||
pub(super) fn set_timestamp(mut self, ts: i32) -> Result<Self, &'static str> {
|
||||
if self.timestamp.is_some() {
|
||||
Err("timestamp cannot appear more than once")
|
||||
} else {
|
||||
self.timestamp = Some(ts);
|
||||
Ok(self)
|
||||
}
|
||||
self.timestamp = Some(ts);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub(super) fn set_year(mut self, year: u32) -> Result<Self, &'static str> {
|
||||
|
||||
@@ -5,6 +5,13 @@ use winnow::{combinator::preceded, ModalResult, Parser};
|
||||
|
||||
use super::primitive::{dec_int, s};
|
||||
|
||||
/// Parse a timestamp in the form of `@1234567890`.
|
||||
///
|
||||
/// Grammar:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// timestamp = "@" dec_int ;
|
||||
/// ```
|
||||
pub fn parse(input: &mut &str) -> ModalResult<i32> {
|
||||
s(preceded("@", dec_int)).parse_next(input)
|
||||
}
|
||||
|
||||
+73
-23
@@ -45,7 +45,7 @@ use builder::DateTimeBuilder;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use primitive::space;
|
||||
use winnow::{
|
||||
combinator::{alt, trace},
|
||||
combinator::{alt, eof, terminated, trace},
|
||||
error::{AddContext, ContextError, ErrMode, StrContext, StrContextValue},
|
||||
stream::Stream,
|
||||
ModalResult, Parser,
|
||||
@@ -65,26 +65,6 @@ pub enum Item {
|
||||
TimeZone(time::Offset),
|
||||
}
|
||||
|
||||
/// Parse an item.
|
||||
/// TODO: timestamp values are exclusive with other items. See
|
||||
/// https://github.com/uutils/parse_datetime/issues/156
|
||||
pub fn parse_one(input: &mut &str) -> ModalResult<Item> {
|
||||
trace(
|
||||
"parse_one",
|
||||
alt((
|
||||
combined::parse.map(Item::DateTime),
|
||||
date::parse.map(Item::Date),
|
||||
time::parse.map(Item::Time),
|
||||
relative::parse.map(Item::Relative),
|
||||
weekday::parse.map(Item::Weekday),
|
||||
epoch::parse.map(Item::Timestamp),
|
||||
timezone::parse.map(Item::TimeZone),
|
||||
date::year.map(Item::Year),
|
||||
)),
|
||||
)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn expect_error(input: &mut &str, reason: &'static str) -> ErrMode<ContextError> {
|
||||
ErrMode::Cut(ContextError::new()).add_context(
|
||||
input,
|
||||
@@ -93,11 +73,41 @@ fn expect_error(input: &mut &str, reason: &'static str) -> ErrMode<ContextError>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse(input: &mut &str) -> ModalResult<DateTimeBuilder> {
|
||||
/// Parse an item.
|
||||
///
|
||||
/// Grammar:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// item = combined | date | time | relative | weekday | timezone | year ;
|
||||
/// ```
|
||||
fn parse_item(input: &mut &str) -> ModalResult<Item> {
|
||||
trace(
|
||||
"parse_item",
|
||||
alt((
|
||||
combined::parse.map(Item::DateTime),
|
||||
date::parse.map(Item::Date),
|
||||
time::parse.map(Item::Time),
|
||||
relative::parse.map(Item::Relative),
|
||||
weekday::parse.map(Item::Weekday),
|
||||
timezone::parse.map(Item::TimeZone),
|
||||
date::year.map(Item::Year),
|
||||
)),
|
||||
)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
/// Parse a sequence of items.
|
||||
///
|
||||
/// Grammar:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// items = item, { space, item } ;
|
||||
/// ```
|
||||
fn parse_items(input: &mut &str) -> ModalResult<DateTimeBuilder> {
|
||||
let mut builder = DateTimeBuilder::new();
|
||||
|
||||
loop {
|
||||
match parse_one.parse_next(input) {
|
||||
match parse_item.parse_next(input) {
|
||||
Ok(item) => match item {
|
||||
Item::Timestamp(ts) => {
|
||||
builder = builder
|
||||
@@ -147,6 +157,38 @@ pub fn parse(input: &mut &str) -> ModalResult<DateTimeBuilder> {
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
/// Parse a timestamp.
|
||||
///
|
||||
/// From the GNU docs:
|
||||
///
|
||||
/// (Timestamp) Such a number cannot be combined with any other date item, as it
|
||||
/// specifies a complete timestamp.
|
||||
fn parse_timestamp(input: &mut &str) -> ModalResult<DateTimeBuilder> {
|
||||
trace(
|
||||
"parse_timestamp",
|
||||
terminated(epoch::parse.map(Item::Timestamp), eof),
|
||||
)
|
||||
.verify_map(|ts: Item| {
|
||||
if let Item::Timestamp(ts) = ts {
|
||||
DateTimeBuilder::new().set_timestamp(ts).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
/// Parse a date and time string.
|
||||
///
|
||||
/// Grammar:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// date_time = timestamp | items ;
|
||||
/// ```
|
||||
pub(crate) fn parse(input: &mut &str) -> ModalResult<DateTimeBuilder> {
|
||||
trace("parse", alt((parse_timestamp, parse_items))).parse_next(input)
|
||||
}
|
||||
|
||||
pub(crate) fn at_date(
|
||||
builder: DateTimeBuilder,
|
||||
base: DateTime<FixedOffset>,
|
||||
@@ -287,6 +329,14 @@ mod tests {
|
||||
let result = parse(&mut "2025-05-19 abcdef");
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().to_string().contains("unexpected input"));
|
||||
|
||||
let result = parse(&mut "@1690466034 2025-05-19");
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().to_string().contains("unexpected input"));
|
||||
|
||||
let result = parse(&mut "2025-05-19 @1690466034");
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().to_string().contains("unexpected input"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user