From 52f646d8db9266a0d5aaeba5877576f9b3bbf746 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 10 Oct 2024 06:38:26 -0500 Subject: [PATCH] fix format date by getting the env vars properly (#14037) # Description This PR is from a [discussion in Discord](https://discord.com/channels/601130461678272522/988303282931912704/1292900183742611466). The gist is that `format date` didn't respect the $env.LC_TIME env var. The reason for this is because it was using std::env::var which doesn't understand nushell's env. Now, this should work. ![image](https://github.com/user-attachments/assets/e4d494b1-9f2b-4993-9729-244e0c47ef0c) # User-Facing Changes # Tests + Formatting # After Submitting --- crates/nu-command/src/strings/format/date.rs | 78 ++++++++++++++------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/crates/nu-command/src/strings/format/date.rs b/crates/nu-command/src/strings/format/date.rs index fd398753d70..658823aa6ac 100644 --- a/crates/nu-command/src/strings/format/date.rs +++ b/crates/nu-command/src/strings/format/date.rs @@ -88,7 +88,25 @@ impl Command for FormatDate { ) -> Result { let list = call.has_flag(engine_state, stack, "list")?; let format = call.opt::>(engine_state, stack, 0)?; - run(engine_state, call, input, list, format) + + // get the locale first so we can use the proper get_env_var functions since this is a const command + // we can override the locale by setting $env.NU_TEST_LOCALE_OVERRIDE or $env.LC_TIME + let locale = if let Some(loc) = engine_state + .get_env_var(LOCALE_OVERRIDE_ENV_VAR) + .or_else(|| engine_state.get_env_var("LC_TIME")) + { + let locale_str = loc.as_str()?.split('.').next().unwrap_or("en_US"); + locale_str.try_into().unwrap_or(Locale::en_US) + } else { + get_system_locale_string() + .map(|l| l.replace('-', "_")) + .unwrap_or_else(|| String::from("en_US")) + .as_str() + .try_into() + .unwrap_or(Locale::en_US) + }; + + run(engine_state, call, input, list, format, locale) } fn run_const( @@ -99,7 +117,25 @@ impl Command for FormatDate { ) -> Result { let list = call.has_flag_const(working_set, "list")?; let format = call.opt_const::>(working_set, 0)?; - run(working_set.permanent(), call, input, list, format) + + // get the locale first so we can use the proper get_env_var functions since this is a const command + // we can override the locale by setting $env.NU_TEST_LOCALE_OVERRIDE or $env.LC_TIME + let locale = if let Some(loc) = working_set + .get_env_var(LOCALE_OVERRIDE_ENV_VAR) + .or_else(|| working_set.get_env_var("LC_TIME")) + { + let locale_str = loc.as_str()?.split('.').next().unwrap_or("en_US"); + locale_str.try_into().unwrap_or(Locale::en_US) + } else { + get_system_locale_string() + .map(|l| l.replace('-', "_")) + .unwrap_or_else(|| String::from("en_US")) + .as_str() + .try_into() + .unwrap_or(Locale::en_US) + }; + + run(working_set.permanent(), call, input, list, format, locale) } } @@ -109,6 +145,7 @@ fn run( input: PipelineData, list: bool, format: Option>, + locale: Locale, ) -> Result { let head = call.head; if list { @@ -124,34 +161,23 @@ fn run( } input.map( move |value| match &format { - Some(format) => format_helper(value, format.item.as_str(), format.span, head), + Some(format) => format_helper(value, format.item.as_str(), format.span, head, locale), None => format_helper_rfc2822(value, head), }, engine_state.signals(), ) } -fn format_from(date_time: DateTime, formatter: &str, span: Span) -> Value +fn format_from( + date_time: DateTime, + formatter: &str, + span: Span, + locale: Locale, +) -> Value where Tz::Offset: Display, { let mut formatter_buf = String::new(); - // Format using locale LC_TIME - let locale = if let Ok(l) = - std::env::var(LOCALE_OVERRIDE_ENV_VAR).or_else(|_| std::env::var("LC_TIME")) - { - let locale_str = l.split('.').next().unwrap_or("en_US"); - locale_str.try_into().unwrap_or(Locale::en_US) - } else { - // LC_ALL > LC_CTYPE > LANG - // Not locale present, default to en_US - get_system_locale_string() - .map(|l| l.replace('-', "_")) // `chrono::Locale` needs something like `xx_xx`, rather than `xx-xx` - .unwrap_or_else(|| String::from("en_US")) - .as_str() - .try_into() - .unwrap_or(Locale::en_US) - }; let format = date_time.format_localized(formatter, locale); match formatter_buf.write_fmt(format_args!("{format}")) { @@ -166,14 +192,20 @@ where } } -fn format_helper(value: Value, formatter: &str, formatter_span: Span, head_span: Span) -> Value { +fn format_helper( + value: Value, + formatter: &str, + formatter_span: Span, + head_span: Span, + locale: Locale, +) -> Value { match value { - Value::Date { val, .. } => format_from(val, formatter, formatter_span), + Value::Date { val, .. } => format_from(val, formatter, formatter_span, locale), Value::String { val, .. } => { let dt = parse_date_from_string(&val, formatter_span); match dt { - Ok(x) => format_from(x, formatter, formatter_span), + Ok(x) => format_from(x, formatter, formatter_span, locale), Err(e) => e, } }