From c0ada32ce1aee446ae11dd11ba93c87085ba08fe Mon Sep 17 00:00:00 2001 From: yuankunzhang Date: Thu, 26 Jun 2025 14:19:41 +0000 Subject: [PATCH] feat: support nanoseconds --- src/items/mod.rs | 20 ++++++++++++++++++-- src/items/primitive.rs | 8 ++++++-- src/items/time.rs | 13 ++++++++++--- tests/time.rs | 4 ---- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/items/mod.rs b/src/items/mod.rs index 6285ba7..2812c4a 100644 --- a/src/items/mod.rs +++ b/src/items/mod.rs @@ -190,6 +190,7 @@ pub fn parse(input: &mut &str) -> ModalResult> { Ok(items) } +#[allow(clippy::too_many_arguments)] fn new_date( year: i32, month: u32, @@ -197,10 +198,11 @@ fn new_date( hour: u32, minute: u32, second: u32, + nano: u32, offset: FixedOffset, ) -> Option> { let newdate = NaiveDate::from_ymd_opt(year, month, day) - .and_then(|naive| naive.and_hms_opt(hour, minute, second))?; + .and_then(|naive| naive.and_hms_nano_opt(hour, minute, second, nano))?; Some(DateTime::::from_local(newdate, offset)) } @@ -220,7 +222,8 @@ fn with_timezone_restore( .with_year(copy.year())? .with_hour(copy.hour())? .with_minute(copy.minute())? - .with_second(copy.second())?; + .with_second(copy.second())? + .with_nanosecond(copy.nanosecond())?; Some(x) } @@ -274,6 +277,7 @@ fn at_date_inner(date: Vec, at: DateTime) -> Option, at: DateTime) -> Option, at: DateTime) -> Option(input: &mut &'a str) -> winnow::Result where E: ParserError<&'a str>, { - (opt(one_of(['+', '-'])), digit1, opt(preceded('.', digit1))) + ( + opt(one_of(['+', '-'])), + digit1, + opt(preceded(one_of(['.', ',']), digit1)), + ) .void() .take() - .verify_map(|s: &str| s.parse().ok()) + .verify_map(|s: &str| s.replace(",", ".").parse().ok()) .parse_next(input) } diff --git a/src/items/time.rs b/src/items/time.rs index da2a3c6..4a9bfc5 100644 --- a/src/items/time.rs +++ b/src/items/time.rs @@ -41,7 +41,7 @@ use std::fmt::Display; use chrono::FixedOffset; use winnow::{ - ascii::{digit1, float}, + ascii::digit1, combinator::{alt, opt, peek, preceded}, error::{ContextError, ErrMode, StrContext, StrContextValue}, seq, @@ -53,7 +53,7 @@ use winnow::{ use crate::ParseDateTimeError; use super::{ - primitive::{dec_uint, s}, + primitive::{dec_uint, float, s}, relative, }; @@ -226,7 +226,14 @@ fn minute(input: &mut &str) -> ModalResult { /// Parse a number of seconds (preceded by whitespace) fn second(input: &mut &str) -> ModalResult { - s(float).verify(|x| *x < 60.0).parse_next(input) + s(float) + .verify(|x| *x < 60.0) + .map(|x| { + // Truncates the fractional part of seconds to 9 digits. + let factor = 10f64.powi(9); + (x * factor).trunc() / factor + }) + .parse_next(input) } pub(crate) fn timezone(input: &mut &str) -> ModalResult { diff --git a/tests/time.rs b/tests/time.rs index e07370d..d0a4765 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -34,12 +34,10 @@ pub fn check_time(input: &str, expected: &str, format: &str, base: Option