1 | // This is a part of Chrono. |
2 | // See README.md and LICENSE.txt for details. |
3 | |
4 | //! Functionality for rounding or truncating a `DateTime` by a `TimeDelta`. |
5 | |
6 | use crate::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Timelike}; |
7 | use core::cmp::Ordering; |
8 | use core::fmt; |
9 | use core::ops::{Add, Sub}; |
10 | |
11 | /// Extension trait for subsecond rounding or truncation to a maximum number |
12 | /// of digits. Rounding can be used to decrease the error variance when |
13 | /// serializing/persisting to lower precision. Truncation is the default |
14 | /// behavior in Chrono display formatting. Either can be used to guarantee |
15 | /// equality (e.g. for testing) when round-tripping through a lower precision |
16 | /// format. |
17 | pub trait SubsecRound { |
18 | /// Return a copy rounded to the specified number of subsecond digits. With |
19 | /// 9 or more digits, self is returned unmodified. Halfway values are |
20 | /// rounded up (away from zero). |
21 | /// |
22 | /// # Example |
23 | /// ``` rust |
24 | /// # use chrono::{SubsecRound, Timelike, NaiveDate}; |
25 | /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) |
26 | /// .unwrap() |
27 | /// .and_hms_milli_opt(12, 0, 0, 154) |
28 | /// .unwrap() |
29 | /// .and_utc(); |
30 | /// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000); |
31 | /// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000); |
32 | /// ``` |
33 | fn round_subsecs(self, digits: u16) -> Self; |
34 | |
35 | /// Return a copy truncated to the specified number of subsecond |
36 | /// digits. With 9 or more digits, self is returned unmodified. |
37 | /// |
38 | /// # Example |
39 | /// ``` rust |
40 | /// # use chrono::{SubsecRound, Timelike, NaiveDate}; |
41 | /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) |
42 | /// .unwrap() |
43 | /// .and_hms_milli_opt(12, 0, 0, 154) |
44 | /// .unwrap() |
45 | /// .and_utc(); |
46 | /// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000); |
47 | /// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000); |
48 | /// ``` |
49 | fn trunc_subsecs(self, digits: u16) -> Self; |
50 | } |
51 | |
52 | impl<T> SubsecRound for T |
53 | where |
54 | T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>, |
55 | { |
56 | fn round_subsecs(self, digits: u16) -> T { |
57 | let span = span_for_digits(digits); |
58 | let delta_down = self.nanosecond() % span; |
59 | if delta_down > 0 { |
60 | let delta_up = span - delta_down; |
61 | if delta_up <= delta_down { |
62 | self + TimeDelta::nanoseconds(delta_up.into()) |
63 | } else { |
64 | self - TimeDelta::nanoseconds(delta_down.into()) |
65 | } |
66 | } else { |
67 | self // unchanged |
68 | } |
69 | } |
70 | |
71 | fn trunc_subsecs(self, digits: u16) -> T { |
72 | let span = span_for_digits(digits); |
73 | let delta_down = self.nanosecond() % span; |
74 | if delta_down > 0 { |
75 | self - TimeDelta::nanoseconds(delta_down.into()) |
76 | } else { |
77 | self // unchanged |
78 | } |
79 | } |
80 | } |
81 | |
82 | // Return the maximum span in nanoseconds for the target number of digits. |
83 | const fn span_for_digits(digits: u16) -> u32 { |
84 | // fast lookup form of: 10^(9-min(9,digits)) |
85 | match digits { |
86 | 0 => 1_000_000_000, |
87 | 1 => 100_000_000, |
88 | 2 => 10_000_000, |
89 | 3 => 1_000_000, |
90 | 4 => 100_000, |
91 | 5 => 10_000, |
92 | 6 => 1_000, |
93 | 7 => 100, |
94 | 8 => 10, |
95 | _ => 1, |
96 | } |
97 | } |
98 | |
99 | /// Extension trait for rounding or truncating a DateTime by a TimeDelta. |
100 | /// |
101 | /// # Limitations |
102 | /// Both rounding and truncating are done via [`TimeDelta::num_nanoseconds`] and |
103 | /// [`DateTime::timestamp_nanos_opt`]. This means that they will fail if either the |
104 | /// `TimeDelta` or the `DateTime` are too big to represented as nanoseconds. They |
105 | /// will also fail if the `TimeDelta` is bigger than the timestamp, negative or zero. |
106 | pub trait DurationRound: Sized { |
107 | /// Error that can occur in rounding or truncating |
108 | #[cfg (feature = "std" )] |
109 | type Err: std::error::Error; |
110 | |
111 | /// Error that can occur in rounding or truncating |
112 | #[cfg (not(feature = "std" ))] |
113 | type Err: fmt::Debug + fmt::Display; |
114 | |
115 | /// Return a copy rounded by TimeDelta. |
116 | /// |
117 | /// # Example |
118 | /// ``` rust |
119 | /// # use chrono::{DurationRound, TimeDelta, NaiveDate}; |
120 | /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) |
121 | /// .unwrap() |
122 | /// .and_hms_milli_opt(12, 0, 0, 154) |
123 | /// .unwrap() |
124 | /// .and_utc(); |
125 | /// assert_eq!( |
126 | /// dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
127 | /// "2018-01-11 12:00:00.150 UTC" |
128 | /// ); |
129 | /// assert_eq!( |
130 | /// dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
131 | /// "2018-01-12 00:00:00 UTC" |
132 | /// ); |
133 | /// ``` |
134 | fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err>; |
135 | |
136 | /// Return a copy truncated by TimeDelta. |
137 | /// |
138 | /// # Example |
139 | /// ``` rust |
140 | /// # use chrono::{DurationRound, TimeDelta, NaiveDate}; |
141 | /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) |
142 | /// .unwrap() |
143 | /// .and_hms_milli_opt(12, 0, 0, 154) |
144 | /// .unwrap() |
145 | /// .and_utc(); |
146 | /// assert_eq!( |
147 | /// dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
148 | /// "2018-01-11 12:00:00.150 UTC" |
149 | /// ); |
150 | /// assert_eq!( |
151 | /// dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
152 | /// "2018-01-11 00:00:00 UTC" |
153 | /// ); |
154 | /// ``` |
155 | fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err>; |
156 | |
157 | /// Return a copy rounded **up** by TimeDelta. |
158 | /// |
159 | /// # Example |
160 | /// ``` rust |
161 | /// # use chrono::{DurationRound, TimeDelta, NaiveDate}; |
162 | /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) |
163 | /// .unwrap() |
164 | /// .and_hms_milli_opt(12, 0, 0, 154) |
165 | /// .unwrap() |
166 | /// .and_utc(); |
167 | /// assert_eq!( |
168 | /// dt.duration_round_up(TimeDelta::milliseconds(10)).unwrap().to_string(), |
169 | /// "2018-01-11 12:00:00.160 UTC" |
170 | /// ); |
171 | /// assert_eq!( |
172 | /// dt.duration_round_up(TimeDelta::hours(1)).unwrap().to_string(), |
173 | /// "2018-01-11 13:00:00 UTC" |
174 | /// ); |
175 | /// |
176 | /// assert_eq!( |
177 | /// dt.duration_round_up(TimeDelta::days(1)).unwrap().to_string(), |
178 | /// "2018-01-12 00:00:00 UTC" |
179 | /// ); |
180 | /// ``` |
181 | fn duration_round_up(self, duration: TimeDelta) -> Result<Self, Self::Err>; |
182 | } |
183 | |
184 | impl<Tz: TimeZone> DurationRound for DateTime<Tz> { |
185 | type Err = RoundingError; |
186 | |
187 | fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err> { |
188 | duration_round(self.naive_local(), self, duration) |
189 | } |
190 | |
191 | fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err> { |
192 | duration_trunc(self.naive_local(), self, duration) |
193 | } |
194 | |
195 | fn duration_round_up(self, duration: TimeDelta) -> Result<Self, Self::Err> { |
196 | duration_round_up(self.naive_local(), self, duration) |
197 | } |
198 | } |
199 | |
200 | impl DurationRound for NaiveDateTime { |
201 | type Err = RoundingError; |
202 | |
203 | fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err> { |
204 | duration_round(self, self, duration) |
205 | } |
206 | |
207 | fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err> { |
208 | duration_trunc(self, self, duration) |
209 | } |
210 | |
211 | fn duration_round_up(self, duration: TimeDelta) -> Result<Self, Self::Err> { |
212 | duration_round_up(self, self, duration) |
213 | } |
214 | } |
215 | |
216 | fn duration_round<T>( |
217 | naive: NaiveDateTime, |
218 | original: T, |
219 | duration: TimeDelta, |
220 | ) -> Result<T, RoundingError> |
221 | where |
222 | T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>, |
223 | { |
224 | if let Some(span) = duration.num_nanoseconds() { |
225 | if span <= 0 { |
226 | return Err(RoundingError::DurationExceedsLimit); |
227 | } |
228 | let stamp = |
229 | naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; |
230 | let delta_down = stamp % span; |
231 | if delta_down == 0 { |
232 | Ok(original) |
233 | } else { |
234 | let (delta_up, delta_down) = if delta_down < 0 { |
235 | (delta_down.abs(), span - delta_down.abs()) |
236 | } else { |
237 | (span - delta_down, delta_down) |
238 | }; |
239 | if delta_up <= delta_down { |
240 | Ok(original + TimeDelta::nanoseconds(delta_up)) |
241 | } else { |
242 | Ok(original - TimeDelta::nanoseconds(delta_down)) |
243 | } |
244 | } |
245 | } else { |
246 | Err(RoundingError::DurationExceedsLimit) |
247 | } |
248 | } |
249 | |
250 | fn duration_trunc<T>( |
251 | naive: NaiveDateTime, |
252 | original: T, |
253 | duration: TimeDelta, |
254 | ) -> Result<T, RoundingError> |
255 | where |
256 | T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>, |
257 | { |
258 | if let Some(span: i64) = duration.num_nanoseconds() { |
259 | if span <= 0 { |
260 | return Err(RoundingError::DurationExceedsLimit); |
261 | } |
262 | let stamp: i64 = |
263 | naive.and_utc().timestamp_nanos_opt().ok_or(err:RoundingError::TimestampExceedsLimit)?; |
264 | let delta_down: i64 = stamp % span; |
265 | match delta_down.cmp(&0) { |
266 | Ordering::Equal => Ok(original), |
267 | Ordering::Greater => Ok(original - TimeDelta::nanoseconds(nanos:delta_down)), |
268 | Ordering::Less => Ok(original - TimeDelta::nanoseconds(nanos:span - delta_down.abs())), |
269 | } |
270 | } else { |
271 | Err(RoundingError::DurationExceedsLimit) |
272 | } |
273 | } |
274 | |
275 | fn duration_round_up<T>( |
276 | naive: NaiveDateTime, |
277 | original: T, |
278 | duration: TimeDelta, |
279 | ) -> Result<T, RoundingError> |
280 | where |
281 | T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>, |
282 | { |
283 | if let Some(span: i64) = duration.num_nanoseconds() { |
284 | if span <= 0 { |
285 | return Err(RoundingError::DurationExceedsLimit); |
286 | } |
287 | let stamp: i64 = |
288 | naive.and_utc().timestamp_nanos_opt().ok_or(err:RoundingError::TimestampExceedsLimit)?; |
289 | let delta_down: i64 = stamp % span; |
290 | match delta_down.cmp(&0) { |
291 | Ordering::Equal => Ok(original), |
292 | Ordering::Greater => Ok(original + TimeDelta::nanoseconds(nanos:span - delta_down)), |
293 | Ordering::Less => Ok(original + TimeDelta::nanoseconds(nanos:delta_down.abs())), |
294 | } |
295 | } else { |
296 | Err(RoundingError::DurationExceedsLimit) |
297 | } |
298 | } |
299 | |
300 | /// An error from rounding by `TimeDelta` |
301 | /// |
302 | /// See: [`DurationRound`] |
303 | #[derive (Debug, Clone, PartialEq, Eq, Copy)] |
304 | pub enum RoundingError { |
305 | /// Error when the TimeDelta exceeds the TimeDelta from or until the Unix epoch. |
306 | /// |
307 | /// Note: this error is not produced anymore. |
308 | DurationExceedsTimestamp, |
309 | |
310 | /// Error when `TimeDelta.num_nanoseconds` exceeds the limit. |
311 | /// |
312 | /// ``` rust |
313 | /// # use chrono::{DurationRound, TimeDelta, RoundingError, NaiveDate}; |
314 | /// let dt = NaiveDate::from_ymd_opt(2260, 12, 31) |
315 | /// .unwrap() |
316 | /// .and_hms_nano_opt(23, 59, 59, 1_75_500_000) |
317 | /// .unwrap() |
318 | /// .and_utc(); |
319 | /// |
320 | /// assert_eq!( |
321 | /// dt.duration_round(TimeDelta::try_days(300 * 365).unwrap()), |
322 | /// Err(RoundingError::DurationExceedsLimit) |
323 | /// ); |
324 | /// ``` |
325 | DurationExceedsLimit, |
326 | |
327 | /// Error when `DateTime.timestamp_nanos` exceeds the limit. |
328 | /// |
329 | /// ``` rust |
330 | /// # use chrono::{DurationRound, TimeDelta, RoundingError, TimeZone, Utc}; |
331 | /// let dt = Utc.with_ymd_and_hms(2300, 12, 12, 0, 0, 0).unwrap(); |
332 | /// |
333 | /// assert_eq!( |
334 | /// dt.duration_round(TimeDelta::try_days(1).unwrap()), |
335 | /// Err(RoundingError::TimestampExceedsLimit) |
336 | /// ); |
337 | /// ``` |
338 | TimestampExceedsLimit, |
339 | } |
340 | |
341 | impl fmt::Display for RoundingError { |
342 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
343 | match *self { |
344 | RoundingError::DurationExceedsTimestamp => { |
345 | write!(f, "duration in nanoseconds exceeds timestamp" ) |
346 | } |
347 | RoundingError::DurationExceedsLimit => { |
348 | write!(f, "duration exceeds num_nanoseconds limit" ) |
349 | } |
350 | RoundingError::TimestampExceedsLimit => { |
351 | write!(f, "timestamp exceeds num_nanoseconds limit" ) |
352 | } |
353 | } |
354 | } |
355 | } |
356 | |
357 | #[cfg (feature = "std" )] |
358 | impl std::error::Error for RoundingError { |
359 | #[allow (deprecated)] |
360 | fn description(&self) -> &str { |
361 | "error from rounding or truncating with DurationRound" |
362 | } |
363 | } |
364 | |
365 | #[cfg (test)] |
366 | mod tests { |
367 | use super::{DurationRound, RoundingError, SubsecRound, TimeDelta}; |
368 | use crate::Timelike; |
369 | use crate::offset::{FixedOffset, TimeZone, Utc}; |
370 | use crate::{DateTime, NaiveDate}; |
371 | |
372 | #[test ] |
373 | fn test_round_subsecs() { |
374 | let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); |
375 | let dt = pst |
376 | .from_local_datetime( |
377 | &NaiveDate::from_ymd_opt(2018, 1, 11) |
378 | .unwrap() |
379 | .and_hms_nano_opt(10, 5, 13, 84_660_684) |
380 | .unwrap(), |
381 | ) |
382 | .unwrap(); |
383 | |
384 | assert_eq!(dt.round_subsecs(10), dt); |
385 | assert_eq!(dt.round_subsecs(9), dt); |
386 | assert_eq!(dt.round_subsecs(8).nanosecond(), 84_660_680); |
387 | assert_eq!(dt.round_subsecs(7).nanosecond(), 84_660_700); |
388 | assert_eq!(dt.round_subsecs(6).nanosecond(), 84_661_000); |
389 | assert_eq!(dt.round_subsecs(5).nanosecond(), 84_660_000); |
390 | assert_eq!(dt.round_subsecs(4).nanosecond(), 84_700_000); |
391 | assert_eq!(dt.round_subsecs(3).nanosecond(), 85_000_000); |
392 | assert_eq!(dt.round_subsecs(2).nanosecond(), 80_000_000); |
393 | assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000); |
394 | |
395 | assert_eq!(dt.round_subsecs(0).nanosecond(), 0); |
396 | assert_eq!(dt.round_subsecs(0).second(), 13); |
397 | |
398 | let dt = Utc |
399 | .from_local_datetime( |
400 | &NaiveDate::from_ymd_opt(2018, 1, 11) |
401 | .unwrap() |
402 | .and_hms_nano_opt(10, 5, 27, 750_500_000) |
403 | .unwrap(), |
404 | ) |
405 | .unwrap(); |
406 | assert_eq!(dt.round_subsecs(9), dt); |
407 | assert_eq!(dt.round_subsecs(4), dt); |
408 | assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000); |
409 | assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000); |
410 | assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000); |
411 | |
412 | assert_eq!(dt.round_subsecs(0).nanosecond(), 0); |
413 | assert_eq!(dt.round_subsecs(0).second(), 28); |
414 | } |
415 | |
416 | #[test ] |
417 | fn test_round_leap_nanos() { |
418 | let dt = Utc |
419 | .from_local_datetime( |
420 | &NaiveDate::from_ymd_opt(2016, 12, 31) |
421 | .unwrap() |
422 | .and_hms_nano_opt(23, 59, 59, 1_750_500_000) |
423 | .unwrap(), |
424 | ) |
425 | .unwrap(); |
426 | assert_eq!(dt.round_subsecs(9), dt); |
427 | assert_eq!(dt.round_subsecs(4), dt); |
428 | assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000); |
429 | assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000); |
430 | assert_eq!(dt.round_subsecs(1).second(), 59); |
431 | |
432 | assert_eq!(dt.round_subsecs(0).nanosecond(), 0); |
433 | assert_eq!(dt.round_subsecs(0).second(), 0); |
434 | } |
435 | |
436 | #[test ] |
437 | fn test_trunc_subsecs() { |
438 | let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); |
439 | let dt = pst |
440 | .from_local_datetime( |
441 | &NaiveDate::from_ymd_opt(2018, 1, 11) |
442 | .unwrap() |
443 | .and_hms_nano_opt(10, 5, 13, 84_660_684) |
444 | .unwrap(), |
445 | ) |
446 | .unwrap(); |
447 | |
448 | assert_eq!(dt.trunc_subsecs(10), dt); |
449 | assert_eq!(dt.trunc_subsecs(9), dt); |
450 | assert_eq!(dt.trunc_subsecs(8).nanosecond(), 84_660_680); |
451 | assert_eq!(dt.trunc_subsecs(7).nanosecond(), 84_660_600); |
452 | assert_eq!(dt.trunc_subsecs(6).nanosecond(), 84_660_000); |
453 | assert_eq!(dt.trunc_subsecs(5).nanosecond(), 84_660_000); |
454 | assert_eq!(dt.trunc_subsecs(4).nanosecond(), 84_600_000); |
455 | assert_eq!(dt.trunc_subsecs(3).nanosecond(), 84_000_000); |
456 | assert_eq!(dt.trunc_subsecs(2).nanosecond(), 80_000_000); |
457 | assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0); |
458 | |
459 | assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); |
460 | assert_eq!(dt.trunc_subsecs(0).second(), 13); |
461 | |
462 | let dt = pst |
463 | .from_local_datetime( |
464 | &NaiveDate::from_ymd_opt(2018, 1, 11) |
465 | .unwrap() |
466 | .and_hms_nano_opt(10, 5, 27, 750_500_000) |
467 | .unwrap(), |
468 | ) |
469 | .unwrap(); |
470 | assert_eq!(dt.trunc_subsecs(9), dt); |
471 | assert_eq!(dt.trunc_subsecs(4), dt); |
472 | assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000); |
473 | assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000); |
474 | assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000); |
475 | |
476 | assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); |
477 | assert_eq!(dt.trunc_subsecs(0).second(), 27); |
478 | } |
479 | |
480 | #[test ] |
481 | fn test_trunc_leap_nanos() { |
482 | let dt = Utc |
483 | .from_local_datetime( |
484 | &NaiveDate::from_ymd_opt(2016, 12, 31) |
485 | .unwrap() |
486 | .and_hms_nano_opt(23, 59, 59, 1_750_500_000) |
487 | .unwrap(), |
488 | ) |
489 | .unwrap(); |
490 | assert_eq!(dt.trunc_subsecs(9), dt); |
491 | assert_eq!(dt.trunc_subsecs(4), dt); |
492 | assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000); |
493 | assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000); |
494 | assert_eq!(dt.trunc_subsecs(1).second(), 59); |
495 | |
496 | assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000); |
497 | assert_eq!(dt.trunc_subsecs(0).second(), 59); |
498 | } |
499 | |
500 | #[test ] |
501 | fn test_duration_round() { |
502 | let dt = Utc |
503 | .from_local_datetime( |
504 | &NaiveDate::from_ymd_opt(2016, 12, 31) |
505 | .unwrap() |
506 | .and_hms_nano_opt(23, 59, 59, 175_500_000) |
507 | .unwrap(), |
508 | ) |
509 | .unwrap(); |
510 | |
511 | assert_eq!( |
512 | dt.duration_round(TimeDelta::new(-1, 0).unwrap()), |
513 | Err(RoundingError::DurationExceedsLimit) |
514 | ); |
515 | assert_eq!(dt.duration_round(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); |
516 | |
517 | assert_eq!( |
518 | dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
519 | "2016-12-31 23:59:59.180 UTC" |
520 | ); |
521 | |
522 | // round up |
523 | let dt = Utc |
524 | .from_local_datetime( |
525 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
526 | .unwrap() |
527 | .and_hms_milli_opt(18, 22, 30, 0) |
528 | .unwrap(), |
529 | ) |
530 | .unwrap(); |
531 | assert_eq!( |
532 | dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
533 | "2012-12-12 18:25:00 UTC" |
534 | ); |
535 | // round down |
536 | let dt = Utc |
537 | .from_local_datetime( |
538 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
539 | .unwrap() |
540 | .and_hms_milli_opt(18, 22, 29, 999) |
541 | .unwrap(), |
542 | ) |
543 | .unwrap(); |
544 | assert_eq!( |
545 | dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
546 | "2012-12-12 18:20:00 UTC" |
547 | ); |
548 | |
549 | assert_eq!( |
550 | dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
551 | "2012-12-12 18:20:00 UTC" |
552 | ); |
553 | assert_eq!( |
554 | dt.duration_round(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), |
555 | "2012-12-12 18:30:00 UTC" |
556 | ); |
557 | assert_eq!( |
558 | dt.duration_round(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), |
559 | "2012-12-12 18:00:00 UTC" |
560 | ); |
561 | assert_eq!( |
562 | dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
563 | "2012-12-13 00:00:00 UTC" |
564 | ); |
565 | |
566 | // timezone east |
567 | let dt = |
568 | FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); |
569 | assert_eq!( |
570 | dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
571 | "2020-10-28 00:00:00 +01:00" |
572 | ); |
573 | assert_eq!( |
574 | dt.duration_round(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), |
575 | "2020-10-29 00:00:00 +01:00" |
576 | ); |
577 | |
578 | // timezone west |
579 | let dt = |
580 | FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); |
581 | assert_eq!( |
582 | dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
583 | "2020-10-28 00:00:00 -01:00" |
584 | ); |
585 | assert_eq!( |
586 | dt.duration_round(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), |
587 | "2020-10-29 00:00:00 -01:00" |
588 | ); |
589 | } |
590 | |
591 | #[test ] |
592 | fn test_duration_round_naive() { |
593 | let dt = Utc |
594 | .from_local_datetime( |
595 | &NaiveDate::from_ymd_opt(2016, 12, 31) |
596 | .unwrap() |
597 | .and_hms_nano_opt(23, 59, 59, 175_500_000) |
598 | .unwrap(), |
599 | ) |
600 | .unwrap() |
601 | .naive_utc(); |
602 | |
603 | assert_eq!( |
604 | dt.duration_round(TimeDelta::new(-1, 0).unwrap()), |
605 | Err(RoundingError::DurationExceedsLimit) |
606 | ); |
607 | assert_eq!(dt.duration_round(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); |
608 | |
609 | assert_eq!( |
610 | dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
611 | "2016-12-31 23:59:59.180" |
612 | ); |
613 | |
614 | // round up |
615 | let dt = Utc |
616 | .from_local_datetime( |
617 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
618 | .unwrap() |
619 | .and_hms_milli_opt(18, 22, 30, 0) |
620 | .unwrap(), |
621 | ) |
622 | .unwrap() |
623 | .naive_utc(); |
624 | assert_eq!( |
625 | dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
626 | "2012-12-12 18:25:00" |
627 | ); |
628 | // round down |
629 | let dt = Utc |
630 | .from_local_datetime( |
631 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
632 | .unwrap() |
633 | .and_hms_milli_opt(18, 22, 29, 999) |
634 | .unwrap(), |
635 | ) |
636 | .unwrap() |
637 | .naive_utc(); |
638 | assert_eq!( |
639 | dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
640 | "2012-12-12 18:20:00" |
641 | ); |
642 | |
643 | assert_eq!( |
644 | dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
645 | "2012-12-12 18:20:00" |
646 | ); |
647 | assert_eq!( |
648 | dt.duration_round(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), |
649 | "2012-12-12 18:30:00" |
650 | ); |
651 | assert_eq!( |
652 | dt.duration_round(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), |
653 | "2012-12-12 18:00:00" |
654 | ); |
655 | assert_eq!( |
656 | dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
657 | "2012-12-13 00:00:00" |
658 | ); |
659 | } |
660 | |
661 | #[test ] |
662 | fn test_duration_round_pre_epoch() { |
663 | let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); |
664 | assert_eq!( |
665 | dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
666 | "1969-12-12 12:10:00 UTC" |
667 | ); |
668 | } |
669 | |
670 | #[test ] |
671 | fn test_duration_trunc() { |
672 | let dt = Utc |
673 | .from_local_datetime( |
674 | &NaiveDate::from_ymd_opt(2016, 12, 31) |
675 | .unwrap() |
676 | .and_hms_nano_opt(23, 59, 59, 175_500_000) |
677 | .unwrap(), |
678 | ) |
679 | .unwrap(); |
680 | |
681 | assert_eq!( |
682 | dt.duration_trunc(TimeDelta::new(-1, 0).unwrap()), |
683 | Err(RoundingError::DurationExceedsLimit) |
684 | ); |
685 | assert_eq!(dt.duration_trunc(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); |
686 | |
687 | assert_eq!( |
688 | dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
689 | "2016-12-31 23:59:59.170 UTC" |
690 | ); |
691 | |
692 | // would round up |
693 | let dt = Utc |
694 | .from_local_datetime( |
695 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
696 | .unwrap() |
697 | .and_hms_milli_opt(18, 22, 30, 0) |
698 | .unwrap(), |
699 | ) |
700 | .unwrap(); |
701 | assert_eq!( |
702 | dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
703 | "2012-12-12 18:20:00 UTC" |
704 | ); |
705 | // would round down |
706 | let dt = Utc |
707 | .from_local_datetime( |
708 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
709 | .unwrap() |
710 | .and_hms_milli_opt(18, 22, 29, 999) |
711 | .unwrap(), |
712 | ) |
713 | .unwrap(); |
714 | assert_eq!( |
715 | dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
716 | "2012-12-12 18:20:00 UTC" |
717 | ); |
718 | assert_eq!( |
719 | dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
720 | "2012-12-12 18:20:00 UTC" |
721 | ); |
722 | assert_eq!( |
723 | dt.duration_trunc(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), |
724 | "2012-12-12 18:00:00 UTC" |
725 | ); |
726 | assert_eq!( |
727 | dt.duration_trunc(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), |
728 | "2012-12-12 18:00:00 UTC" |
729 | ); |
730 | assert_eq!( |
731 | dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
732 | "2012-12-12 00:00:00 UTC" |
733 | ); |
734 | |
735 | // timezone east |
736 | let dt = |
737 | FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); |
738 | assert_eq!( |
739 | dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
740 | "2020-10-27 00:00:00 +01:00" |
741 | ); |
742 | assert_eq!( |
743 | dt.duration_trunc(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), |
744 | "2020-10-22 00:00:00 +01:00" |
745 | ); |
746 | |
747 | // timezone west |
748 | let dt = |
749 | FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); |
750 | assert_eq!( |
751 | dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
752 | "2020-10-27 00:00:00 -01:00" |
753 | ); |
754 | assert_eq!( |
755 | dt.duration_trunc(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), |
756 | "2020-10-22 00:00:00 -01:00" |
757 | ); |
758 | } |
759 | |
760 | #[test ] |
761 | fn test_duration_trunc_naive() { |
762 | let dt = Utc |
763 | .from_local_datetime( |
764 | &NaiveDate::from_ymd_opt(2016, 12, 31) |
765 | .unwrap() |
766 | .and_hms_nano_opt(23, 59, 59, 175_500_000) |
767 | .unwrap(), |
768 | ) |
769 | .unwrap() |
770 | .naive_utc(); |
771 | |
772 | assert_eq!( |
773 | dt.duration_trunc(TimeDelta::new(-1, 0).unwrap()), |
774 | Err(RoundingError::DurationExceedsLimit) |
775 | ); |
776 | assert_eq!(dt.duration_trunc(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); |
777 | |
778 | assert_eq!( |
779 | dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
780 | "2016-12-31 23:59:59.170" |
781 | ); |
782 | |
783 | // would round up |
784 | let dt = Utc |
785 | .from_local_datetime( |
786 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
787 | .unwrap() |
788 | .and_hms_milli_opt(18, 22, 30, 0) |
789 | .unwrap(), |
790 | ) |
791 | .unwrap() |
792 | .naive_utc(); |
793 | assert_eq!( |
794 | dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
795 | "2012-12-12 18:20:00" |
796 | ); |
797 | // would round down |
798 | let dt = Utc |
799 | .from_local_datetime( |
800 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
801 | .unwrap() |
802 | .and_hms_milli_opt(18, 22, 29, 999) |
803 | .unwrap(), |
804 | ) |
805 | .unwrap() |
806 | .naive_utc(); |
807 | assert_eq!( |
808 | dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
809 | "2012-12-12 18:20:00" |
810 | ); |
811 | assert_eq!( |
812 | dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
813 | "2012-12-12 18:20:00" |
814 | ); |
815 | assert_eq!( |
816 | dt.duration_trunc(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), |
817 | "2012-12-12 18:00:00" |
818 | ); |
819 | assert_eq!( |
820 | dt.duration_trunc(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), |
821 | "2012-12-12 18:00:00" |
822 | ); |
823 | assert_eq!( |
824 | dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
825 | "2012-12-12 00:00:00" |
826 | ); |
827 | } |
828 | |
829 | #[test ] |
830 | fn test_duration_trunc_pre_epoch() { |
831 | let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); |
832 | assert_eq!( |
833 | dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
834 | "1969-12-12 12:10:00 UTC" |
835 | ); |
836 | } |
837 | |
838 | #[test ] |
839 | fn issue1010() { |
840 | let dt = DateTime::from_timestamp(-4_227_854_320, 678_774_288).unwrap(); |
841 | let span = TimeDelta::microseconds(-7_019_067_213_869_040); |
842 | assert_eq!(dt.duration_trunc(span), Err(RoundingError::DurationExceedsLimit)); |
843 | |
844 | let dt = DateTime::from_timestamp(320_041_586, 920_103_021).unwrap(); |
845 | let span = TimeDelta::nanoseconds(-8_923_838_508_697_114_584); |
846 | assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit)); |
847 | |
848 | let dt = DateTime::from_timestamp(-2_621_440, 0).unwrap(); |
849 | let span = TimeDelta::nanoseconds(-9_223_372_036_854_771_421); |
850 | assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit)); |
851 | } |
852 | |
853 | #[test ] |
854 | fn test_duration_trunc_close_to_epoch() { |
855 | let span = TimeDelta::try_minutes(15).unwrap(); |
856 | |
857 | let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 15).unwrap(); |
858 | assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1970-01-01 00:00:00" ); |
859 | |
860 | let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 45).unwrap(); |
861 | assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1969-12-31 23:45:00" ); |
862 | } |
863 | |
864 | #[test ] |
865 | fn test_duration_round_close_to_epoch() { |
866 | let span = TimeDelta::try_minutes(15).unwrap(); |
867 | |
868 | let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 15).unwrap(); |
869 | assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00" ); |
870 | |
871 | let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 45).unwrap(); |
872 | assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00" ); |
873 | } |
874 | |
875 | #[test ] |
876 | fn test_duration_round_close_to_min_max() { |
877 | let span = TimeDelta::nanoseconds(i64::MAX); |
878 | |
879 | let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 - 1); |
880 | assert_eq!( |
881 | dt.duration_round(span).unwrap().to_string(), |
882 | "1677-09-21 00:12:43.145224193 UTC" |
883 | ); |
884 | |
885 | let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 + 1); |
886 | assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC" ); |
887 | |
888 | let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 + 1); |
889 | assert_eq!( |
890 | dt.duration_round(span).unwrap().to_string(), |
891 | "2262-04-11 23:47:16.854775807 UTC" |
892 | ); |
893 | |
894 | let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 - 1); |
895 | assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC" ); |
896 | } |
897 | |
898 | #[test ] |
899 | fn test_duration_round_up() { |
900 | let dt = NaiveDate::from_ymd_opt(2016, 12, 31) |
901 | .unwrap() |
902 | .and_hms_nano_opt(23, 59, 59, 175_500_000) |
903 | .unwrap() |
904 | .and_utc(); |
905 | |
906 | assert_eq!( |
907 | dt.duration_round_up(TimeDelta::new(-1, 0).unwrap()), |
908 | Err(RoundingError::DurationExceedsLimit) |
909 | ); |
910 | |
911 | assert_eq!( |
912 | dt.duration_round_up(TimeDelta::zero()), |
913 | Err(RoundingError::DurationExceedsLimit) |
914 | ); |
915 | |
916 | assert_eq!(dt.duration_round_up(TimeDelta::MAX), Err(RoundingError::DurationExceedsLimit)); |
917 | |
918 | assert_eq!( |
919 | dt.duration_round_up(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
920 | "2016-12-31 23:59:59.180 UTC" |
921 | ); |
922 | |
923 | // round up |
924 | let dt = NaiveDate::from_ymd_opt(2012, 12, 12) |
925 | .unwrap() |
926 | .and_hms_milli_opt(18, 22, 30, 0) |
927 | .unwrap() |
928 | .and_utc(); |
929 | |
930 | assert_eq!( |
931 | dt.duration_round_up(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
932 | "2012-12-12 18:25:00 UTC" |
933 | ); |
934 | |
935 | assert_eq!( |
936 | dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
937 | "2012-12-12 18:30:00 UTC" |
938 | ); |
939 | assert_eq!( |
940 | dt.duration_round_up(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), |
941 | "2012-12-12 18:30:00 UTC" |
942 | ); |
943 | assert_eq!( |
944 | dt.duration_round_up(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), |
945 | "2012-12-12 19:00:00 UTC" |
946 | ); |
947 | assert_eq!( |
948 | dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
949 | "2012-12-13 00:00:00 UTC" |
950 | ); |
951 | |
952 | // timezone east |
953 | let dt = |
954 | FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); |
955 | assert_eq!( |
956 | dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
957 | "2020-10-28 00:00:00 +01:00" |
958 | ); |
959 | assert_eq!( |
960 | dt.duration_round_up(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), |
961 | "2020-10-29 00:00:00 +01:00" |
962 | ); |
963 | |
964 | // timezone west |
965 | let dt = |
966 | FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); |
967 | assert_eq!( |
968 | dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
969 | "2020-10-28 00:00:00 -01:00" |
970 | ); |
971 | assert_eq!( |
972 | dt.duration_round_up(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), |
973 | "2020-10-29 00:00:00 -01:00" |
974 | ); |
975 | } |
976 | |
977 | #[test ] |
978 | fn test_duration_round_up_naive() { |
979 | let dt = NaiveDate::from_ymd_opt(2016, 12, 31) |
980 | .unwrap() |
981 | .and_hms_nano_opt(23, 59, 59, 175_500_000) |
982 | .unwrap(); |
983 | |
984 | assert_eq!( |
985 | dt.duration_round_up(TimeDelta::new(-1, 0).unwrap()), |
986 | Err(RoundingError::DurationExceedsLimit) |
987 | ); |
988 | assert_eq!( |
989 | dt.duration_round_up(TimeDelta::zero()), |
990 | Err(RoundingError::DurationExceedsLimit) |
991 | ); |
992 | |
993 | assert_eq!(dt.duration_round_up(TimeDelta::MAX), Err(RoundingError::DurationExceedsLimit)); |
994 | |
995 | assert_eq!( |
996 | dt.duration_round_up(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), |
997 | "2016-12-31 23:59:59.180" |
998 | ); |
999 | |
1000 | let dt = Utc |
1001 | .from_local_datetime( |
1002 | &NaiveDate::from_ymd_opt(2012, 12, 12) |
1003 | .unwrap() |
1004 | .and_hms_milli_opt(18, 22, 30, 0) |
1005 | .unwrap(), |
1006 | ) |
1007 | .unwrap() |
1008 | .naive_utc(); |
1009 | assert_eq!( |
1010 | dt.duration_round_up(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), |
1011 | "2012-12-12 18:25:00" |
1012 | ); |
1013 | assert_eq!( |
1014 | dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
1015 | "2012-12-12 18:30:00" |
1016 | ); |
1017 | assert_eq!( |
1018 | dt.duration_round_up(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), |
1019 | "2012-12-12 18:30:00" |
1020 | ); |
1021 | assert_eq!( |
1022 | dt.duration_round_up(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), |
1023 | "2012-12-12 19:00:00" |
1024 | ); |
1025 | assert_eq!( |
1026 | dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), |
1027 | "2012-12-13 00:00:00" |
1028 | ); |
1029 | } |
1030 | |
1031 | #[test ] |
1032 | fn test_duration_round_up_pre_epoch() { |
1033 | let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); |
1034 | assert_eq!( |
1035 | dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), |
1036 | "1969-12-12 12:20:00 UTC" |
1037 | ); |
1038 | |
1039 | let time_delta = TimeDelta::minutes(30); |
1040 | assert_eq!( |
1041 | DateTime::UNIX_EPOCH.duration_round_up(time_delta).unwrap().to_string(), |
1042 | "1970-01-01 00:00:00 UTC" |
1043 | ) |
1044 | } |
1045 | |
1046 | #[test ] |
1047 | fn test_duration_round_up_close_to_min_max() { |
1048 | let mut dt = NaiveDate::from_ymd_opt(2012, 12, 12) |
1049 | .unwrap() |
1050 | .and_hms_milli_opt(18, 22, 30, 0) |
1051 | .unwrap() |
1052 | .and_utc(); |
1053 | |
1054 | let span = TimeDelta::nanoseconds(i64::MAX); |
1055 | |
1056 | assert_eq!( |
1057 | dt.duration_round_up(span).unwrap().to_string(), |
1058 | DateTime::from_timestamp_nanos(i64::MAX).to_string() |
1059 | ); |
1060 | |
1061 | dt = DateTime::UNIX_EPOCH + TimeDelta::nanoseconds(1); |
1062 | assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::from_timestamp_nanos(i64::MAX)); |
1063 | |
1064 | let dt = DateTime::from_timestamp_nanos(1); |
1065 | assert_eq!( |
1066 | dt.duration_round_up(span).unwrap().to_string(), |
1067 | "2262-04-11 23:47:16.854775807 UTC" |
1068 | ); |
1069 | |
1070 | let dt = DateTime::from_timestamp_nanos(-1); |
1071 | assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::UNIX_EPOCH); |
1072 | |
1073 | // Rounds to 1677-09-21 00:12:43.145224193 UTC if at i64::MIN. |
1074 | // because i64::MIN is 1677-09-21 00:12:43.145224192 UTC. |
1075 | // |
1076 | // v |
1077 | // We add 2 to get to 1677-09-21 00:12:43.145224194 UTC |
1078 | // this issue is because abs(i64::MIN) == i64::MAX + 1 |
1079 | let dt = DateTime::from_timestamp_nanos(i64::MIN + 2); |
1080 | assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::UNIX_EPOCH); |
1081 | } |
1082 | } |
1083 | |