Switch duration back to bigint (#3554)

This commit is contained in:
JT
2021-06-04 19:39:12 +12:00
committed by GitHub
parent 7d78f40bf6
commit 4bca36f479
7 changed files with 66 additions and 35 deletions

View File

@ -45,7 +45,7 @@ pub enum Primitive {
/// A date value
Date(DateTime<FixedOffset>),
/// A count in the number of nanoseconds
Duration(i64),
Duration(BigInt),
/// A range of values
Range(Box<Range>),
/// A file path
@ -171,7 +171,13 @@ impl Primitive {
"converting a decimal into a signed 64-bit integer",
)
}),
Primitive::Duration(duration) => Ok(*duration),
Primitive::Duration(duration) => duration.to_i64().ok_or_else(|| {
ShellError::range_error(
ExpectedRange::I64,
&format!("{}", duration).spanned(span),
"converting a duration into a signed 64-bit integer",
)
}),
other => Err(ShellError::type_error(
"number",
other.type_name().spanned(span),
@ -277,7 +283,10 @@ impl Primitive {
match self {
Primitive::Duration(duration) => {
// Divide into seconds because BigInt can be larger than i64
let (secs, nanos) = duration.div_rem(&(NANOS_PER_SEC as i64));
let (secs, nanos) = duration.div_rem(
&BigInt::from_u32(NANOS_PER_SEC)
.expect("Internal error: conversion from u32 failed"),
);
let secs = match secs.to_i64() {
Some(secs) => secs,
None => {
@ -396,7 +405,10 @@ impl From<chrono::Duration> for Primitive {
.expect("Unexpected overflow")
.num_nanoseconds()
.expect("Unexpected overflow") as u32;
Primitive::Duration(secs * NANOS_PER_SEC as i64 + nanos as i64)
Primitive::Duration(
BigInt::from_i64(secs * NANOS_PER_SEC as i64 + nanos as i64)
.expect("Internal error: can't convert from i64"),
)
}
}
@ -513,20 +525,24 @@ pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> S
}
/// Format a duration in nanoseconds into a string
pub fn format_duration(duration: &i64) -> String {
pub fn format_duration(duration: &BigInt) -> String {
let is_zero = duration.is_zero();
// FIXME: This involves a lot of allocation, but it seems inevitable with BigInt.
let big_int_1000 = BigInt::from(1000);
let big_int_60 = BigInt::from(60);
let big_int_24 = BigInt::from(24);
// We only want the biggest subdivision to have the negative sign.
let (sign, duration) = if duration.is_zero() || duration.is_positive() {
(1, *duration)
(1, duration.clone())
} else {
(-1, -duration)
};
let (micros, nanos): (i64, i64) = duration.div_rem(&1000);
let (millis, micros): (i64, i64) = micros.div_rem(&1000);
let (secs, millis): (i64, i64) = millis.div_rem(&1000);
let (mins, secs): (i64, i64) = secs.div_rem(&60);
let (hours, mins): (i64, i64) = mins.div_rem(&60);
let (days, hours): (i64, i64) = hours.div_rem(&24);
let (micros, nanos): (BigInt, BigInt) = duration.div_rem(&big_int_1000);
let (millis, micros): (BigInt, BigInt) = micros.div_rem(&big_int_1000);
let (secs, millis): (BigInt, BigInt) = millis.div_rem(&big_int_1000);
let (mins, secs): (BigInt, BigInt) = secs.div_rem(&big_int_60);
let (hours, mins): (BigInt, BigInt) = mins.div_rem(&big_int_60);
let (days, hours): (BigInt, BigInt) = hours.div_rem(&big_int_24);
let mut output_prep = vec![];