Skip to content

Commit 7281da3

Browse files
committed
fix: support float timestamp values
1 parent 5b52929 commit 7281da3

File tree

4 files changed

+35
-7
lines changed

4 files changed

+35
-7
lines changed

src/items/builder.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use super::{date, relative, time, weekday};
1313
#[derive(Debug, Default)]
1414
pub struct DateTimeBuilder {
1515
base: Option<DateTime<FixedOffset>>,
16-
timestamp: Option<i32>,
16+
timestamp: Option<f64>,
1717
date: Option<date::Date>,
1818
time: Option<time::Time>,
1919
weekday: Option<weekday::Weekday>,
@@ -35,7 +35,7 @@ impl DateTimeBuilder {
3535

3636
/// Timestamp value is exclusive to other date/time components. Caller of
3737
/// the builder must ensure that it is not combined with other items.
38-
pub(super) fn set_timestamp(mut self, ts: i32) -> Result<Self, &'static str> {
38+
pub(super) fn set_timestamp(mut self, ts: f64) -> Result<Self, &'static str> {
3939
self.timestamp = Some(ts);
4040
Ok(self)
4141
}
@@ -117,8 +117,11 @@ impl DateTimeBuilder {
117117
)?;
118118

119119
if let Some(ts) = self.timestamp {
120+
// TODO: How to make the fract -> nanosecond conversion more precise?
121+
// Maybe considering using the
122+
// [rust_decimal](https://crates.io/crates/rust_decimal) crate?
120123
dt = chrono::Utc
121-
.timestamp_opt(ts.into(), 0)
124+
.timestamp_opt(ts as i64, (ts.fract() * 10f64.powi(9)).round() as u32)
122125
.unwrap()
123126
.with_timezone(&dt.timezone());
124127
}

src/items/epoch.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,33 @@
33

44
use winnow::{combinator::preceded, ModalResult, Parser};
55

6-
use super::primitive::{dec_int, s};
6+
use super::primitive::{float, s};
77

88
/// Parse a timestamp in the form of `@1234567890`.
9-
pub fn parse(input: &mut &str) -> ModalResult<i32> {
10-
s(preceded("@", dec_int)).parse_next(input)
9+
pub fn parse(input: &mut &str) -> ModalResult<f64> {
10+
s(preceded("@", float)).parse_next(input)
11+
}
12+
13+
#[cfg(test)]
14+
mod tests {
15+
use super::parse;
16+
17+
fn float_eq(a: f64, b: f64) -> bool {
18+
(a - b).abs() < f64::EPSILON
19+
}
20+
21+
#[test]
22+
fn float() {
23+
let mut input = "@1234567890";
24+
assert!(float_eq(parse(&mut input).unwrap(), 1234567890.0));
25+
26+
let mut input = "@1234567890.12345";
27+
assert!(float_eq(parse(&mut input).unwrap(), 1234567890.12345));
28+
29+
let mut input = "@1234567890,12345";
30+
assert!(float_eq(parse(&mut input).unwrap(), 1234567890.12345));
31+
32+
let mut input = "@-1234567890.12345";
33+
assert_eq!(parse(&mut input).unwrap(), -1234567890.12345);
34+
}
1135
}

src/items/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use crate::ParseDateTimeError;
5555

5656
#[derive(PartialEq, Debug)]
5757
pub(crate) enum Item {
58-
Timestamp(i32),
58+
Timestamp(f64),
5959
Year(u32),
6060
DateTime(combined::DateTime),
6161
Date(date::Date),

src/items/primitive.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ where
8484
///
8585
/// Inputs like [+-]?0[0-9]* (e.g., `+012`) are therefore rejected. We provide a
8686
/// custom implementation to support such zero-prefixed integers.
87+
#[allow(unused)]
8788
pub(super) fn dec_int<'a, E>(input: &mut &'a str) -> winnow::Result<i32, E>
8889
where
8990
E: ParserError<&'a str>,

0 commit comments

Comments
 (0)