From 08449e174c0eb408251d872dea3f9848198dd3e1 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Sun, 25 Jun 2023 12:53:17 -0600 Subject: [PATCH] Format negative datetimes with rfc3339 (#9501) (#9502) - fixes #9501 # Description `chrono::Datetime::to_rfc2822()` panics when it would format a negative year. 3339 does not. This makes negative-year datetimes inconsistent compared to positive ones, but it's better than a panic. --- crates/nu-protocol/src/value/mod.rs | 54 +++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 560a99d41..f8b4cd37c 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -11,8 +11,9 @@ use crate::ast::{Math, Operator}; use crate::engine::EngineState; use crate::ShellError; use crate::{did_you_mean, BlockId, Config, Span, Spanned, Type, VarId}; + use byte_unit::ByteUnit; -use chrono::{DateTime, Duration, FixedOffset, Locale, TimeZone}; +use chrono::{DateTime, Datelike, Duration, FixedOffset, Locale, TimeZone}; use chrono_humanize::HumanTime; pub use custom_value::CustomValue; use fancy_regex::Regex; @@ -536,10 +537,19 @@ impl Value { Value::Float { val, .. } => val.to_string(), Value::Filesize { val, .. } => format_filesize_from_conf(*val, config), Value::Duration { val, .. } => format_duration(*val), + Value::Date { val, .. } => match &config.datetime_normal_format { Some(format) => self.format_datetime(val, format), None => { - format!("{} ({})", val.to_rfc2822(), HumanTime::from(*val)) + format!( + "{} ({})", + if val.year() >= 0 { + val.to_rfc2822() + } else { + val.to_rfc3339() + }, + HumanTime::from(*val), + ) } }, Value::Range { val, .. } => { @@ -3889,4 +3899,44 @@ mod tests { ); } } + + mod into_string { + use chrono::{DateTime, FixedOffset, NaiveDateTime}; + + use super::*; + + #[test] + fn test_datetime() { + let string = Value::Date { + val: DateTime::from_utc( + NaiveDateTime::from_timestamp_millis(-123456789).unwrap(), + FixedOffset::east_opt(0).unwrap(), + ), + span: Span::unknown(), + } + .into_string("", &Default::default()); + + // We need to cut the humanized part off for tests to work, because + // it is relative to current time. + let formatted = string.split("(").next().unwrap(); + assert_eq!("Tue, 30 Dec 1969 13:42:23 +0000 ", formatted); + } + + #[test] + fn test_negative_year_datetime() { + let string = Value::Date { + val: DateTime::from_utc( + NaiveDateTime::from_timestamp_millis(-72135596800000).unwrap(), + FixedOffset::east_opt(0).unwrap(), + ), + span: Span::unknown(), + } + .into_string("", &Default::default()); + + // We need to cut the humanized part off for tests to work, because + // it is relative to current time. + let formatted = string.split(" ").next().unwrap(); + assert_eq!("-0316-02-11T06:13:20+00:00", formatted); + } + } }