diff --git a/crates/nu-command/tests/commands/into_duration.rs b/crates/nu-command/tests/commands/into_duration.rs new file mode 100644 index 0000000000..a39ea8e12d --- /dev/null +++ b/crates/nu-command/tests/commands/into_duration.rs @@ -0,0 +1,10 @@ +use nu_test_support::nu; + +// Tests happy paths + +#[test] +fn into_duration_float() { + let actual = nu!(r#"1.07min | into duration"#); + + assert_eq!("1min 4sec 200ms", actual.out); +} diff --git a/crates/nu-command/tests/commands/math/mod.rs b/crates/nu-command/tests/commands/math/mod.rs index 5d383b9d9d..f500f9dc02 100644 --- a/crates/nu-command/tests/commands/math/mod.rs +++ b/crates/nu-command/tests/commands/math/mod.rs @@ -375,7 +375,7 @@ fn duration_decimal_math_with_nanoseconds() { "# )); - assert_eq!(actual.out, "1wk 3day 10ns"); + assert_eq!(actual.out, "1wk 3day 12hr 10ns"); } #[test] diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index f19c99abb7..0d0fa8b1a1 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -54,6 +54,7 @@ mod insert; mod inspect; mod interleave; mod into_datetime; +mod into_duration; mod into_filesize; mod into_int; mod join; diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 6dd56b47e3..06a38afe7e 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2701,12 +2701,31 @@ pub fn parse_unit_value<'res>( } }); - let (num, unit) = match convert { - Some(convert_to) => ( - ((number_part * convert_to.1 as f64) + (decimal_part * convert_to.1 as f64)) as i64, - convert_to.0, - ), - None => (number_part as i64, *unit), + let mut unit = match convert { + Some(convert_to) => convert_to.0, + None => *unit, + }; + + let num_float = match convert { + Some(convert_to) => { + (number_part * convert_to.1 as f64) + (decimal_part * convert_to.1 as f64) + } + None => number_part, + }; + + // Convert all durations to nanoseconds to not lose precision + let num = match unit_to_ns_factor(&unit) { + Some(factor) => { + let num_ns = num_float * factor; + if i64::MIN as f64 <= num_ns && num_ns <= i64::MAX as f64 { + unit = Unit::Nanosecond; + num_ns as i64 + } else { + // not safe to convert, because of the overflow + num_float as i64 + } + } + None => num_float as i64, }; trace!("-- found {} {:?}", num, unit); @@ -2813,6 +2832,20 @@ pub const DURATION_UNIT_GROUPS: &[UnitGroup] = &[ (Unit::Week, "wk", Some((Unit::Day, 7))), ]; +fn unit_to_ns_factor(unit: &Unit) -> Option { + match unit { + Unit::Nanosecond => Some(1.0), + Unit::Microsecond => Some(1_000.0), + Unit::Millisecond => Some(1_000_000.0), + Unit::Second => Some(1_000_000_000.0), + Unit::Minute => Some(60.0 * 1_000_000_000.0), + Unit::Hour => Some(60.0 * 60.0 * 1_000_000_000.0), + Unit::Day => Some(24.0 * 60.0 * 60.0 * 1_000_000_000.0), + Unit::Week => Some(7.0 * 24.0 * 60.0 * 60.0 * 1_000_000_000.0), + _ => None, + } +} + // Borrowed from libm at https://github.com/rust-lang/libm/blob/master/src/math/modf.rs fn modf(x: f64) -> (f64, f64) { let rv2: f64; @@ -6878,13 +6911,9 @@ pub fn parse( let mut output = { if let Some(block) = previously_parsed_block { - // dbg!("previous block"); return block; } else { - // dbg!("starting lex"); let (output, err) = lex(contents, new_span.start, &[], &[], false); - // dbg!("finished lex"); - // dbg!(&output); if let Some(err) = err { working_set.error(err) } diff --git a/tests/parsing/mod.rs b/tests/parsing/mod.rs index 5a6b55fc44..ceab1721ac 100644 --- a/tests/parsing/mod.rs +++ b/tests/parsing/mod.rs @@ -300,7 +300,7 @@ fn parse_long_duration() { "78.797877879789789sec" | into duration "#); - assert_eq!(actual.out, "1min 18sec 797ms"); + assert_eq!(actual.out, "1min 18sec 797ms 877µs 879ns"); } #[rstest]