Skip to content

Commit 79a4b62

Browse files
committed
Move interop between datetime types to single mod
1 parent 56187f1 commit 79a4b62

10 files changed

+341
-323
lines changed

time/src/duration.rs

+33
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use core::fmt;
55
use core::iter::Sum;
66
use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
77
use core::time::Duration as StdDuration;
8+
#[cfg(feature = "std")]
9+
use std::time::SystemTime;
810

911
use deranged::RangedI32;
1012
use num_conv::prelude::*;
@@ -17,6 +19,8 @@ use crate::internal_macros::{
1719
#[cfg(feature = "std")]
1820
#[allow(deprecated)]
1921
use crate::Instant;
22+
#[cfg(feature = "std")]
23+
use crate::OffsetDateTime;
2024

2125
/// By explicitly inserting this enum where padding is expected, the compiler is able to better
2226
/// perform niche value optimization.
@@ -1558,3 +1562,32 @@ impl<'a> Sum<&'a Self> for Duration {
15581562
iter.copied().sum()
15591563
}
15601564
}
1565+
1566+
#[cfg(feature = "std")]
1567+
impl Add<Duration> for SystemTime {
1568+
type Output = Self;
1569+
1570+
fn add(self, duration: Duration) -> Self::Output {
1571+
if duration.is_zero() {
1572+
self
1573+
} else if duration.is_positive() {
1574+
self + duration.unsigned_abs()
1575+
} else {
1576+
debug_assert!(duration.is_negative());
1577+
self - duration.unsigned_abs()
1578+
}
1579+
}
1580+
}
1581+
1582+
impl_add_assign!(SystemTime: #[cfg(feature = "std")] Duration);
1583+
1584+
#[cfg(feature = "std")]
1585+
impl Sub<Duration> for SystemTime {
1586+
type Output = Self;
1587+
1588+
fn sub(self, duration: Duration) -> Self::Output {
1589+
(OffsetDateTime::from(self) - duration).into()
1590+
}
1591+
}
1592+
1593+
impl_sub_assign!(SystemTime: #[cfg(feature = "std")] Duration);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use num_conv::prelude::*;
2+
3+
use crate::convert::*;
4+
use crate::OffsetDateTime;
5+
6+
impl From<js_sys::Date> for OffsetDateTime {
7+
/// # Panics
8+
///
9+
/// This may panic if the timestamp can not be represented.
10+
fn from(js_date: js_sys::Date) -> Self {
11+
// get_time() returns milliseconds
12+
let timestamp_nanos = js_date.get_time() as i128
13+
* Nanosecond::per(Millisecond).cast_signed().extend::<i128>();
14+
Self::from_unix_timestamp_nanos(timestamp_nanos)
15+
.expect("invalid timestamp: Timestamp cannot fit in range")
16+
}
17+
}
18+
19+
impl From<OffsetDateTime> for js_sys::Date {
20+
fn from(datetime: OffsetDateTime) -> Self {
21+
// new Date() takes milliseconds
22+
let timestamp = (datetime.unix_timestamp_nanos()
23+
/ Nanosecond::per(Millisecond).cast_signed().extend::<i128>())
24+
as f64;
25+
Self::new(&timestamp.into())
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use num_conv::prelude::*;
2+
3+
use crate::convert::*;
4+
use crate::UtcDateTime;
5+
6+
impl From<js_sys::Date> for UtcDateTime {
7+
/// # Panics
8+
///
9+
/// This may panic if the timestamp can not be represented.
10+
fn from(js_date: js_sys::Date) -> Self {
11+
// get_time() returns milliseconds
12+
let timestamp_nanos = (js_date.get_time() * Nanosecond::per(Millisecond) as f64) as i128;
13+
Self::from_unix_timestamp_nanos(timestamp_nanos)
14+
.expect("invalid timestamp: Timestamp cannot fit in range")
15+
}
16+
}
17+
18+
impl From<UtcDateTime> for js_sys::Date {
19+
fn from(datetime: UtcDateTime) -> Self {
20+
// new Date() takes milliseconds
21+
let timestamp = (datetime.unix_timestamp_nanos()
22+
/ Nanosecond::per(Millisecond).cast_signed().extend::<i128>())
23+
as f64;
24+
Self::new(&timestamp.into())
25+
}
26+
}

time/src/interop/mod.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! Comparison, arithmetic, and conversion between various types in `time` and the standard library.
2+
//!
3+
//! Currently, full interoperability is present between [`OffsetDateTime`](crate::OffsetDateTime),
4+
//! [`UtcDateTime`](crate::UtcDateTime), and [`SystemTime`](std::time::SystemTime). Partial
5+
//! interoperability is present with [`js_sys::Date`]. Note that
6+
//! [`PrimitiveDateTime`](crate::PrimitiveDateTime) is not interoperable with any of these types due
7+
//! to the lack of an associated UTC offset.
8+
9+
// Module names should have the two types sorted in alphabetical order. This avoids any question
10+
// of which type should be the "primary" type in the module name.
11+
12+
#[cfg(all(
13+
target_family = "wasm",
14+
not(any(target_os = "emscripten", target_os = "wasi")),
15+
feature = "wasm-bindgen"
16+
))]
17+
mod js_sys_date_offsetdatetime;
18+
#[cfg(all(
19+
target_family = "wasm",
20+
not(any(target_os = "emscripten", target_os = "wasi")),
21+
feature = "wasm-bindgen"
22+
))]
23+
mod js_sys_date_utcdatetime;
24+
#[cfg(feature = "std")]
25+
mod offsetdatetime_systemtime;
26+
mod offsetdatetime_utcdatetime;
27+
#[cfg(feature = "std")]
28+
mod utcdatetime_systemtime;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use core::cmp::Ordering;
2+
use core::ops::Sub;
3+
use std::time::SystemTime;
4+
5+
use crate::{Duration, OffsetDateTime};
6+
7+
impl Sub<SystemTime> for OffsetDateTime {
8+
type Output = Duration;
9+
10+
/// # Panics
11+
///
12+
/// This may panic if an overflow occurs.
13+
fn sub(self, rhs: SystemTime) -> Self::Output {
14+
self - Self::from(rhs)
15+
}
16+
}
17+
18+
impl Sub<OffsetDateTime> for SystemTime {
19+
type Output = Duration;
20+
21+
/// # Panics
22+
///
23+
/// This may panic if an overflow occurs.
24+
fn sub(self, rhs: OffsetDateTime) -> Self::Output {
25+
OffsetDateTime::from(self) - rhs
26+
}
27+
}
28+
29+
impl PartialEq<SystemTime> for OffsetDateTime {
30+
fn eq(&self, rhs: &SystemTime) -> bool {
31+
self == &Self::from(*rhs)
32+
}
33+
}
34+
35+
impl PartialEq<OffsetDateTime> for SystemTime {
36+
fn eq(&self, rhs: &OffsetDateTime) -> bool {
37+
&OffsetDateTime::from(*self) == rhs
38+
}
39+
}
40+
41+
impl PartialOrd<SystemTime> for OffsetDateTime {
42+
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
43+
self.partial_cmp(&Self::from(*other))
44+
}
45+
}
46+
47+
impl PartialOrd<OffsetDateTime> for SystemTime {
48+
fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> {
49+
OffsetDateTime::from(*self).partial_cmp(other)
50+
}
51+
}
52+
53+
impl From<SystemTime> for OffsetDateTime {
54+
fn from(system_time: SystemTime) -> Self {
55+
match system_time.duration_since(SystemTime::UNIX_EPOCH) {
56+
Ok(duration) => Self::UNIX_EPOCH + duration,
57+
Err(err) => Self::UNIX_EPOCH - err.duration(),
58+
}
59+
}
60+
}
61+
62+
impl From<OffsetDateTime> for SystemTime {
63+
fn from(datetime: OffsetDateTime) -> Self {
64+
let duration = datetime - OffsetDateTime::UNIX_EPOCH;
65+
66+
if duration.is_zero() {
67+
Self::UNIX_EPOCH
68+
} else if duration.is_positive() {
69+
Self::UNIX_EPOCH + duration.unsigned_abs()
70+
} else {
71+
debug_assert!(duration.is_negative());
72+
Self::UNIX_EPOCH - duration.unsigned_abs()
73+
}
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use core::cmp::Ordering;
2+
use core::ops::Sub;
3+
4+
use crate::{Duration, OffsetDateTime, UtcDateTime};
5+
6+
impl Sub<OffsetDateTime> for UtcDateTime {
7+
type Output = Duration;
8+
9+
/// # Panics
10+
///
11+
/// This may panic if an overflow occurs.
12+
fn sub(self, rhs: OffsetDateTime) -> Self::Output {
13+
OffsetDateTime::from(self) - rhs
14+
}
15+
}
16+
17+
impl Sub<UtcDateTime> for OffsetDateTime {
18+
type Output = Duration;
19+
20+
/// # Panics
21+
///
22+
/// This may panic if an overflow occurs.
23+
fn sub(self, rhs: UtcDateTime) -> Self::Output {
24+
self - Self::from(rhs)
25+
}
26+
}
27+
28+
impl PartialEq<OffsetDateTime> for UtcDateTime {
29+
fn eq(&self, other: &OffsetDateTime) -> bool {
30+
OffsetDateTime::from(*self) == *other
31+
}
32+
}
33+
34+
impl PartialEq<UtcDateTime> for OffsetDateTime {
35+
fn eq(&self, other: &UtcDateTime) -> bool {
36+
*self == Self::from(*other)
37+
}
38+
}
39+
40+
impl PartialOrd<OffsetDateTime> for UtcDateTime {
41+
fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> {
42+
OffsetDateTime::from(*self).partial_cmp(other)
43+
}
44+
}
45+
46+
impl PartialOrd<UtcDateTime> for OffsetDateTime {
47+
fn partial_cmp(&self, other: &UtcDateTime) -> Option<Ordering> {
48+
self.partial_cmp(&Self::from(*other))
49+
}
50+
}
51+
52+
impl From<OffsetDateTime> for UtcDateTime {
53+
/// # Panics
54+
///
55+
/// This may panic if an overflow occurs.
56+
fn from(datetime: OffsetDateTime) -> Self {
57+
datetime.to_utc()
58+
}
59+
}
60+
61+
impl From<UtcDateTime> for OffsetDateTime {
62+
/// # Panics
63+
///
64+
/// This may panic if an overflow occurs.
65+
fn from(datetime: UtcDateTime) -> Self {
66+
datetime.as_primitive().assume_utc()
67+
}
68+
}
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use core::cmp::Ordering;
2+
use core::ops::Sub;
3+
use std::time::SystemTime;
4+
5+
use crate::{Duration, UtcDateTime};
6+
7+
impl Sub<SystemTime> for UtcDateTime {
8+
type Output = Duration;
9+
10+
/// # Panics
11+
///
12+
/// This may panic if an overflow occurs.
13+
fn sub(self, rhs: SystemTime) -> Self::Output {
14+
self - Self::from(rhs)
15+
}
16+
}
17+
18+
impl Sub<UtcDateTime> for SystemTime {
19+
type Output = Duration;
20+
21+
/// # Panics
22+
///
23+
/// This may panic if an overflow occurs.
24+
fn sub(self, rhs: UtcDateTime) -> Self::Output {
25+
UtcDateTime::from(self) - rhs
26+
}
27+
}
28+
29+
impl PartialEq<SystemTime> for UtcDateTime {
30+
fn eq(&self, rhs: &SystemTime) -> bool {
31+
self == &Self::from(*rhs)
32+
}
33+
}
34+
35+
impl PartialEq<UtcDateTime> for SystemTime {
36+
fn eq(&self, rhs: &UtcDateTime) -> bool {
37+
&UtcDateTime::from(*self) == rhs
38+
}
39+
}
40+
41+
impl PartialOrd<SystemTime> for UtcDateTime {
42+
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
43+
self.partial_cmp(&Self::from(*other))
44+
}
45+
}
46+
47+
impl PartialOrd<UtcDateTime> for SystemTime {
48+
fn partial_cmp(&self, other: &UtcDateTime) -> Option<Ordering> {
49+
UtcDateTime::from(*self).partial_cmp(other)
50+
}
51+
}
52+
53+
impl From<SystemTime> for UtcDateTime {
54+
fn from(system_time: SystemTime) -> Self {
55+
match system_time.duration_since(SystemTime::UNIX_EPOCH) {
56+
Ok(duration) => Self::UNIX_EPOCH + duration,
57+
Err(err) => Self::UNIX_EPOCH - err.duration(),
58+
}
59+
}
60+
}
61+
62+
impl From<UtcDateTime> for SystemTime {
63+
fn from(datetime: UtcDateTime) -> Self {
64+
let duration = datetime - UtcDateTime::UNIX_EPOCH;
65+
66+
if duration.is_zero() {
67+
Self::UNIX_EPOCH
68+
} else if duration.is_positive() {
69+
Self::UNIX_EPOCH + duration.unsigned_abs()
70+
} else {
71+
debug_assert!(duration.is_negative());
72+
Self::UNIX_EPOCH - duration.unsigned_abs()
73+
}
74+
}
75+
}

time/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ mod hint;
9292
#[cfg(feature = "std")]
9393
mod instant;
9494
mod internal_macros;
95+
mod interop;
9596
#[cfg(feature = "macros")]
9697
pub mod macros;
9798
mod month;

0 commit comments

Comments
 (0)