Skip to content

Commit 0fada43

Browse files
authored
Implement DateTime round method (#88)
This PR implements `DateTime:round`. In order to do this, `IsoDateTime::round` needed to be implemented, and `IsoTime::round` was updated to the current specification version
1 parent d43b706 commit 0fada43

File tree

5 files changed

+229
-116
lines changed

5 files changed

+229
-116
lines changed

src/components/datetime.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
iso::{IsoDate, IsoDateSlots, IsoDateTime, IsoTime},
66
options::{
77
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, ResolvedRoundingOptions,
8-
TemporalUnit,
8+
RoundingOptions, TemporalUnit,
99
},
1010
parsers::parse_date_time,
1111
temporal_assert, Sign, TemporalError, TemporalResult, TemporalUnwrap,
@@ -463,6 +463,19 @@ impl DateTime {
463463
pub fn since(&self, other: &Self, settings: DifferenceSettings) -> TemporalResult<Duration> {
464464
self.diff(DifferenceOperation::Since, other, settings)
465465
}
466+
467+
/// Rounds the current datetime based on provided options.
468+
pub fn round(&self, options: RoundingOptions) -> TemporalResult<Self> {
469+
let resolved = ResolvedRoundingOptions::from_dt_options(options)?;
470+
471+
if resolved.is_noop() {
472+
return Ok(self.clone());
473+
}
474+
475+
let result = self.iso.round(resolved)?;
476+
477+
Ok(Self::new_unchecked(result, self.calendar.clone()))
478+
}
466479
}
467480

468481
// ==== Trait impls ====
@@ -530,7 +543,10 @@ mod tests {
530543
use crate::{
531544
components::{calendar::Calendar, duration::DateDuration, Duration},
532545
iso::{IsoDate, IsoTime},
533-
options::{DifferenceSettings, RoundingIncrement, TemporalRoundingMode, TemporalUnit},
546+
options::{
547+
DifferenceSettings, RoundingIncrement, RoundingOptions, TemporalRoundingMode,
548+
TemporalUnit,
549+
},
534550
primitive::FiniteF64,
535551
};
536552

@@ -716,4 +732,60 @@ mod tests {
716732
assert_eq!(result.hours(), 4.0);
717733
assert_eq!(result.minutes(), 30.0);
718734
}
735+
736+
#[test]
737+
fn dt_round_basic() {
738+
let assert_datetime = |dt: DateTime, expected: (i32, u8, u8, u8, u8, u8, u16, u16, u16)| {
739+
assert_eq!(dt.iso_year(), expected.0);
740+
assert_eq!(dt.iso_month(), expected.1);
741+
assert_eq!(dt.iso_day(), expected.2);
742+
assert_eq!(dt.hour(), expected.3);
743+
assert_eq!(dt.minute(), expected.4);
744+
assert_eq!(dt.second(), expected.5);
745+
assert_eq!(dt.millisecond(), expected.6);
746+
assert_eq!(dt.microsecond(), expected.7);
747+
assert_eq!(dt.nanosecond(), expected.8);
748+
};
749+
750+
let gen_rounding_options = |smallest: TemporalUnit, increment: u32| -> RoundingOptions {
751+
RoundingOptions {
752+
largest_unit: None,
753+
smallest_unit: Some(smallest),
754+
increment: Some(RoundingIncrement::try_new(increment).unwrap()),
755+
rounding_mode: None,
756+
}
757+
};
758+
let dt =
759+
DateTime::new(1976, 11, 18, 14, 23, 30, 123, 456, 789, Calendar::default()).unwrap();
760+
761+
let result = dt
762+
.round(gen_rounding_options(TemporalUnit::Hour, 4))
763+
.unwrap();
764+
assert_datetime(result, (1976, 11, 18, 16, 0, 0, 0, 0, 0));
765+
766+
let result = dt
767+
.round(gen_rounding_options(TemporalUnit::Minute, 15))
768+
.unwrap();
769+
assert_datetime(result, (1976, 11, 18, 14, 30, 0, 0, 0, 0));
770+
771+
let result = dt
772+
.round(gen_rounding_options(TemporalUnit::Second, 30))
773+
.unwrap();
774+
assert_datetime(result, (1976, 11, 18, 14, 23, 30, 0, 0, 0));
775+
776+
let result = dt
777+
.round(gen_rounding_options(TemporalUnit::Millisecond, 10))
778+
.unwrap();
779+
assert_datetime(result, (1976, 11, 18, 14, 23, 30, 120, 0, 0));
780+
781+
let result = dt
782+
.round(gen_rounding_options(TemporalUnit::Microsecond, 10))
783+
.unwrap();
784+
assert_datetime(result, (1976, 11, 18, 14, 23, 30, 123, 460, 0));
785+
786+
let result = dt
787+
.round(gen_rounding_options(TemporalUnit::Nanosecond, 10))
788+
.unwrap();
789+
assert_datetime(result, (1976, 11, 18, 14, 23, 30, 123, 456, 790));
790+
}
719791
}

src/components/duration.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ impl Duration {
429429
// 25. If maximum is not undefined, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
430430
let existing_largest_unit = self.default_largest_unit();
431431
let resolved_options =
432-
ResolvedRoundingOptions::from_options(options, existing_largest_unit)?;
432+
ResolvedRoundingOptions::from_duration_options(options, existing_largest_unit)?;
433433

434434
// 26. Let hoursToDaysConversionMayOccur be false.
435435
// 27. If duration.[[Days]] ≠ 0 and zonedRelativeTo is not undefined, set hoursToDaysConversionMayOccur to true.

src/components/time.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ impl Time {
255255
rounding_mode: Option<TemporalRoundingMode>,
256256
) -> TemporalResult<Self> {
257257
let increment = RoundingIncrement::try_from(rounding_increment.unwrap_or(1.0))?;
258-
let mode = rounding_mode.unwrap_or(TemporalRoundingMode::HalfExpand);
258+
let rounding_mode = rounding_mode.unwrap_or(TemporalRoundingMode::HalfExpand);
259259

260260
let max = smallest_unit
261261
.to_maximum_rounding_increment()
@@ -266,7 +266,14 @@ impl Time {
266266
// Safety (nekevss): to_rounding_increment returns a value in the range of a u32.
267267
increment.validate(u64::from(max), false)?;
268268

269-
let (_, result) = self.iso.round(increment, smallest_unit, mode, None)?;
269+
let resolved = ResolvedRoundingOptions {
270+
largest_unit: TemporalUnit::Auto,
271+
increment,
272+
smallest_unit,
273+
rounding_mode,
274+
};
275+
276+
let (_, result) = self.iso.round(resolved)?;
270277

271278
Ok(Self::new_unchecked(result))
272279
}

0 commit comments

Comments
 (0)