Skip to content

Commit 6aeea3f

Browse files
jasonaowendjc
authored andcommitted
Add from_timestamp_secs method to DateTime
Add a new single-argument function for creating a `DateTime` from a Unix timestamp, to match `from_timestamp_micros`, `from_timestamp_millis`, and `from_timestamp_nanos`. Refactor existing code and examples to use `DateTime::from_timestamp_secs` and `DateTime::from_timestamp_millis` where possible. Issue #1716 Suggestion: add DateTime::from_timestamp_secs function
1 parent 59d5f46 commit 6aeea3f

File tree

6 files changed

+89
-29
lines changed

6 files changed

+89
-29
lines changed

src/datetime/mod.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,61 @@ impl<Tz: TimeZone> DateTime<Tz> {
713713
}
714714

715715
impl DateTime<Utc> {
716+
/// Makes a new `DateTime<Utc>` from the number of non-leap seconds
717+
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
718+
///
719+
/// This is a convenience wrapper around [`DateTime::from_timestamp`],
720+
/// which is useful in functions like [`Iterator::map`] to avoid a closure.
721+
///
722+
/// This is guaranteed to round-trip with regard to [`timestamp`](DateTime::timestamp).
723+
///
724+
/// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use
725+
/// [`TimeZone::timestamp_opt`] or [`DateTime::with_timezone`]; if you need to create a
726+
/// `DateTime` with more precision, use [`DateTime::from_timestamp_micros`],
727+
/// [`DateTime::from_timestamp_millis`], or [`DateTime::from_timestamp_nanos`].
728+
///
729+
/// # Errors
730+
///
731+
/// Returns `None` on out-of-range number of seconds,
732+
/// otherwise returns `Some(DateTime {...})`.
733+
///
734+
/// # Examples
735+
///
736+
/// Using [`Option::and_then`]:
737+
///
738+
/// ```
739+
/// # use chrono::DateTime;
740+
/// let maybe_timestamp: Option<i64> = Some(1431648000);
741+
/// let maybe_dt = maybe_timestamp.and_then(DateTime::from_timestamp_secs);
742+
///
743+
/// assert!(maybe_dt.is_some());
744+
/// assert_eq!(maybe_dt.unwrap().to_string(), "2015-05-15 00:00:00 UTC");
745+
/// ```
746+
///
747+
/// Using [`Iterator::map`]:
748+
///
749+
/// ```
750+
/// # use chrono::{DateTime, Utc};
751+
/// let v = vec![i64::MIN, 1_000_000_000, 1_234_567_890, i64::MAX];
752+
/// let timestamps: Vec<Option<DateTime<Utc>>> = v
753+
/// .into_iter()
754+
/// .map(DateTime::from_timestamp_secs)
755+
/// .collect();
756+
///
757+
/// assert_eq!(vec![
758+
/// None,
759+
/// Some(DateTime::parse_from_rfc3339("2001-09-09 01:46:40Z").unwrap().to_utc()),
760+
/// Some(DateTime::parse_from_rfc3339("2009-02-13 23:31:30Z").unwrap().to_utc()),
761+
/// None,
762+
/// ], timestamps);
763+
/// ```
764+
///
765+
#[inline]
766+
#[must_use]
767+
pub const fn from_timestamp_secs(secs: i64) -> Option<Self> {
768+
Self::from_timestamp(secs, 0)
769+
}
770+
716771
/// Makes a new `DateTime<Utc>` from the number of non-leap seconds
717772
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
718773
/// and the number of nanoseconds since the last whole non-leap second.

src/datetime/serde.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,7 @@ pub mod ts_nanoseconds {
242242
where
243243
E: de::Error,
244244
{
245-
DateTime::from_timestamp(
246-
value.div_euclid(1_000_000_000),
247-
(value.rem_euclid(1_000_000_000)) as u32,
248-
)
249-
.ok_or_else(|| invalid_ts(value))
245+
Ok(DateTime::from_timestamp_nanos(value))
250246
}
251247

252248
/// Deserialize a timestamp in nanoseconds since the epoch
@@ -524,11 +520,7 @@ pub mod ts_microseconds {
524520
where
525521
E: de::Error,
526522
{
527-
DateTime::from_timestamp(
528-
value.div_euclid(1_000_000),
529-
(value.rem_euclid(1_000_000) * 1000) as u32,
530-
)
531-
.ok_or_else(|| invalid_ts(value))
523+
DateTime::from_timestamp_micros(value).ok_or_else(|| invalid_ts(value))
532524
}
533525

534526
/// Deserialize a timestamp in milliseconds since the epoch
@@ -1064,7 +1056,7 @@ pub mod ts_seconds {
10641056
where
10651057
E: de::Error,
10661058
{
1067-
DateTime::from_timestamp(value, 0).ok_or_else(|| invalid_ts(value))
1059+
DateTime::from_timestamp_secs(value).ok_or_else(|| invalid_ts(value))
10681060
}
10691061

10701062
/// Deserialize a timestamp in seconds since the epoch
@@ -1075,7 +1067,7 @@ pub mod ts_seconds {
10751067
if value > i64::MAX as u64 {
10761068
Err(invalid_ts(value))
10771069
} else {
1078-
DateTime::from_timestamp(value as i64, 0).ok_or_else(|| invalid_ts(value))
1070+
DateTime::from_timestamp_secs(value as i64).ok_or_else(|| invalid_ts(value))
10791071
}
10801072
}
10811073
}

src/datetime/tests.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,10 @@ fn test_datetime_from_timestamp_millis() {
154154
// that of `from_timestamp_opt`.
155155
let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678];
156156
for secs in secs_test.iter().cloned() {
157-
assert_eq!(DateTime::from_timestamp_millis(secs * 1000), DateTime::from_timestamp(secs, 0));
157+
assert_eq!(
158+
DateTime::from_timestamp_millis(secs * 1000),
159+
DateTime::from_timestamp_secs(secs)
160+
);
158161
}
159162
}
160163

@@ -191,7 +194,7 @@ fn test_datetime_from_timestamp_micros() {
191194
for secs in secs_test.iter().copied() {
192195
assert_eq!(
193196
DateTime::from_timestamp_micros(secs * 1_000_000),
194-
DateTime::from_timestamp(secs, 0)
197+
DateTime::from_timestamp_secs(secs)
195198
);
196199
}
197200
}
@@ -242,24 +245,34 @@ fn test_datetime_from_timestamp_nanos() {
242245
for secs in secs_test.iter().copied() {
243246
assert_eq!(
244247
Some(DateTime::from_timestamp_nanos(secs * 1_000_000_000)),
245-
DateTime::from_timestamp(secs, 0)
248+
DateTime::from_timestamp_secs(secs)
246249
);
247250
}
248251
}
249252

253+
#[test]
254+
fn test_datetime_from_timestamp_secs() {
255+
let valid = [-2208936075, 0, 119731017, 1234567890, 2034061609];
256+
257+
for timestamp_secs in valid.iter().copied() {
258+
let datetime = DateTime::from_timestamp_secs(timestamp_secs).unwrap();
259+
assert_eq!(timestamp_secs, datetime.timestamp());
260+
assert_eq!(DateTime::from_timestamp(timestamp_secs, 0).unwrap(), datetime);
261+
}
262+
}
263+
250264
#[test]
251265
fn test_datetime_from_timestamp() {
252-
let from_timestamp = |secs| DateTime::from_timestamp(secs, 0);
253266
let ymdhms = |y, m, d, h, n, s| {
254267
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().and_utc()
255268
};
256-
assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59)));
257-
assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0)));
258-
assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1)));
259-
assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40)));
260-
assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7)));
261-
assert_eq!(from_timestamp(i64::MIN), None);
262-
assert_eq!(from_timestamp(i64::MAX), None);
269+
assert_eq!(DateTime::from_timestamp_secs(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59)));
270+
assert_eq!(DateTime::from_timestamp_secs(0), Some(ymdhms(1970, 1, 1, 0, 0, 0)));
271+
assert_eq!(DateTime::from_timestamp_secs(1), Some(ymdhms(1970, 1, 1, 0, 0, 1)));
272+
assert_eq!(DateTime::from_timestamp_secs(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40)));
273+
assert_eq!(DateTime::from_timestamp_secs(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7)));
274+
assert_eq!(DateTime::from_timestamp_secs(i64::MIN), None);
275+
assert_eq!(DateTime::from_timestamp_secs(i64::MAX), None);
263276
}
264277

