summaryrefslogtreecommitdiffstats
path: root/third_party/rust/chrono/src/round.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/chrono/src/round.rs')
-rw-r--r--third_party/rust/chrono/src/round.rs178
1 files changed, 178 insertions, 0 deletions
diff --git a/third_party/rust/chrono/src/round.rs b/third_party/rust/chrono/src/round.rs
new file mode 100644
index 0000000000..ac5b984be3
--- /dev/null
+++ b/third_party/rust/chrono/src/round.rs
@@ -0,0 +1,178 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+use Timelike;
+use core::ops::{Add, Sub};
+use oldtime::Duration;
+
+/// Extension trait for subsecond rounding or truncation to a maximum number
+/// of digits. Rounding can be used to decrease the error variance when
+/// serializing/persisting to lower precision. Truncation is the default
+/// behavior in Chrono display formatting. Either can be used to guarantee
+/// equality (e.g. for testing) when round-tripping through a lower precision
+/// format.
+pub trait SubsecRound {
+ /// Return a copy rounded to the specified number of subsecond digits. With
+ /// 9 or more digits, self is returned unmodified. Halfway values are
+ /// rounded up (away from zero).
+ ///
+ /// # Example
+ /// ``` rust
+ /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
+ /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
+ /// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000);
+ /// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000);
+ /// ```
+ fn round_subsecs(self, digits: u16) -> Self;
+
+ /// Return a copy truncated to the specified number of subsecond
+ /// digits. With 9 or more digits, self is returned unmodified.
+ ///
+ /// # Example
+ /// ``` rust
+ /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
+ /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
+ /// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000);
+ /// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000);
+ /// ```
+ fn trunc_subsecs(self, digits: u16) -> Self;
+}
+
+impl<T> SubsecRound for T
+where T: Timelike + Add<Duration, Output=T> + Sub<Duration, Output=T>
+{
+ fn round_subsecs(self, digits: u16) -> T {
+ let span = span_for_digits(digits);
+ let delta_down = self.nanosecond() % span;
+ if delta_down > 0 {
+ let delta_up = span - delta_down;
+ if delta_up <= delta_down {
+ self + Duration::nanoseconds(delta_up.into())
+ } else {
+ self - Duration::nanoseconds(delta_down.into())
+ }
+ } else {
+ self // unchanged
+ }
+ }
+
+ fn trunc_subsecs(self, digits: u16) -> T {
+ let span = span_for_digits(digits);
+ let delta_down = self.nanosecond() % span;
+ if delta_down > 0 {
+ self - Duration::nanoseconds(delta_down.into())
+ } else {
+ self // unchanged
+ }
+ }
+}
+
+// Return the maximum span in nanoseconds for the target number of digits.
+fn span_for_digits(digits: u16) -> u32 {
+ // fast lookup form of: 10^(9-min(9,digits))
+ match digits {
+ 0 => 1_000_000_000,
+ 1 => 100_000_000,
+ 2 => 10_000_000,
+ 3 => 1_000_000,
+ 4 => 100_000,
+ 5 => 10_000,
+ 6 => 1_000,
+ 7 => 100,
+ 8 => 10,
+ _ => 1
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use Timelike;
+ use offset::{FixedOffset, TimeZone, Utc};
+ use super::SubsecRound;
+
+ #[test]
+ fn test_round() {
+ let pst = FixedOffset::east(8 * 60 * 60);
+ let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
+
+ assert_eq!(dt.round_subsecs(10), dt);
+ assert_eq!(dt.round_subsecs(9), dt);
+ assert_eq!(dt.round_subsecs(8).nanosecond(), 084_660_680);
+ assert_eq!(dt.round_subsecs(7).nanosecond(), 084_660_700);
+ assert_eq!(dt.round_subsecs(6).nanosecond(), 084_661_000);
+ assert_eq!(dt.round_subsecs(5).nanosecond(), 084_660_000);
+ assert_eq!(dt.round_subsecs(4).nanosecond(), 084_700_000);
+ assert_eq!(dt.round_subsecs(3).nanosecond(), 085_000_000);
+ assert_eq!(dt.round_subsecs(2).nanosecond(), 080_000_000);
+ assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000);
+
+ assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
+ assert_eq!(dt.round_subsecs(0).second(), 13);
+
+ let dt = Utc.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
+ assert_eq!(dt.round_subsecs(9), dt);
+ assert_eq!(dt.round_subsecs(4), dt);
+ assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000);
+ assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000);
+ assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000);
+
+ assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
+ assert_eq!(dt.round_subsecs(0).second(), 28);
+ }
+
+ #[test]
+ fn test_round_leap_nanos() {
+ let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
+ assert_eq!(dt.round_subsecs(9), dt);
+ assert_eq!(dt.round_subsecs(4), dt);
+ assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000);
+ assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000);
+ assert_eq!(dt.round_subsecs(1).second(), 59);
+
+ assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
+ assert_eq!(dt.round_subsecs(0).second(), 0);
+ }
+
+ #[test]
+ fn test_trunc() {
+ let pst = FixedOffset::east(8 * 60 * 60);
+ let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
+
+ assert_eq!(dt.trunc_subsecs(10), dt);
+ assert_eq!(dt.trunc_subsecs(9), dt);
+ assert_eq!(dt.trunc_subsecs(8).nanosecond(), 084_660_680);
+ assert_eq!(dt.trunc_subsecs(7).nanosecond(), 084_660_600);
+ assert_eq!(dt.trunc_subsecs(6).nanosecond(), 084_660_000);
+ assert_eq!(dt.trunc_subsecs(5).nanosecond(), 084_660_000);
+ assert_eq!(dt.trunc_subsecs(4).nanosecond(), 084_600_000);
+ assert_eq!(dt.trunc_subsecs(3).nanosecond(), 084_000_000);
+ assert_eq!(dt.trunc_subsecs(2).nanosecond(), 080_000_000);
+ assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0);
+
+ assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
+ assert_eq!(dt.trunc_subsecs(0).second(), 13);
+
+ let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
+ assert_eq!(dt.trunc_subsecs(9), dt);
+ assert_eq!(dt.trunc_subsecs(4), dt);
+ assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000);
+ assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000);
+ assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000);
+
+ assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
+ assert_eq!(dt.trunc_subsecs(0).second(), 27);
+ }
+
+ #[test]
+ fn test_trunc_leap_nanos() {
+ let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
+ assert_eq!(dt.trunc_subsecs(9), dt);
+ assert_eq!(dt.trunc_subsecs(4), dt);
+ assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000);
+ assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000);
+ assert_eq!(dt.trunc_subsecs(1).second(), 59);
+
+ assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000);
+ assert_eq!(dt.trunc_subsecs(0).second(), 59);
+ }
+}