Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,61 @@ impl<Tz: TimeZone> DateTime<Tz> {
}

impl DateTime<Utc> {
/// Makes a new `DateTime<Utc>` from the number of non-leap seconds
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
///
/// This is a convenience wrapper around [`DateTime::from_timestamp`],
/// which is useful in functions like [`Iterator::map`] to avoid a closure.
///
/// This is guaranteed to round-trip with regard to [`timestamp`](DateTime::timestamp).
///
/// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use
/// [`TimeZone::timestamp_opt`] or [`DateTime::with_timezone`]; if you need to create a
/// `DateTime` with more precision, use [`DateTime::from_timestamp_micros`],
/// [`DateTime::from_timestamp_millis`], or [`DateTime::from_timestamp_nanos`].
///
/// # Errors
///
/// Returns `None` on out-of-range number of seconds,
/// otherwise returns `Some(DateTime {...})`.
///
/// # Examples
///
/// Using [`Option::and_then`]:
///
/// ```
/// # use chrono::DateTime;
/// let maybe_timestamp: Option<i64> = Some(1431648000);
/// let maybe_dt = maybe_timestamp.and_then(DateTime::from_timestamp_secs);
///
/// assert!(maybe_dt.is_some());
/// assert_eq!(maybe_dt.unwrap().to_string(), "2015-05-15 00:00:00 UTC");
/// ```
///
/// Using [`Iterator::map`]:
///
/// ```
/// # use chrono::{DateTime, Utc};
/// let v = vec![i64::MIN, 1_000_000_000, 1_234_567_890, i64::MAX];
/// let timestamps: Vec<Option<DateTime<Utc>>> = v
/// .into_iter()
/// .map(DateTime::from_timestamp_secs)
/// .collect();
///
/// assert_eq!(vec![
/// None,
/// Some(DateTime::parse_from_rfc3339("2001-09-09 01:46:40Z").unwrap().to_utc()),
/// Some(DateTime::parse_from_rfc3339("2009-02-13 23:31:30Z").unwrap().to_utc()),
/// None,
/// ], timestamps);
/// ```
///
#[inline]
#[must_use]
pub const fn from_timestamp_secs(secs: i64) -> Option<Self> {
Self::from_timestamp(secs, 0)
}

/// Makes a new `DateTime<Utc>` from the number of non-leap seconds
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
/// and the number of nanoseconds since the last whole non-leap second.
Expand Down
16 changes: 4 additions & 12 deletions src/datetime/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,7 @@ pub mod ts_nanoseconds {
where
E: de::Error,
{
DateTime::from_timestamp(
value.div_euclid(1_000_000_000),
(value.rem_euclid(1_000_000_000)) as u32,
)
.ok_or_else(|| invalid_ts(value))
Ok(DateTime::from_timestamp_nanos(value))
}

/// Deserialize a timestamp in nanoseconds since the epoch
Expand Down Expand Up @@ -524,11 +520,7 @@ pub mod ts_microseconds {
where
E: de::Error,
{
DateTime::from_timestamp(
value.div_euclid(1_000_000),
(value.rem_euclid(1_000_000) * 1000) as u32,
)
.ok_or_else(|| invalid_ts(value))
DateTime::from_timestamp_micros(value).ok_or_else(|| invalid_ts(value))
}

/// Deserialize a timestamp in milliseconds since the epoch
Expand Down Expand Up @@ -1064,7 +1056,7 @@ pub mod ts_seconds {
where
E: de::Error,
{
DateTime::from_timestamp(value, 0).ok_or_else(|| invalid_ts(value))
DateTime::from_timestamp_secs(value).ok_or_else(|| invalid_ts(value))
}

/// Deserialize a timestamp in seconds since the epoch
Expand All @@ -1075,7 +1067,7 @@ pub mod ts_seconds {
if value > i64::MAX as u64 {
Err(invalid_ts(value))
} else {
DateTime::from_timestamp(value as i64, 0).ok_or_else(|| invalid_ts(value))
DateTime::from_timestamp_secs(value as i64).ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down
35 changes: 24 additions & 11 deletions src/datetime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ fn test_datetime_from_timestamp_millis() {
// that of `from_timestamp_opt`.
let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678];
for secs in secs_test.iter().cloned() {
assert_eq!(DateTime::from_timestamp_millis(secs * 1000), DateTime::from_timestamp(secs, 0));
assert_eq!(
DateTime::from_timestamp_millis(secs * 1000),
DateTime::from_timestamp_secs(secs)
);
}
}

Expand Down Expand Up @@ -191,7 +194,7 @@ fn test_datetime_from_timestamp_micros() {
for secs in secs_test.iter().copied() {
assert_eq!(
DateTime::from_timestamp_micros(secs * 1_000_000),
DateTime::from_timestamp(secs, 0)
DateTime::from_timestamp_secs(secs)
);
}
}
Expand Down Expand Up @@ -242,24 +245,34 @@ fn test_datetime_from_timestamp_nanos() {
for secs in secs_test.iter().copied() {
assert_eq!(
Some(DateTime::from_timestamp_nanos(secs * 1_000_000_000)),
DateTime::from_timestamp(secs, 0)
DateTime::from_timestamp_secs(secs)
);
}
}

#[test]
fn test_datetime_from_timestamp_secs() {
let valid = [-2208936075, 0, 119731017, 1234567890, 2034061609];

for timestamp_secs in valid.iter().copied() {
let datetime = DateTime::from_timestamp_secs(timestamp_secs).unwrap();
assert_eq!(timestamp_secs, datetime.timestamp());
assert_eq!(DateTime::from_timestamp(timestamp_secs, 0).unwrap(), datetime);
}
}

#[test]
fn test_datetime_from_timestamp() {
let from_timestamp = |secs| DateTime::from_timestamp(secs, 0);
let ymdhms = |y, m, d, h, n, s| {
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().and_utc()
};
assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59)));
assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0)));
assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1)));
assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40)));
assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7)));
assert_eq!(from_timestamp(i64::MIN), None);
assert_eq!(from_timestamp(i64::MAX), None);
assert_eq!(DateTime::from_timestamp_secs(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59)));
assert_eq!(DateTime::from_timestamp_secs(0), Some(ymdhms(1970, 1, 1, 0, 0, 0)));
assert_eq!(DateTime::from_timestamp_secs(1), Some(ymdhms(1970, 1, 1, 0, 0, 1)));
assert_eq!(DateTime::from_timestamp_secs(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40)));
assert_eq!(DateTime::from_timestamp_secs(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7)));
assert_eq!(DateTime::from_timestamp_secs(i64::MIN), None);
assert_eq!(DateTime::from_timestamp_secs(i64::MAX), None);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/format/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ impl Parsed {

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

// fill year, ordinal, hour, minute and second fields from timestamp.
// if existing fields are consistent, this will allow the full date/time reconstruction.
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@
//! use chrono::{DateTime, Utc};
//!
//! // Construct a datetime from epoch:
//! let dt: DateTime<Utc> = DateTime::from_timestamp(1_500_000_000, 0).unwrap();
//! let dt: DateTime<Utc> = DateTime::from_timestamp_secs(1_500_000_000).unwrap();
//! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000");
//!
//! // Get epoch value from a datetime:
Expand Down
8 changes: 4 additions & 4 deletions src/naive/datetime/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ pub mod ts_seconds {
/// }
///
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
/// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc();
/// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc();
/// assert_eq!(my_s, S { time: expected });
/// # Ok::<(), serde_json::Error>(())
/// ```
Expand All @@ -979,7 +979,7 @@ pub mod ts_seconds {
where
E: de::Error,
{
DateTime::from_timestamp(value, 0)
DateTime::from_timestamp_secs(value)
.map(|dt| dt.naive_utc())
.ok_or_else(|| invalid_ts(value))
}
Expand All @@ -991,7 +991,7 @@ pub mod ts_seconds {
if value > i64::MAX as u64 {
Err(invalid_ts(value))
} else {
DateTime::from_timestamp(value as i64, 0)
DateTime::from_timestamp_secs(value as i64)
.map(|dt| dt.naive_utc())
.ok_or_else(|| invalid_ts(value))
}
Expand Down Expand Up @@ -1080,7 +1080,7 @@ pub mod ts_seconds_option {
/// }
///
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
/// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc();
/// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc();
/// assert_eq!(my_s, S { time: Some(expected) });
/// # Ok::<(), serde_json::Error>(())
/// ```
Expand Down
Loading