From 67b6188b19a5279eaf13a33bfee5b44583c3b9ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lo=C3=AFc=20Riegel?=
<96702577+LoicRiegel@users.noreply.github.com>
Date: Thu, 3 Apr 2025 14:05:18 +0200
Subject: [PATCH] feat: into duration accepts floats (#15297)
Issue #9887 which can be closed after this is merged.
# Description
This allows the "into duration" command to accept floats as inputs.
Examples:
**How it works:**
Using strings, like `"1.234sec" | into duration`, is already working, so
if a user inputs `1.234 | into duration --sec`, I just convert this back
to a string and use the previous conversion functions.
**Limitations:**
there are some limitation to using floats, but it's a general limitation
that is already present for other use cases:
- only 3 digits are taken into account in the decimal part
- floating durations in nano seconds are always floored and not rounded
# User-Facing Changes
Users can inject floats with `into duration`
# Tests + Formatting
cargo fmt and clippy OK
Tests OK
# After Submitting
The example I added will automatically become part of the doc, I think
that's enough for documentation.
---
.../src/conversions/into/duration.rs | 50 +++++++++++++------
1 file changed, 35 insertions(+), 15 deletions(-)
diff --git a/crates/nu-command/src/conversions/into/duration.rs b/crates/nu-command/src/conversions/into/duration.rs
index 129188f839..8c92c22841 100644
--- a/crates/nu-command/src/conversions/into/duration.rs
+++ b/crates/nu-command/src/conversions/into/duration.rs
@@ -15,6 +15,7 @@ impl Command for IntoDuration {
Signature::build("into duration")
.input_output_types(vec![
(Type::Int, Type::Duration),
+ (Type::Float, Type::Duration),
(Type::String, Type::Duration),
(Type::Duration, Type::Duration),
(Type::table(), Type::table()),
@@ -109,6 +110,11 @@ impl Command for IntoDuration {
example: "1_234 | into duration --unit ms",
result: Some(Value::test_duration(1_234 * 1_000_000)),
},
+ Example {
+ description: "Convert a floating point number of an arbitrary unit to duration",
+ example: "1.234 | into duration --unit sec",
+ result: Some(Value::test_duration(1_234 * 1_000_000)),
+ },
]
}
}
@@ -236,22 +242,22 @@ fn action(input: &Value, unit: &str, span: Span) -> Value {
let value_span = input.span();
match input {
Value::Duration { .. } => input.clone(),
- Value::String { val, .. } => match compound_to_duration(val, value_span) {
- Ok(val) => Value::duration(val, span),
- Err(error) => Value::error(error, span),
- },
+ Value::String { val, .. } => {
+ if let Ok(num) = val.parse::() {
+ let ns = unit_to_ns_factor(unit);
+ return Value::duration((num * (ns as f64)) as i64, span);
+ }
+ match compound_to_duration(val, value_span) {
+ Ok(val) => Value::duration(val, span),
+ Err(error) => Value::error(error, span),
+ }
+ }
+ Value::Float { val, .. } => {
+ let ns = unit_to_ns_factor(unit);
+ Value::duration((*val * (ns as f64)) as i64, span)
+ }
Value::Int { val, .. } => {
- let ns = match unit {
- "ns" => 1,
- "us" | "µs" => 1_000,
- "ms" => 1_000_000,
- "sec" => NS_PER_SEC,
- "min" => NS_PER_SEC * 60,
- "hr" => NS_PER_SEC * 60 * 60,
- "day" => NS_PER_SEC * 60 * 60 * 24,
- "wk" => NS_PER_SEC * 60 * 60 * 24 * 7,
- _ => 0,
- };
+ let ns = unit_to_ns_factor(unit);
Value::duration(*val * ns, span)
}
// Propagate errors by explicitly matching them before the final case.
@@ -268,6 +274,20 @@ fn action(input: &Value, unit: &str, span: Span) -> Value {
}
}
+fn unit_to_ns_factor(unit: &str) -> i64 {
+ match unit {
+ "ns" => 1,
+ "us" | "µs" => 1_000,
+ "ms" => 1_000_000,
+ "sec" => NS_PER_SEC,
+ "min" => NS_PER_SEC * 60,
+ "hr" => NS_PER_SEC * 60 * 60,
+ "day" => NS_PER_SEC * 60 * 60 * 24,
+ "wk" => NS_PER_SEC * 60 * 60 * 24 * 7,
+ _ => 0,
+ }
+}
+
#[cfg(test)]
mod test {
use super::*;