1use std::fmt;
2
3use js_int::{uint, UInt};
4use serde::{Deserialize, Serialize};
5use time::OffsetDateTime;
6use web_time::{Duration, SystemTime, UNIX_EPOCH};
7
8#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
10#[allow(clippy::exhaustive_structs)]
11#[serde(transparent)]
12pub struct MilliSecondsSinceUnixEpoch(pub UInt);
13
14impl MilliSecondsSinceUnixEpoch {
15 pub fn from_system_time(time: SystemTime) -> Option<Self> {
18 let duration = time.duration_since(UNIX_EPOCH).ok()?;
19 let millis = duration.as_millis().try_into().ok()?;
20 Some(Self(millis))
21 }
22
23 pub fn now() -> Self {
25 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown", feature = "js")))]
26 return Self::from_system_time(SystemTime::now()).expect("date out of range");
27
28 #[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "js"))]
29 return Self(f64_to_uint(js_sys::Date::now()));
30 }
31
32 pub fn to_system_time(self) -> Option<SystemTime> {
34 UNIX_EPOCH.checked_add(Duration::from_millis(self.0.into()))
35 }
36
37 pub fn get(&self) -> UInt {
39 self.0
40 }
41
42 pub fn as_secs(&self) -> UInt {
44 self.0 / uint!(1000)
45 }
46}
47
48impl fmt::Debug for MilliSecondsSinceUnixEpoch {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match OffsetDateTime::from_unix_timestamp(i64::from(self.0) / 1000) {
51 Ok(date) => {
52 let date = date + Duration::from_millis(u64::from(self.0) % 1000);
53
54 let (year, month, day) = date.to_calendar_date();
55 let month = month as u8;
56 let (hours, minutes, seconds, milliseconds) = date.to_hms_milli();
57
58 write!(
59 f,
60 "{year}-{month:02}-{day:02}T\
61 {hours:02}:{minutes:02}:{seconds:02}.{milliseconds:03}"
62 )
63 }
64 Err(_) => {
66 write!(f, "MilliSecondsSinceUnixEpoch({})", self.0)
70 }
71 }
72 }
73}
74
75#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
77#[allow(clippy::exhaustive_structs)]
78#[serde(transparent)]
79pub struct SecondsSinceUnixEpoch(pub UInt);
80
81impl SecondsSinceUnixEpoch {
82 pub fn from_system_time(time: SystemTime) -> Option<Self> {
85 let duration = time.duration_since(UNIX_EPOCH).ok()?;
86 let millis = duration.as_secs().try_into().ok()?;
87 Some(Self(millis))
88 }
89
90 pub fn now() -> Self {
92 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown", feature = "js")))]
93 return Self::from_system_time(SystemTime::now()).expect("date out of range");
94
95 #[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "js"))]
96 return Self(f64_to_uint(js_sys::Date::now() / 1000.0));
97 }
98
99 pub fn to_system_time(self) -> Option<SystemTime> {
101 UNIX_EPOCH.checked_add(Duration::from_secs(self.0.into()))
102 }
103
104 pub fn get(&self) -> UInt {
106 self.0
107 }
108}
109
110impl fmt::Debug for SecondsSinceUnixEpoch {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 match OffsetDateTime::from_unix_timestamp(i64::from(self.0)) {
113 Ok(date) => {
114 let (year, month, day) = date.to_calendar_date();
115 let month = month as u8;
116 let (hours, minutes, seconds) = date.to_hms();
117
118 write!(
119 f,
120 "{year}-{month:02}-{day:02}T\
121 {hours:02}:{minutes:02}:{seconds:02}"
122 )
123 }
124 Err(_) => {
126 write!(f, "SecondsSinceUnixEpoch({})", self.0)
130 }
131 }
132 }
133}
134
135#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "js"))]
136fn f64_to_uint(val: f64) -> UInt {
137 UInt::try_from(val as u64).expect("date out of range")
140}
141
142#[cfg(test)]
143mod tests {
144 use std::time::{Duration, UNIX_EPOCH};
145
146 use js_int::uint;
147 use serde::{Deserialize, Serialize};
148 use serde_json::json;
149
150 use super::{MilliSecondsSinceUnixEpoch, SecondsSinceUnixEpoch};
151
152 #[derive(Clone, Debug, Deserialize, Serialize)]
153 struct SystemTimeTest {
154 millis: MilliSecondsSinceUnixEpoch,
155 secs: SecondsSinceUnixEpoch,
156 }
157
158 #[test]
159 fn deserialize() {
160 let json = json!({ "millis": 3000, "secs": 60 });
161
162 let time = serde_json::from_value::<SystemTimeTest>(json).unwrap();
163 assert_eq!(time.millis.to_system_time(), Some(UNIX_EPOCH + Duration::from_millis(3000)));
164 assert_eq!(time.secs.to_system_time(), Some(UNIX_EPOCH + Duration::from_secs(60)));
165 }
166
167 #[test]
168 fn serialize() {
169 let request = SystemTimeTest {
170 millis: MilliSecondsSinceUnixEpoch::from_system_time(UNIX_EPOCH + Duration::new(2, 0))
171 .unwrap(),
172 secs: SecondsSinceUnixEpoch(uint!(0)),
173 };
174
175 assert_eq!(serde_json::to_value(request).unwrap(), json!({ "millis": 2000, "secs": 0 }));
176 }
177
178 #[test]
179 fn debug_s() {
180 let seconds = SecondsSinceUnixEpoch(uint!(0));
181 assert_eq!(format!("{seconds:?}"), "1970-01-01T00:00:00");
182 }
183
184 #[test]
185 fn debug_ms() {
186 let seconds = MilliSecondsSinceUnixEpoch(uint!(0));
187 assert_eq!(format!("{seconds:?}"), "1970-01-01T00:00:00.000");
188 }
189}