Skip to content

Commit efb607a

Browse files
authored
Implement until and since methods (#36)
1 parent 0efe96d commit efb607a

File tree

3 files changed

+182
-9
lines changed

3 files changed

+182
-9
lines changed

src/components/duration/normalized.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const MAX_TIME_DURATION: f64 = 2e53 * 10e9 - 1.0;
1010

1111
/// A Normalized `TimeDuration` that represents the current `TimeDuration` in nanoseconds.
1212
#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd)]
13-
pub struct NormalizedTimeDuration(pub(super) f64);
13+
pub struct NormalizedTimeDuration(pub(crate) f64);
1414

1515
impl NormalizedTimeDuration {
1616
/// Equivalent: 7.5.20 NormalizeTimeDuration ( hours, minutes, seconds, milliseconds, microseconds, nanoseconds )

src/components/time.rs

Lines changed: 168 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub struct Time {
1919
impl Time {
2020
#[inline]
2121
#[must_use]
22+
/// Creates a new unvalidated `Time`.
2223
pub(crate) fn new_unchecked(iso: IsoTime) -> Self {
2324
Self { iso }
2425
}
@@ -46,6 +47,69 @@ impl Time {
4647

4748
Self::new_unchecked(result)
4849
}
50+
51+
/// Performs a desired difference op between two `Time`'s, returning the resulting `Duration`.
52+
pub(crate) fn diff_time(
53+
&self,
54+
op: bool,
55+
other: &Time,
56+
rounding_mode: Option<TemporalRoundingMode>,
57+
rounding_increment: Option<f64>,
58+
largest_unit: Option<TemporalUnit>,
59+
smallest_unit: Option<TemporalUnit>,
60+
) -> TemporalResult<Duration> {
61+
// 1. If operation is SINCE, let sign be -1. Otherwise, let sign be 1.
62+
// 2. Set other to ? ToTemporalTime(other).
63+
// 3. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
64+
// 4. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, TIME, « », "nanosecond", "hour").
65+
let increment = utils::to_rounding_increment(rounding_increment)?;
66+
let (sign, rounding_mode) = if op {
67+
(
68+
-1.0,
69+
rounding_mode
70+
.unwrap_or(TemporalRoundingMode::Trunc)
71+
.negate(),
72+
)
73+
} else {
74+
(1.0, rounding_mode.unwrap_or(TemporalRoundingMode::Trunc))
75+
};
76+
77+
let smallest_unit = smallest_unit.unwrap_or(TemporalUnit::Nanosecond);
78+
// Use the defaultlargestunit which is max smallestlargestdefault and smallestunit
79+
let largest_unit = largest_unit.unwrap_or(smallest_unit.max(TemporalUnit::Hour));
80+
81+
// 5. Let norm be ! DifferenceTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]],
82+
// temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]],
83+
// temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]],
84+
// other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
85+
let time = self.iso.diff(&other.iso);
86+
87+
// 6. If settings.[[SmallestUnit]] is not "nanosecond" or settings.[[RoundingIncrement]] ≠ 1, then
88+
let norm = if smallest_unit != TemporalUnit::Nanosecond || rounding_increment != Some(1.0) {
89+
// a. Let roundRecord be ! RoundDuration(0, 0, 0, 0, norm, settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
90+
let round_record = time.round(increment, smallest_unit, rounding_mode)?;
91+
// b. Set norm to roundRecord.[[NormalizedDuration]].[[NormalizedTime]].
92+
round_record.0
93+
} else {
94+
time.to_normalized()
95+
};
96+
97+
// 7. Let result be BalanceTimeDuration(norm, settings.[[LargestUnit]]).
98+
let result = TimeDuration::from_normalized(norm, largest_unit)?.1;
99+
// 8. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
100+
Duration::new(
101+
0.0,
102+
0.0,
103+
0.0,
104+
0.0,
105+
sign * result.hours,
106+
sign * result.minutes,
107+
sign * result.seconds,
108+
sign * result.milliseconds,
109+
sign * result.microseconds,
110+
sign * result.nanoseconds,
111+
)
112+
}
49113
}
50114

51115
// ==== Public API ====
@@ -147,6 +211,50 @@ impl Time {
147211
self.add_to_time(&duration.negated())
148212
}
149213

214+
#[inline]
215+
/// Returns the `Duration` until the provided `Time` from the current `Time`.
216+
///
217+
/// NOTE: `until` assumes the provided other time will occur in the future relative to the current.
218+
pub fn until(
219+
&self,
220+
other: &Self,
221+
rounding_mode: Option<TemporalRoundingMode>,
222+
rounding_increment: Option<f64>,
223+
largest_unit: Option<TemporalUnit>,
224+
smallest_unit: Option<TemporalUnit>,
225+
) -> TemporalResult<Duration> {
226+
self.diff_time(
227+
false,
228+
other,
229+
rounding_mode,
230+
rounding_increment,
231+
largest_unit,
232+
smallest_unit,
233+
)
234+
}
235+
236+
#[inline]
237+
/// Returns the `Duration` since the provided `Time` from the current `Time`.
238+
///
239+
/// NOTE: `since` assumes the provided other time is in the past relative to the current.
240+
pub fn since(
241+
&self,
242+
other: &Self,
243+
rounding_mode: Option<TemporalRoundingMode>,
244+
rounding_increment: Option<f64>,
245+
largest_unit: Option<TemporalUnit>,
246+
smallest_unit: Option<TemporalUnit>,
247+
) -> TemporalResult<Duration> {
248+
self.diff_time(
249+
true,
250+
other,
251+
rounding_mode,
252+
rounding_increment,
253+
largest_unit,
254+
smallest_unit,
255+
)
256+
}
257+
150258
// TODO (nekevss): optimize and test rounding_increment type (f64 vs. u64).
151259
/// Rounds the current `Time` according to provided options.
152260
pub fn round(
@@ -177,17 +285,28 @@ impl Time {
177285

178286
#[cfg(test)]
179287
mod tests {
180-
use crate::{components::Duration, iso::IsoTime, options::TemporalUnit};
288+
use crate::{
289+
components::Duration,
290+
iso::IsoTime,
291+
options::{ArithmeticOverflow, TemporalUnit},
292+
};
181293

182294
use super::Time;
183295

184296
fn assert_time(result: Time, values: (u8, u8, u8, u16, u16, u16)) {
185-
assert!(result.hour() == values.0);
186-
assert!(result.minute() == values.1);
187-
assert!(result.second() == values.2);
188-
assert!(result.millisecond() == values.3);
189-
assert!(result.microsecond() == values.4);
190-
assert!(result.nanosecond() == values.5);
297+
assert_eq!(
298+
result,
299+
Time {
300+
iso: IsoTime {
301+
hour: values.0,
302+
minute: values.1,
303+
second: values.2,
304+
millisecond: values.3,
305+
microsecond: values.4,
306+
nanosecond: values.5,
307+
}
308+
}
309+
);
191310
}
192311

193312
#[test]
@@ -272,4 +391,46 @@ mod tests {
272391

273392
assert_time(result, (7, 23, 30, 123, 456, 789));
274393
}
394+
395+
#[test]
396+
fn since_basic() {
397+
let one = Time::new(15, 23, 30, 123, 456, 789, ArithmeticOverflow::Constrain).unwrap();
398+
let two = Time::new(14, 23, 30, 123, 456, 789, ArithmeticOverflow::Constrain).unwrap();
399+
let three = Time::new(13, 30, 30, 123, 456, 789, ArithmeticOverflow::Constrain).unwrap();
400+
401+
let result = one.since(&two, None, None, None, None).unwrap();
402+
assert_eq!(result.hours(), 1.0);
403+
404+
let result = two.since(&one, None, None, None, None).unwrap();
405+
assert_eq!(result.hours(), -1.0);
406+
407+
let result = one.since(&three, None, None, None, None).unwrap();
408+
assert_eq!(result.hours(), 1.0);
409+
assert_eq!(result.minutes(), 53.0);
410+
411+
let result = three.since(&one, None, None, None, None).unwrap();
412+
assert_eq!(result.hours(), -1.0);
413+
assert_eq!(result.minutes(), -53.0);
414+
}
415+
416+
#[test]
417+
fn until_basic() {
418+
let one = Time::new(15, 23, 30, 123, 456, 789, ArithmeticOverflow::Constrain).unwrap();
419+
let two = Time::new(16, 23, 30, 123, 456, 789, ArithmeticOverflow::Constrain).unwrap();
420+
let three = Time::new(17, 0, 30, 123, 456, 789, ArithmeticOverflow::Constrain).unwrap();
421+
422+
let result = one.until(&two, None, None, None, None).unwrap();
423+
assert_eq!(result.hours(), 1.0);
424+
425+
let result = two.until(&one, None, None, None, None).unwrap();
426+
assert_eq!(result.hours(), -1.0);
427+
428+
let result = one.until(&three, None, None, None, None).unwrap();
429+
assert_eq!(result.hours(), 1.0);
430+
assert_eq!(result.minutes(), 37.0);
431+
432+
let result = three.until(&one, None, None, None, None).unwrap();
433+
assert_eq!(result.hours(), -1.0);
434+
assert_eq!(result.minutes(), -37.0);
435+
}
275436
}

src/iso.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//! An `IsoDateTime` has the internal slots of both an `IsoDate` and `IsoTime`.
1414
1515
use crate::{
16-
components::duration::DateDuration,
16+
components::duration::{DateDuration, TimeDuration},
1717
error::TemporalError,
1818
options::{ArithmeticOverflow, TemporalRoundingMode, TemporalUnit},
1919
utils, TemporalResult, NS_PER_DAY,
@@ -495,6 +495,18 @@ impl IsoTime {
495495
(days as i32, time)
496496
}
497497

498+
/// Difference this `IsoTime` against another and returning a `TimeDuration`.
499+
pub(crate) fn diff(&self, other: &Self) -> TimeDuration {
500+
let h = f64::from(other.hour) - f64::from(self.hour);
501+
let m = f64::from(other.minute) - f64::from(self.minute);
502+
let s = f64::from(other.second) - f64::from(self.second);
503+
let ms = f64::from(other.millisecond) - f64::from(self.millisecond);
504+
let mis = f64::from(other.microsecond) - f64::from(self.microsecond);
505+
let ns = f64::from(other.nanosecond) - f64::from(self.nanosecond);
506+
507+
TimeDuration::new_unchecked(h, m, s, ms, mis, ns)
508+
}
509+
498510
// NOTE (nekevss): Specification seemed to be off / not entirely working, so the below was adapted from the
499511
// temporal-polyfill
500512
// TODO: DayLengthNS can probably be a u64, but keep as is for now and optimize.

0 commit comments

Comments
 (0)