Merge pull request #168 from yuankunzhang/nanoseconds

feat: support nanoseconds
This commit is contained in:
Daniel Hofstetter
2025-06-28 16:45:22 +02:00
committed by GitHub
4 changed files with 34 additions and 11 deletions
+18 -2
View File
@@ -190,6 +190,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
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<DateTime<FixedOffset>> {
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::<FixedOffset>::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<Item>, at: DateTime<FixedOffset>) -> Option<DateTime<
d.hour(),
d.minute(),
d.second(),
d.nanosecond(),
*d.offset(),
)?;
}
@@ -299,6 +303,7 @@ fn at_date_inner(date: Vec<Item>, at: DateTime<FixedOffset>) -> Option<DateTime<
hour,
minute,
second as u32,
(second.fract() * 10f64.powi(9)).round() as u32,
offset,
)?;
}
@@ -320,6 +325,7 @@ fn at_date_inner(date: Vec<Item>, at: DateTime<FixedOffset>) -> Option<DateTime<
hour,
minute,
second as u32,
(second.fract() * 10f64.powi(9)).round() as u32,
offset,
)?;
}
@@ -495,6 +501,16 @@ mod tests {
test_eq_fmt("%Y-%m-%d %H:%M:%S %:z", "Jul 17 06:14:49 2024 GMT"),
);
assert_eq!(
"2024-07-17 06:14:49.567 +00:00",
test_eq_fmt("%Y-%m-%d %H:%M:%S%.f %:z", "Jul 17 06:14:49.567 2024 GMT"),
);
assert_eq!(
"2024-07-17 06:14:49.567 +00:00",
test_eq_fmt("%Y-%m-%d %H:%M:%S%.f %:z", "Jul 17 06:14:49,567 2024 GMT"),
);
assert_eq!(
"2024-07-17 06:14:49 -03:00",
test_eq_fmt("%Y-%m-%d %H:%M:%S %:z", "Jul 17 06:14:49 2024 BRT"),
+6 -2
View File
@@ -120,9 +120,13 @@ pub(super) fn float<'a, E>(input: &mut &'a str) -> winnow::Result<f64, E>
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)
}
+10 -3
View File
@@ -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<u32> {
/// Parse a number of seconds (preceded by whitespace)
fn second(input: &mut &str) -> ModalResult<f64> {
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<Offset> {
-4
View File
@@ -34,12 +34,10 @@ pub fn check_time(input: &str, expected: &str, format: &str, base: Option<DateTi
#[case::full_time_with_spaces("12 : 34 : 56", "12:34:56.000000000")]
#[case::full_time_midnight("00:00:00", "00:00:00.000000000")]
#[case::full_time_almost_midnight("23:59:59", "23:59:59.000000000")]
/* TODO: https://github.com/uutils/parse_datetime/issues/165
#[case::full_time_decimal_seconds("12:34:56.666", "12:34:56.666000000")]
#[case::full_time_decimal_seconds("12:34:56.999999999", "12:34:56.999999999")]
#[case::full_time_decimal_seconds("12:34:56.9999999999", "12:34:56.999999999")]
#[case::full_time_decimal_seconds_after_comma("12:34:56,666", "12:34:56.666000000")]
*/
#[case::without_seconds("12:34", "12:34:00.000000000")]
fn test_time_24h_format(#[case] input: &str, #[case] expected: &str) {
check_time(input, expected, "%H:%M:%S%.9f", None);
@@ -54,10 +52,8 @@ fn test_time_24h_format(#[case] input: &str, #[case] expected: &str) {
#[case::full_time_capital("12:34:56pm", "12:34:56.000000000")]
#[case::full_time_midnight("00:00:00", "00:00:00.000000000")]
#[case::full_time_almost_midnight("23:59:59", "23:59:59.000000000")]
/* TODO: https://github.com/uutils/parse_datetime/issues/165
#[case::full_time_decimal_seconds("12:34:56.666pm", "12:34:56.666000000")]
#[case::full_time_decimal_seconds_after_comma("12:34:56,666pm", "12:34:56.666000000")]
*/
#[case::without_seconds("12:34pm", "12:34:00.000000000")]
fn test_time_12h_format(#[case] input: &str, #[case] expected: &str) {
check_time(input, expected, "%H:%M:%S%.9f", None);