265278
#[test]

src/format/parsed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,7 @@ impl Parsed {
832832

833833
// reconstruct date and time fields from timestamp
834834
let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?;
835-
let mut datetime = DateTime::from_timestamp(ts, 0).ok_or(OUT_OF_RANGE)?.naive_utc();
835+
let mut datetime = DateTime::from_timestamp_secs(ts).ok_or(OUT_OF_RANGE)?.naive_utc();
836836

837837
// fill year, ordinal, hour, minute and second fields from timestamp.
838838
// if existing fields are consistent, this will allow the full date/time reconstruction.

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@
380380
//! use chrono::{DateTime, Utc};
381381
//!
382382
//! // Construct a datetime from epoch:
383-
//! let dt: DateTime<Utc> = DateTime::from_timestamp(1_500_000_000, 0).unwrap();
383+
//! let dt: DateTime<Utc> = DateTime::from_timestamp_secs(1_500_000_000).unwrap();
384384
//! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000");
385385
//!
386386
//! // Get epoch value from a datetime:

src/naive/datetime/serde.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ pub mod ts_seconds {
955955
/// }
956956
///
957957
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
958-
/// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc();
958+
/// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc();
959959
/// assert_eq!(my_s, S { time: expected });
960960
/// # Ok::<(), serde_json::Error>(())
961961
/// ```
@@ -979,7 +979,7 @@ pub mod ts_seconds {
979979
where
980980
E: de::Error,
981981
{
982-
DateTime::from_timestamp(value, 0)
982+
DateTime::from_timestamp_secs(value)
983983
.map(|dt| dt.naive_utc())
984984
.ok_or_else(|| invalid_ts(value))
985985
}
@@ -991,7 +991,7 @@ pub mod ts_seconds {
991991
if value > i64::MAX as u64 {
992992
Err(invalid_ts(value))
993993
} else {
994-
DateTime::from_timestamp(value as i64, 0)
994+
DateTime::from_timestamp_secs(value as i64)
995995
.map(|dt| dt.naive_utc())
996996
.ok_or_else(|| invalid_ts(value))
997997
}
@@ -1080,7 +1080,7 @@ pub mod ts_seconds_option {
10801080
/// }
10811081
///
10821082
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
1083-
/// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc();
1083+
/// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc();
10841084
/// assert_eq!(my_s, S { time: Some(expected) });
10851085
/// # Ok::<(), serde_json::Error>(())
10861086
/// ```

0 commit comments

Comments
 (0)