Skip to content

Commit ff370ae

Browse files
esheppadjc
authored andcommitted
fix bug and add more test cases
1 parent e7d4402 commit ff370ae

File tree

2 files changed

+52
-14
lines changed

2 files changed

+52
-14
lines changed

src/naive/datetime/mod.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::format::{Fixed, Item, Numeric, Pad};
2222
use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime};
2323
use crate::oldtime::Duration as OldDuration;
2424
use crate::{DateTime, Datelike, LocalResult, Months, TimeZone, Timelike, Weekday};
25+
use core::cmp::Ordering;
2526

2627
#[cfg(feature = "rustc-serialize")]
2728
pub(super) mod rustc_serialize;
@@ -138,31 +139,41 @@ impl NaiveDateTime {
138139
///
139140
/// ```
140141
/// use chrono::NaiveDateTime;
141-
/// let timestamp_millis: i64 = 1662921288; //Sunday, September 11, 2022 6:34:48 PM
142+
/// let timestamp_millis: i64 = 1662921288000; //Sunday, September 11, 2022 6:34:48 PM
142143
/// let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis);
143144
/// assert!(naive_datetime.is_some());
144145
/// assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis());
145146
///
146147
/// // Negative timestamps (before the UNIX epoch) are supported as well.
147-
/// let timestamp_millis: i64 = -2208936075; //Mon Jan 01 1900 14:38:45 GMT+0000
148+
/// let timestamp_millis: i64 = -2208936075000; //Mon Jan 01 1900 14:38:45 GMT+0000
148149
/// let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis);
149150
/// assert!(naive_datetime.is_some());
150151
/// assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis());
151152
/// ```
152153
#[inline]
153154
pub fn from_timestamp_millis(millis: i64) -> Option<NaiveDateTime> {
154-
let mut secs = millis / 1000;
155-
if millis < 0 {
156-
secs = secs.checked_sub(1)?;
155+
let (secs, subsec_millis) = (millis / 1000, millis % 1000);
156+
157+
match subsec_millis.cmp(&0) {
158+
Ordering::Less => {
159+
// in the case where our subsec part is negative, then we are actually in the earlier second
160+
// hence we subtract one from the seconds part, and we then add a whole second worth of nanos
161+
// to our nanos part. Due to the use of u32 datatype, it is more convenient to subtract
162+
// the absolute value of the subsec nanos from a whole second worth of nanos
163+
let nsecs = u32::try_from(subsec_millis.abs()).ok()? * NANOS_IN_MILLISECOND;
164+
NaiveDateTime::from_timestamp_opt(
165+
secs.checked_sub(1)?,
166+
NANOS_IN_SECOND.checked_sub(nsecs)?,
167+
)
168+
}
169+
Ordering::Equal => NaiveDateTime::from_timestamp_opt(secs, 0),
170+
Ordering::Greater => {
171+
// convert the subsec millis into nanosecond scale so they can be supplied
172+
// as the nanoseconds parameter
173+
let nsecs = u32::try_from(subsec_millis).ok()? * NANOS_IN_MILLISECOND;
174+
NaiveDateTime::from_timestamp_opt(secs, nsecs)
175+
}
157176
}
158-
159-
let nsecs = (millis % 1000).abs();
160-
let mut nsecs = u32::try_from(nsecs).ok()? * NANOS_IN_MILLISECOND;
161-
if secs < 0 {
162-
nsecs = NANOS_IN_SECOND.checked_sub(nsecs)?;
163-
}
164-
165-
NaiveDateTime::from_timestamp_opt(secs, nsecs)
166177
}
167178

168179
/// Makes a new `NaiveDateTime` corresponding to a UTC date and time,

src/naive/datetime/tests.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,33 @@ use crate::NaiveDate;
44
use crate::{Datelike, FixedOffset, Utc};
55
use std::i64;
66

7+
#[test]
8+
fn test_datetime_from_timestamp_millis() {
9+
let valid_map = [
10+
(1662921288000, "2022-09-11 18:34:48.000000000"),
11+
(1662921288123, "2022-09-11 18:34:48.123000000"),
12+
(1662921287890, "2022-09-11 18:34:47.890000000"),
13+
(-2208936075000, "1900-01-01 14:38:45.000000000"),
14+
(0, "1970-01-01 00:00:00.000000000"),
15+
(119731017000, "1973-10-17 18:36:57.000000000"),
16+
(1234567890000, "2009-02-13 23:31:30.000000000"),
17+
(2034061609000, "2034-06-16 09:06:49.000000000"),
18+
];
19+
20+
for (timestamp_millis, formatted) in valid_map.iter().cloned() {
21+
let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis);
22+
assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis());
23+
assert_eq!(naive_datetime.unwrap().format("%F %T%.9f").to_string(), formatted);
24+
}
25+
26+
let invalid = [i64::MAX, i64::MIN];
27+
28+
for timestamp_millis in invalid.iter().cloned() {
29+
let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis);
30+
assert!(naive_datetime.is_none());
31+
}
32+
}
33+
734
#[test]
835
fn test_datetime_from_timestamp() {
936
let from_timestamp = |secs| NaiveDateTime::from_timestamp_opt(secs, 0);
@@ -143,7 +170,7 @@ fn test_datetime_from_str() {
143170
assert!(
144171
d == d_,
145172
"`{}` is parsed into `{:?}`, but reparsed result \
146-
`{:?}` does not match",
173+
`{:?}` does not match",
147174
s,
148175
d,
149176
d_

0 commit comments

Comments
 (0)