mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
expand durations to include month, year, decade (#6123)
* expand durations to include month, year, decade * remove commented out fn * oops, found more debug comments * tweaked tests for the new way, borrowed heavily from chrono-humanize-rs * clippy * grammar
This commit is contained in:
parent
f5856b0914
commit
d856ac92f4
7 changed files with 339 additions and 50 deletions
|
@ -28,6 +28,10 @@ impl Command for SubCommand {
|
|||
"Convert value to duration"
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"into duration does not take leap years into account and every month is calculated with 30 days"
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["convert", "time", "period"]
|
||||
}
|
||||
|
@ -149,6 +153,9 @@ fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result<i64, Shel
|
|||
Unit::Hour => return Ok(x * 60 * 60 * 1000 * 1000 * 1000),
|
||||
Unit::Day => return Ok(x * 24 * 60 * 60 * 1000 * 1000 * 1000),
|
||||
Unit::Week => return Ok(x * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000),
|
||||
Unit::Month => return Ok(x * 30 * 24 * 60 * 60 * 1000 * 1000 * 1000), //30 days to a month
|
||||
Unit::Year => return Ok(x * 365 * 24 * 60 * 60 * 1000 * 1000 * 1000), //365 days to a year
|
||||
Unit::Decade => return Ok(x * 10 * 365 * 24 * 60 * 60 * 1000 * 1000 * 1000), //365 days to a year
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -442,6 +442,14 @@ fn convert_to_value(
|
|||
val: size * 1000 * 1000 * 1000 * 1000 * 1000,
|
||||
span,
|
||||
}),
|
||||
Unit::Exabyte => Ok(Value::Filesize {
|
||||
val: size * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
||||
span,
|
||||
}),
|
||||
Unit::Zettabyte => Ok(Value::Filesize {
|
||||
val: size * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
||||
span,
|
||||
}),
|
||||
|
||||
Unit::Kibibyte => Ok(Value::Filesize {
|
||||
val: size * 1024,
|
||||
|
@ -463,6 +471,14 @@ fn convert_to_value(
|
|||
val: size * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
span,
|
||||
}),
|
||||
Unit::Exbibyte => Ok(Value::Filesize {
|
||||
val: size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
span,
|
||||
}),
|
||||
Unit::Zebibyte => Ok(Value::Filesize {
|
||||
val: size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
span,
|
||||
}),
|
||||
|
||||
Unit::Nanosecond => Ok(Value::Duration { val: size, span }),
|
||||
Unit::Microsecond => Ok(Value::Duration {
|
||||
|
@ -489,8 +505,8 @@ fn convert_to_value(
|
|||
Some(val) => Ok(Value::Duration { val, span }),
|
||||
None => Err(ShellError::OutsideSpannedLabeledError(
|
||||
original_text.to_string(),
|
||||
"duration too large".into(),
|
||||
"duration too large".into(),
|
||||
"day duration too large".into(),
|
||||
"day duration too large".into(),
|
||||
expr.span,
|
||||
)),
|
||||
},
|
||||
|
@ -499,11 +515,40 @@ fn convert_to_value(
|
|||
Some(val) => Ok(Value::Duration { val, span }),
|
||||
None => Err(ShellError::OutsideSpannedLabeledError(
|
||||
original_text.to_string(),
|
||||
"duration too large".into(),
|
||||
"duration too large".into(),
|
||||
"week duration too large".into(),
|
||||
"week duration too large".into(),
|
||||
expr.span,
|
||||
)),
|
||||
},
|
||||
Unit::Month => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 30) {
|
||||
Some(val) => Ok(Value::Duration { val, span }),
|
||||
None => Err(ShellError::OutsideSpannedLabeledError(
|
||||
original_text.to_string(),
|
||||
"month duration too large".into(),
|
||||
"month duration too large".into(),
|
||||
expr.span,
|
||||
)),
|
||||
},
|
||||
Unit::Year => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 365) {
|
||||
Some(val) => Ok(Value::Duration { val, span }),
|
||||
None => Err(ShellError::OutsideSpannedLabeledError(
|
||||
original_text.to_string(),
|
||||
"year duration too large".into(),
|
||||
"year duration too large".into(),
|
||||
expr.span,
|
||||
)),
|
||||
},
|
||||
Unit::Decade => {
|
||||
match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 365 * 10) {
|
||||
Some(val) => Ok(Value::Duration { val, span }),
|
||||
None => Err(ShellError::OutsideSpannedLabeledError(
|
||||
original_text.to_string(),
|
||||
"decade duration too large".into(),
|
||||
"decade duration too large".into(),
|
||||
expr.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Var(..) => Err(ShellError::OutsideSpannedLabeledError(
|
||||
|
|
|
@ -291,7 +291,7 @@ fn duration_math() {
|
|||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "8day");
|
||||
assert_eq!(actual.out, "1wk 1day");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -315,7 +315,7 @@ fn duration_math_with_nanoseconds() {
|
|||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "7day 10ns");
|
||||
assert_eq!(actual.out, "1wk 10ns");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -327,7 +327,22 @@ fn duration_decimal_math_with_nanoseconds() {
|
|||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "10day 10ns");
|
||||
assert_eq!(actual.out, "1wk 3day 10ns");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_decimal_math_with_all_units() {
|
||||
let actual = nu!(
|
||||
cwd: "tests/fixtures/formats", pipeline(
|
||||
r#"
|
||||
5dec + 3yr + 2month + 1wk + 3day + 8hr + 10min + 16sec + 121ms + 11us + 12ns
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
actual.out,
|
||||
"53yr 2month 1wk 3day 8hr 10min 16sec 121ms 11µs 12ns"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1452,6 +1452,14 @@ fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
|||
val: size * 1000 * 1000 * 1000 * 1000 * 1000,
|
||||
span,
|
||||
},
|
||||
Unit::Exabyte => Value::Filesize {
|
||||
val: size * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
||||
span,
|
||||
},
|
||||
Unit::Zettabyte => Value::Filesize {
|
||||
val: size * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
||||
span,
|
||||
},
|
||||
|
||||
Unit::Kibibyte => Value::Filesize {
|
||||
val: size * 1024,
|
||||
|
@ -1473,6 +1481,14 @@ fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
|||
val: size * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
span,
|
||||
},
|
||||
Unit::Exbibyte => Value::Filesize {
|
||||
val: size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
span,
|
||||
},
|
||||
Unit::Zebibyte => Value::Filesize {
|
||||
val: size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
span,
|
||||
},
|
||||
|
||||
Unit::Nanosecond => Value::Duration { val: size, span },
|
||||
Unit::Microsecond => Value::Duration {
|
||||
|
@ -1499,8 +1515,8 @@ fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
|||
Some(val) => Value::Duration { val, span },
|
||||
None => Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"duration too large".into(),
|
||||
"duration too large".into(),
|
||||
"day duration too large".into(),
|
||||
"day duration too large".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
|
@ -1511,8 +1527,44 @@ fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
|||
Some(val) => Value::Duration { val, span },
|
||||
None => Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"duration too large".into(),
|
||||
"duration too large".into(),
|
||||
"week duration too large".into(),
|
||||
"week duration too large".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
),
|
||||
},
|
||||
},
|
||||
Unit::Month => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 30) {
|
||||
Some(val) => Value::Duration { val, span },
|
||||
None => Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"month duration too large".into(),
|
||||
"month duration too large".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
),
|
||||
},
|
||||
},
|
||||
Unit::Year => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 365) {
|
||||
Some(val) => Value::Duration { val, span },
|
||||
None => Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"year duration too large".into(),
|
||||
"year duration too large".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
),
|
||||
},
|
||||
},
|
||||
Unit::Decade => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 365 * 10) {
|
||||
Some(val) => Value::Duration { val, span },
|
||||
None => Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"decade duration too large".into(),
|
||||
"decade duration too large".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
|
|
|
@ -2170,6 +2170,9 @@ pub fn parse_duration_bytes(bytes: &[u8], span: Span) -> Option<Expression> {
|
|||
(Unit::Hour, "HR", Some((Unit::Minute, 60))),
|
||||
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
|
||||
(Unit::Week, "WK", Some((Unit::Day, 7))),
|
||||
(Unit::Month, "MONTH", Some((Unit::Day, 30))), //30 day month
|
||||
(Unit::Year, "YR", Some((Unit::Day, 365))), //365 day year
|
||||
(Unit::Decade, "DEC", Some((Unit::Year, 10))), //365 day years
|
||||
];
|
||||
if let Some(unit) = unit_groups.iter().find(|&x| upper.ends_with(x.1)) {
|
||||
let mut lhs = token;
|
||||
|
@ -2264,11 +2267,15 @@ pub fn parse_filesize(
|
|||
(Unit::Gigabyte, "GB", Some((Unit::Megabyte, 1000))),
|
||||
(Unit::Terabyte, "TB", Some((Unit::Gigabyte, 1000))),
|
||||
(Unit::Petabyte, "PB", Some((Unit::Terabyte, 1000))),
|
||||
(Unit::Exabyte, "EB", Some((Unit::Petabyte, 1000))),
|
||||
(Unit::Zettabyte, "ZB", Some((Unit::Exabyte, 1000))),
|
||||
(Unit::Kibibyte, "KIB", Some((Unit::Byte, 1024))),
|
||||
(Unit::Mebibyte, "MIB", Some((Unit::Kibibyte, 1024))),
|
||||
(Unit::Gibibyte, "GIB", Some((Unit::Mebibyte, 1024))),
|
||||
(Unit::Tebibyte, "TIB", Some((Unit::Gibibyte, 1024))),
|
||||
(Unit::Pebibyte, "PIB", Some((Unit::Tebibyte, 1024))),
|
||||
(Unit::Exbibyte, "EIB", Some((Unit::Pebibyte, 1024))),
|
||||
(Unit::Zebibyte, "ZIB", Some((Unit::Exbibyte, 1024))),
|
||||
(Unit::Byte, "B", None),
|
||||
];
|
||||
if let Some(unit) = unit_groups.iter().find(|&x| upper.ends_with(x.1)) {
|
||||
|
|
|
@ -5,32 +5,32 @@ mod range;
|
|||
mod stream;
|
||||
mod unit;
|
||||
|
||||
use crate::ast::Operator;
|
||||
use crate::ast::{CellPath, PathMember};
|
||||
use crate::ShellError;
|
||||
use crate::{did_you_mean, BlockId, Config, Span, Spanned, Type, VarId};
|
||||
use byte_unit::ByteUnit;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use chrono::{DateTime, Duration, FixedOffset};
|
||||
use chrono_humanize::HumanTime;
|
||||
pub use custom_value::CustomValue;
|
||||
pub use from_value::FromValue;
|
||||
use indexmap::map::IndexMap;
|
||||
use num_format::{Locale, ToFormattedString};
|
||||
pub use range::*;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
iter,
|
||||
path::PathBuf,
|
||||
{cmp::Ordering, fmt::Debug},
|
||||
};
|
||||
pub use stream::*;
|
||||
use sys_locale::get_locale;
|
||||
pub use unit::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::{cmp::Ordering, fmt::Debug};
|
||||
|
||||
use crate::ast::{CellPath, PathMember};
|
||||
use crate::{did_you_mean, BlockId, Config, Span, Spanned, Type, VarId};
|
||||
|
||||
use crate::ast::Operator;
|
||||
pub use custom_value::CustomValue;
|
||||
use std::iter;
|
||||
|
||||
use crate::ShellError;
|
||||
|
||||
/// Core structured values that pass through the pipeline in Nushell.
|
||||
// NOTE: Please do not reorder these enum cases without thinking through the
|
||||
// impact on the PartialOrd implementation and the global sort order
|
||||
|
@ -2525,54 +2525,210 @@ impl From<Spanned<IndexMap<String, Value>>> for Value {
|
|||
}
|
||||
}
|
||||
|
||||
/// Format a duration in nanoseconds into a string
|
||||
/// Is the given year a leap year?
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
pub fn is_leap_year(year: i32) -> bool {
|
||||
(year % 4 == 0) && (year % 100 != 0 || (year % 100 == 0 && year % 400 == 0))
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum TimePeriod {
|
||||
Nanos(i64),
|
||||
Micros(i64),
|
||||
Millis(i64),
|
||||
Seconds(i64),
|
||||
Minutes(i64),
|
||||
Hours(i64),
|
||||
Days(i64),
|
||||
Weeks(i64),
|
||||
Months(i64),
|
||||
Years(i64),
|
||||
}
|
||||
|
||||
impl TimePeriod {
|
||||
fn to_text(self) -> Cow<'static, str> {
|
||||
match self {
|
||||
Self::Nanos(n) => format!("{}ns", n).into(),
|
||||
Self::Micros(n) => format!("{}µs", n).into(),
|
||||
Self::Millis(n) => format!("{}ms", n).into(),
|
||||
Self::Seconds(n) => format!("{}sec", n).into(),
|
||||
Self::Minutes(n) => format!("{}min", n).into(),
|
||||
Self::Hours(n) => format!("{}hr", n).into(),
|
||||
Self::Days(n) => format!("{}day", n).into(),
|
||||
Self::Weeks(n) => format!("{}wk", n).into(),
|
||||
Self::Months(n) => format!("{}month", n).into(),
|
||||
Self::Years(n) => format!("{}yr", n).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TimePeriod {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "{}", self.to_text())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_duration(duration: i64) -> String {
|
||||
// Attribution: most of this is taken from chrono-humanize-rs. Thanks!
|
||||
// https://gitlab.com/imp/chrono-humanize-rs/-/blob/master/src/humantime.rs
|
||||
const DAYS_IN_YEAR: i64 = 365;
|
||||
const DAYS_IN_MONTH: i64 = 30;
|
||||
|
||||
let (sign, duration) = if duration >= 0 {
|
||||
(1, duration)
|
||||
} else {
|
||||
(-1, -duration)
|
||||
};
|
||||
let (micros, nanos): (i64, i64) = (duration / 1000, duration % 1000);
|
||||
let (millis, micros): (i64, i64) = (micros / 1000, micros % 1000);
|
||||
let (secs, millis): (i64, i64) = (millis / 1000, millis % 1000);
|
||||
let (mins, secs): (i64, i64) = (secs / 60, secs % 60);
|
||||
let (hours, mins): (i64, i64) = (mins / 60, mins % 60);
|
||||
let (days, hours): (i64, i64) = (hours / 24, hours % 24);
|
||||
|
||||
let mut output_prep = vec![];
|
||||
let dur = Duration::nanoseconds(duration);
|
||||
|
||||
if days != 0 {
|
||||
output_prep.push(format!("{}day", days));
|
||||
/// Split this a duration into number of whole years and the remainder
|
||||
fn split_years(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let years = duration.num_days() / DAYS_IN_YEAR;
|
||||
let remainder = duration - Duration::days(years * DAYS_IN_YEAR);
|
||||
normalize_split(years, remainder)
|
||||
}
|
||||
|
||||
if hours != 0 {
|
||||
output_prep.push(format!("{}hr", hours));
|
||||
/// Split this a duration into number of whole months and the remainder
|
||||
fn split_months(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let months = duration.num_days() / DAYS_IN_MONTH;
|
||||
let remainder = duration - Duration::days(months * DAYS_IN_MONTH);
|
||||
normalize_split(months, remainder)
|
||||
}
|
||||
|
||||
if mins != 0 {
|
||||
output_prep.push(format!("{}min", mins));
|
||||
}
|
||||
// output 0sec for zero duration
|
||||
if duration == 0 || secs != 0 {
|
||||
output_prep.push(format!("{}sec", secs));
|
||||
/// Split this a duration into number of whole weeks and the remainder
|
||||
fn split_weeks(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let weeks = duration.num_weeks();
|
||||
let remainder = duration - Duration::weeks(weeks);
|
||||
normalize_split(weeks, remainder)
|
||||
}
|
||||
|
||||
if millis != 0 {
|
||||
output_prep.push(format!("{}ms", millis));
|
||||
/// Split this a duration into number of whole days and the remainder
|
||||
fn split_days(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let days = duration.num_days();
|
||||
let remainder = duration - Duration::days(days);
|
||||
normalize_split(days, remainder)
|
||||
}
|
||||
|
||||
if micros != 0 {
|
||||
output_prep.push(format!("{}us", micros));
|
||||
/// Split this a duration into number of whole hours and the remainder
|
||||
fn split_hours(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let hours = duration.num_hours();
|
||||
let remainder = duration - Duration::hours(hours);
|
||||
normalize_split(hours, remainder)
|
||||
}
|
||||
|
||||
if nanos != 0 {
|
||||
output_prep.push(format!("{}ns", nanos));
|
||||
/// Split this a duration into number of whole minutes and the remainder
|
||||
fn split_minutes(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let minutes = duration.num_minutes();
|
||||
let remainder = duration - Duration::minutes(minutes);
|
||||
normalize_split(minutes, remainder)
|
||||
}
|
||||
|
||||
/// Split this a duration into number of whole seconds and the remainder
|
||||
fn split_seconds(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let seconds = duration.num_seconds();
|
||||
let remainder = duration - Duration::seconds(seconds);
|
||||
normalize_split(seconds, remainder)
|
||||
}
|
||||
|
||||
/// Split this a duration into number of whole milliseconds and the remainder
|
||||
fn split_milliseconds(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let millis = duration.num_milliseconds();
|
||||
let remainder = duration - Duration::milliseconds(millis);
|
||||
normalize_split(millis, remainder)
|
||||
}
|
||||
|
||||
/// Split this a duration into number of whole seconds and the remainder
|
||||
fn split_microseconds(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let micros = duration.num_microseconds().unwrap_or_default();
|
||||
let remainder = duration - Duration::microseconds(micros);
|
||||
normalize_split(micros, remainder)
|
||||
}
|
||||
|
||||
/// Split this a duration into number of whole seconds and the remainder
|
||||
fn split_nanoseconds(duration: Duration) -> (Option<i64>, Duration) {
|
||||
let nanos = duration.num_nanoseconds().unwrap_or_default();
|
||||
let remainder = duration - Duration::nanoseconds(nanos);
|
||||
normalize_split(nanos, remainder)
|
||||
}
|
||||
|
||||
fn normalize_split(
|
||||
wholes: impl Into<Option<i64>>,
|
||||
remainder: Duration,
|
||||
) -> (Option<i64>, Duration) {
|
||||
let wholes = wholes.into().map(i64::abs).filter(|x| *x > 0);
|
||||
(wholes, remainder)
|
||||
}
|
||||
|
||||
let mut periods = vec![];
|
||||
let (years, remainder) = split_years(dur);
|
||||
if let Some(years) = years {
|
||||
periods.push(TimePeriod::Years(years));
|
||||
}
|
||||
|
||||
let (months, remainder) = split_months(remainder);
|
||||
if let Some(months) = months {
|
||||
periods.push(TimePeriod::Months(months));
|
||||
}
|
||||
|
||||
let (weeks, remainder) = split_weeks(remainder);
|
||||
if let Some(weeks) = weeks {
|
||||
periods.push(TimePeriod::Weeks(weeks));
|
||||
}
|
||||
|
||||
let (days, remainder) = split_days(remainder);
|
||||
if let Some(days) = days {
|
||||
periods.push(TimePeriod::Days(days));
|
||||
}
|
||||
|
||||
let (hours, remainder) = split_hours(remainder);
|
||||
if let Some(hours) = hours {
|
||||
periods.push(TimePeriod::Hours(hours));
|
||||
}
|
||||
|
||||
let (minutes, remainder) = split_minutes(remainder);
|
||||
if let Some(minutes) = minutes {
|
||||
periods.push(TimePeriod::Minutes(minutes));
|
||||
}
|
||||
|
||||
let (seconds, remainder) = split_seconds(remainder);
|
||||
if let Some(seconds) = seconds {
|
||||
periods.push(TimePeriod::Seconds(seconds));
|
||||
}
|
||||
|
||||
let (millis, remainder) = split_milliseconds(remainder);
|
||||
if let Some(millis) = millis {
|
||||
periods.push(TimePeriod::Millis(millis));
|
||||
}
|
||||
|
||||
let (micros, remainder) = split_microseconds(remainder);
|
||||
if let Some(micros) = micros {
|
||||
periods.push(TimePeriod::Micros(micros));
|
||||
}
|
||||
|
||||
let (nanos, _remainder) = split_nanoseconds(remainder);
|
||||
if let Some(nanos) = nanos {
|
||||
periods.push(TimePeriod::Nanos(nanos));
|
||||
}
|
||||
|
||||
if periods.is_empty() {
|
||||
periods.push(TimePeriod::Seconds(0));
|
||||
}
|
||||
|
||||
// let last = periods.pop().map(|last| last.to_text().to_string());
|
||||
let text = periods
|
||||
.into_iter()
|
||||
.map(|p| p.to_text().to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
// if let Some(last) = last {
|
||||
// text.push(format!("and {}", last));
|
||||
// }
|
||||
|
||||
format!(
|
||||
"{}{}",
|
||||
if sign == -1 { "-" } else { "" },
|
||||
output_prep.join(" ")
|
||||
text.join(" ").trim()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ pub enum Unit {
|
|||
Gigabyte,
|
||||
Terabyte,
|
||||
Petabyte,
|
||||
Exabyte,
|
||||
Zettabyte,
|
||||
|
||||
// Filesize units: ISO/IEC 80000
|
||||
Kibibyte,
|
||||
|
@ -16,6 +18,8 @@ pub enum Unit {
|
|||
Gibibyte,
|
||||
Tebibyte,
|
||||
Pebibyte,
|
||||
Exbibyte,
|
||||
Zebibyte,
|
||||
|
||||
// Duration units
|
||||
Nanosecond,
|
||||
|
@ -26,4 +30,7 @@ pub enum Unit {
|
|||
Hour,
|
||||
Day,
|
||||
Week,
|
||||
Month,
|
||||
Year,
|
||||
Decade,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue