Skip to content

Commit ec30e0e

Browse files
authored
Merge pull request #218 from yuankunzhang/improve-epoch
refactor: improve timestamp handling and test organization
2 parents 91692a1 + 70055ea commit ec30e0e

File tree

2 files changed

+57
-87
lines changed

2 files changed

+57
-87
lines changed

src/items/builder.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,20 +148,16 @@ impl DateTimeBuilder {
148148
self.set_time(time)
149149
}
150150

151-
fn build_from_timestamp(ts: epoch::Timestamp, tz: jiff::tz::TimeZone) -> Option<Zoned> {
152-
Some(
153-
jiff::Timestamp::new(ts.second, ts.nanosecond as i32)
154-
.ok()?
155-
.to_zoned(tz),
156-
)
157-
}
158-
159151
pub(super) fn build(self) -> Option<Zoned> {
160152
let base = self.base.unwrap_or(Zoned::now());
161153

162154
// If a timestamp is set, we use it to build the `Zoned` object.
163155
if let Some(ts) = self.timestamp {
164-
return Self::build_from_timestamp(ts, base.offset().to_time_zone());
156+
return Some(
157+
jiff::Timestamp::try_from(ts)
158+
.ok()?
159+
.to_zoned(base.offset().to_time_zone()),
160+
);
165161
}
166162

167163
// If any of the following items are set, we truncate the time portion

src/items/epoch.rs

Lines changed: 52 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,26 @@ use super::primitive::{dec_uint, s};
3232
/// - Negative timestamps are represented by a negative `second` value and a
3333
/// positive `nanosecond` value.
3434
#[derive(Debug, PartialEq)]
35-
pub(crate) struct Timestamp {
36-
pub(crate) second: i64,
37-
pub(crate) nanosecond: u32,
35+
pub(super) struct Timestamp {
36+
second: i64,
37+
nanosecond: u32,
38+
}
39+
40+
impl TryFrom<Timestamp> for jiff::Timestamp {
41+
type Error = &'static str;
42+
43+
fn try_from(ts: Timestamp) -> Result<Self, Self::Error> {
44+
jiff::Timestamp::new(
45+
ts.second,
46+
i32::try_from(ts.nanosecond).map_err(|_| "nanosecond value exceeds i32::MAX")?,
47+
)
48+
.map_err(|_| "timestamp value is out of valid range")
49+
}
3850
}
3951

4052
/// Parse a timestamp in the form of `@1234567890` or `@-1234567890.12345` or
4153
/// `@1234567890,12345`.
42-
pub(crate) fn parse(input: &mut &str) -> ModalResult<Timestamp> {
54+
pub(super) fn parse(input: &mut &str) -> ModalResult<Timestamp> {
4355
(s("@"), opt(s(one_of(['-', '+']))), sec_and_nsec)
4456
.verify_map(|(_, sign, (sec, nsec))| {
4557
let sec = i64::try_from(sec).ok()?;
@@ -78,84 +90,46 @@ pub(super) fn sec_and_nsec(input: &mut &str) -> ModalResult<(u64, u32)> {
7890
mod tests {
7991
use super::*;
8092

81-
#[test]
82-
fn sec_and_nsec_test() {
83-
let mut input = "1234567890";
84-
assert_eq!(sec_and_nsec(&mut input).unwrap(), (1234567890, 0));
85-
86-
let mut input = "1234567890.12345";
87-
assert_eq!(sec_and_nsec(&mut input).unwrap(), (1234567890, 123450000));
88-
89-
let mut input = "1234567890,12345";
90-
assert_eq!(sec_and_nsec(&mut input).unwrap(), (1234567890, 123450000));
93+
fn ts(second: i64, nanosecond: u32) -> Timestamp {
94+
Timestamp { second, nanosecond }
95+
}
9196

92-
let mut input = "1234567890.1234567890123";
93-
assert_eq!(sec_and_nsec(&mut input).unwrap(), (1234567890, 123456789));
97+
#[test]
98+
fn parse_sec_and_nsec() {
99+
for (input, expected) in [
100+
("1234567890", (1234567890, 0)), // only seconds
101+
("1234567890.12345", (1234567890, 123450000)), // seconds and nanoseconds, '.' as floating point
102+
("1234567890,12345", (1234567890, 123450000)), // seconds and nanoseconds, ',' as floating point
103+
("1234567890.1234567890123", (1234567890, 123456789)), // nanoseconds with more than 9 digits, truncated
104+
] {
105+
let mut s = input;
106+
assert_eq!(sec_and_nsec(&mut s).unwrap(), expected, "{input}");
107+
}
108+
109+
for input in [
110+
".1234567890", // invalid: no leading seconds
111+
"-1234567890", // invalid: negative input not allowed
112+
] {
113+
let mut s = input;
114+
assert!(sec_and_nsec(&mut s).is_err(), "{input}");
115+
}
94116
}
95117

96118
#[test]
97119
fn timestamp() {
98-
let mut input = "@1234567890";
99-
assert_eq!(
100-
parse(&mut input).unwrap(),
101-
Timestamp {
102-
second: 1234567890,
103-
nanosecond: 0,
104-
}
105-
);
106-
107-
let mut input = "@ 1234567890";
108-
assert_eq!(
109-
parse(&mut input).unwrap(),
110-
Timestamp {
111-
second: 1234567890,
112-
nanosecond: 0,
113-
}
114-
);
115-
116-
let mut input = "@ -1234567890";
117-
assert_eq!(
118-
parse(&mut input).unwrap(),
119-
Timestamp {
120-
second: -1234567890,
121-
nanosecond: 0,
122-
}
123-
);
124-
125-
let mut input = "@ - 1234567890";
126-
assert_eq!(
127-
parse(&mut input).unwrap(),
128-
Timestamp {
129-
second: -1234567890,
130-
nanosecond: 0,
131-
}
132-
);
133-
134-
let mut input = "@1234567890.12345";
135-
assert_eq!(
136-
parse(&mut input).unwrap(),
137-
Timestamp {
138-
second: 1234567890,
139-
nanosecond: 123450000,
140-
}
141-
);
142-
143-
let mut input = "@1234567890,12345";
144-
assert_eq!(
145-
parse(&mut input).unwrap(),
146-
Timestamp {
147-
second: 1234567890,
148-
nanosecond: 123450000,
149-
}
150-
);
151-
152-
let mut input = "@-1234567890.12345";
153-
assert_eq!(
154-
parse(&mut input).unwrap(),
155-
Timestamp {
156-
second: -1234567891,
157-
nanosecond: 876550000,
158-
}
159-
);
120+
for (input, expected) in [
121+
("@1234567890", ts(1234567890, 0)), // positive seconds, no nanoseconds
122+
("@ 1234567890", ts(1234567890, 0)), // space after '@', positive seconds, no nanoseconds
123+
("@-1234567890", ts(-1234567890, 0)), // negative seconds, no nanoseconds
124+
("@ -1234567890", ts(-1234567890, 0)), // space after '@', negative seconds, no nanoseconds
125+
("@ - 1234567890", ts(-1234567890, 0)), // space after '@' and after '-', negative seconds, no nanoseconds
126+
("@1234567890.12345", ts(1234567890, 123450000)), // positive seconds with nanoseconds, '.' as floating point
127+
("@1234567890,12345", ts(1234567890, 123450000)), // positive seconds with nanoseconds, ',' as floating point
128+
("@-1234567890.12345", ts(-1234567891, 876550000)), // negative seconds with nanoseconds, '.' as floating point
129+
("@1234567890.1234567890123", ts(1234567890, 123456789)), // nanoseconds with more than 9 digits, truncated
130+
] {
131+
let mut s = input;
132+
assert_eq!(parse(&mut s).unwrap(), expected, "{input}");
133+
}
160134
}
161135
}

0 commit comments

Comments
 (0)