You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
Merge pull request #189 from yuankunzhang/support-float-timestamp
fix: support float timestamp values
This commit is contained in:
+20
-6
@@ -13,7 +13,7 @@ use super::{date, relative, time, weekday};
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DateTimeBuilder {
|
||||
base: Option<DateTime<FixedOffset>>,
|
||||
timestamp: Option<i32>,
|
||||
timestamp: Option<f64>,
|
||||
date: Option<date::Date>,
|
||||
time: Option<time::Time>,
|
||||
weekday: Option<weekday::Weekday>,
|
||||
@@ -35,7 +35,7 @@ impl DateTimeBuilder {
|
||||
|
||||
/// 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> {
|
||||
pub(super) fn set_timestamp(mut self, ts: f64) -> Result<Self, &'static str> {
|
||||
self.timestamp = Some(ts);
|
||||
Ok(self)
|
||||
}
|
||||
@@ -117,10 +117,24 @@ impl DateTimeBuilder {
|
||||
)?;
|
||||
|
||||
if let Some(ts) = self.timestamp {
|
||||
dt = chrono::Utc
|
||||
.timestamp_opt(ts.into(), 0)
|
||||
.unwrap()
|
||||
.with_timezone(&dt.timezone());
|
||||
// TODO: How to make the fract -> nanosecond conversion more precise?
|
||||
// Maybe considering using the
|
||||
// [rust_decimal](https://crates.io/crates/rust_decimal) crate?
|
||||
match chrono::Utc.timestamp_opt(ts as i64, (ts.fract() * 10f64.powi(9)).round() as u32)
|
||||
{
|
||||
chrono::MappedLocalTime::Single(t) => {
|
||||
// If the timestamp is valid, we can use it directly.
|
||||
dt = t.with_timezone(&dt.timezone());
|
||||
}
|
||||
chrono::MappedLocalTime::Ambiguous(earliest, _latest) => {
|
||||
// TODO: When there is a fold in the local time, which value
|
||||
// do we choose? For now, we use the earliest one.
|
||||
dt = earliest.with_timezone(&dt.timezone());
|
||||
}
|
||||
chrono::MappedLocalTime::None => {
|
||||
return None; // Invalid timestamp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(date::Date { year, month, day }) = self.date {
|
||||
|
||||
+27
-3
@@ -3,9 +3,33 @@
|
||||
|
||||
use winnow::{combinator::preceded, ModalResult, Parser};
|
||||
|
||||
use super::primitive::{dec_int, s};
|
||||
use super::primitive::{float, s};
|
||||
|
||||
/// Parse a timestamp in the form of `@1234567890`.
|
||||
pub fn parse(input: &mut &str) -> ModalResult<i32> {
|
||||
s(preceded("@", dec_int)).parse_next(input)
|
||||
pub fn parse(input: &mut &str) -> ModalResult<f64> {
|
||||
s(preceded("@", float)).parse_next(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::parse;
|
||||
|
||||
fn float_eq(a: f64, b: f64) -> bool {
|
||||
(a - b).abs() < f64::EPSILON
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float() {
|
||||
let mut input = "@1234567890";
|
||||
assert!(float_eq(parse(&mut input).unwrap(), 1234567890.0));
|
||||
|
||||
let mut input = "@1234567890.12345";
|
||||
assert!(float_eq(parse(&mut input).unwrap(), 1234567890.12345));
|
||||
|
||||
let mut input = "@1234567890,12345";
|
||||
assert!(float_eq(parse(&mut input).unwrap(), 1234567890.12345));
|
||||
|
||||
let mut input = "@-1234567890.12345";
|
||||
assert_eq!(parse(&mut input).unwrap(), -1234567890.12345);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ use crate::ParseDateTimeError;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) enum Item {
|
||||
Timestamp(i32),
|
||||
Timestamp(f64),
|
||||
Year(u32),
|
||||
DateTime(combined::DateTime),
|
||||
Date(date::Date),
|
||||
|
||||
@@ -84,6 +84,7 @@ where
|
||||
///
|
||||
/// Inputs like [+-]?0[0-9]* (e.g., `+012`) are therefore rejected. We provide a
|
||||
/// custom implementation to support such zero-prefixed integers.
|
||||
#[allow(unused)]
|
||||
pub(super) fn dec_int<'a, E>(input: &mut &'a str) -> winnow::Result<i32, E>
|
||||
where
|
||||
E: ParserError<&'a str>,
|
||||
|
||||
Reference in New Issue
Block a user