Skip to content

Commit a8405af

Browse files
authored
Add feature to force float64-representable durations (#621)
Fixes #613
1 parent 71b78df commit a8405af

3 files changed

Lines changed: 47 additions & 13 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ tzdb = [
109109
"timezone_provider/tzif",
110110
]
111111
std = []
112+
# https://github.com/boa-dev/temporal/issues/613
113+
# ECMA-conformant implementations must store durations as float64-representable numbers
114+
# Rust users probably should not enable this, unless they wish to match the quirks of JavaScript
115+
float64_representable_durations = []
112116

113117
[package.metadata.cargo-all-features]
114118
denylist = ["default"]

src/builtins/core/duration.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,21 @@ impl Duration {
389389
hours,
390390
minutes,
391391
seconds,
392+
393+
// https://github.com/boa-dev/temporal/issues/613
394+
// With float64_representable_durations enabled, force all smaller units
395+
// to be in the float64-representable range.
396+
#[cfg(feature = "float64_representable_durations")]
397+
milliseconds: milliseconds as f64 as u64,
398+
#[cfg(feature = "float64_representable_durations")]
399+
microseconds: microseconds as f64 as u128,
400+
#[cfg(feature = "float64_representable_durations")]
401+
nanoseconds: nanoseconds as f64 as u128,
402+
#[cfg(not(feature = "float64_representable_durations"))]
392403
milliseconds,
404+
#[cfg(not(feature = "float64_representable_durations"))]
393405
microseconds,
406+
#[cfg(not(feature = "float64_representable_durations"))]
394407
nanoseconds,
395408
}
396409
}

src/builtins/core/duration/tests.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -404,23 +404,40 @@ fn test_duration_compare() {
404404
)
405405
}
406406
}
407-
/*
408-
TODO: Uncomment
409407

410-
The below test should fail, but currently doesn't. This has to do with weird
411-
floating point math in IsValidDuration Step 6-8 that defers to C++ std::remquo
412-
413-
Needs further clarification.
408+
const MAX_SAFE_INT: i64 = 9_007_199_254_740_991;
414409

415410
#[test]
416411
fn duration_round_out_of_range_norm_conversion() {
417-
const MAX_SAFE_INT: i64 = 9_007_199_254_740_991;
418412
let duration = Duration::new(0, 0, 0, 0, 0, 0, MAX_SAFE_INT, 0, 0, 999_999_999).unwrap();
419-
let err = duration.round_with_provider( RoundingOptions {
420-
largest_unit: Some(Unit::Nanosecond),
421-
increment: Some(RoundingIncrement::ONE),
422-
..Default::default()
423-
}, None, &NeverProvider::default());
413+
let err = duration.round_with_provider(
414+
RoundingOptions {
415+
largest_unit: Some(Unit::Nanosecond),
416+
increment: Some(RoundingIncrement::ONE),
417+
..Default::default()
418+
},
419+
None,
420+
&NeverProvider::default(),
421+
);
424422
assert!(err.is_err())
425423
}
426-
*/
424+
425+
#[test]
426+
#[cfg_attr(not(feature = "float64_representable_durations"), should_panic)]
427+
fn duration_float64_representable() {
428+
// built-ins/Temporal/Duration/prototype/add/float64-representable-integer
429+
let duration = Duration::new(0, 0, 0, 0, 0, 0, 0, 0, MAX_SAFE_INT as i128, 0).unwrap();
430+
let duration2 = Duration::new(0, 0, 0, 0, 0, 0, 0, 0, MAX_SAFE_INT as i128 - 1, 0).unwrap();
431+
let added = duration.add(&duration2).unwrap();
432+
assert_eq!(added.microseconds, 18014398509481980);
433+
assert_eq!(
434+
added.as_temporal_string(Default::default()).unwrap(),
435+
"PT18014398509.48198S"
436+
);
437+
let one_ms = Duration::new(0, 0, 0, 0, 0, 0, 0, 0, 1, 0).unwrap();
438+
let added_plus_one = added.add(&one_ms).unwrap();
439+
assert_eq!(
440+
added, added_plus_one,
441+
"Should not internally use a more accurate representation when adding"
442+
);
443+
}

0 commit comments

Comments
 (0)