mirror of
https://github.com/nushell/nushell.git
synced 2025-06-19 00:17:02 +02:00
Bugfix/into datetime ignores timezone with format (#15370)
Close #15119 when this is merged # Description > Note: my locale is +1 **Before the changes 🔴**  See the issue for more detailed description of the problem. **After the changes 🟢**  # User-Facing Changes The ``into datetime`` command will now work with formatting and time zones or offset together # Tests + Formatting Fmt + clippy OK **Note about the tests I added**: those tests don't really test my changes, as they were already passing before my changes. Nevertheless I thought I could push them # After Submitting I don't think anything is necessary
This commit is contained in:
parent
3030608de0
commit
2bad1371f0
@ -18,4 +18,4 @@ A base crate is one with minimal dependencies in our system so that other develo
|
||||
|
||||
### Background on nu-cmd-lang
|
||||
|
||||
This crate was designed to be a small, concise set of tools or commands that serve as the *foundation layer* of both nu and nushell. These are the core commands needed to have a nice working version of the *nu language* without all of the support that the other commands provide inside nushell. Prior to the launch of this crate all of our commands were housed in the crate *nu-command*. Moving forward we would like to *slowly* break out the commands in nu-command into different crates; the naming and how this will work and where all the commands will be located is a "work in progress" especially now that the *standard library* is starting to become more popular as a location for commands. As time goes on some of our commands written in rust will be migrated to nu and when this happens they will be moved into the *standard library*.
|
||||
This crate was designed to be a small, concise set of tools or commands that serve as the *foundation layer* of both nu and nushell. These are the core commands needed to have a nice working version of the *nu language* without all of the support that the other commands provide inside nushell. Prior to the launch of this crate all of our commands were housed in the crate *nu-command*. Moving forward we would like to *slowly* break out the commands in nu-command into different crates; the naming and how this will work and where all the commands will be located is a "work in progress" especially now that the *standard library* is starting to become more popular as a location for commands. As time goes on some of our commands written in rust will be migrated to nu and when this happens they will be moved into the *standard library*.
|
||||
|
@ -4,6 +4,9 @@ use human_date_parser::{from_human_time, ParseResult};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
const HOUR: i32 = 60 * 60;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Arguments {
|
||||
zone_options: Option<Spanned<Zone>>,
|
||||
format_options: Option<DatetimeFormat>,
|
||||
@ -272,7 +275,7 @@ impl Command for IntoDatetime {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct DatetimeFormat(String);
|
||||
|
||||
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
@ -322,7 +325,6 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
};
|
||||
}
|
||||
}
|
||||
const HOUR: i32 = 60 * 60;
|
||||
|
||||
// Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?)
|
||||
let timestamp = match input {
|
||||
@ -403,10 +405,56 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
|
||||
let parse_as_string = |val: &str| {
|
||||
match dateformat {
|
||||
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
|
||||
Ok(d) => Value::date ( d, head ),
|
||||
Some(dt_format) => match DateTime::parse_from_str(val, &dt_format.0) {
|
||||
Ok(dt) => {
|
||||
match timezone {
|
||||
None => {
|
||||
Value::date ( dt, head )
|
||||
},
|
||||
Some(Spanned { item, span }) => match item {
|
||||
Zone::Utc => {
|
||||
Value::date ( dt, head )
|
||||
}
|
||||
Zone::Local => {
|
||||
Value::date(dt.with_timezone(&Local).into(), *span)
|
||||
}
|
||||
Zone::East(i) => match FixedOffset::east_opt((*i as i32) * HOUR) {
|
||||
Some(eastoffset) => {
|
||||
Value::date(dt.with_timezone(&eastoffset), *span)
|
||||
}
|
||||
None => Value::error(
|
||||
ShellError::DatetimeParseError {
|
||||
msg: input.to_abbreviated_string(&nu_protocol::Config::default()),
|
||||
span: *span,
|
||||
},
|
||||
*span,
|
||||
),
|
||||
},
|
||||
Zone::West(i) => match FixedOffset::west_opt((*i as i32) * HOUR) {
|
||||
Some(westoffset) => {
|
||||
Value::date(dt.with_timezone(&westoffset), *span)
|
||||
}
|
||||
None => Value::error(
|
||||
ShellError::DatetimeParseError {
|
||||
msg: input.to_abbreviated_string(&nu_protocol::Config::default()),
|
||||
span: *span,
|
||||
},
|
||||
*span,
|
||||
),
|
||||
},
|
||||
Zone::Error => Value::error(
|
||||
// This is an argument error, not an input error
|
||||
ShellError::TypeMismatch {
|
||||
err_message: "Invalid timezone or offset".to_string(),
|
||||
span: *span,
|
||||
},
|
||||
*span,
|
||||
),
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(reason) => {
|
||||
match NaiveDateTime::parse_from_str(val, &dt.0) {
|
||||
match NaiveDateTime::parse_from_str(val, &dt_format.0) {
|
||||
Ok(d) => {
|
||||
let dt_fixed =
|
||||
Local.from_local_datetime(&d).single().unwrap_or_default();
|
||||
@ -415,7 +463,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
}
|
||||
Err(_) => {
|
||||
Value::error (
|
||||
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
|
||||
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt_format.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
|
||||
head,
|
||||
)
|
||||
}
|
||||
@ -629,6 +677,49 @@ mod tests {
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn takes_timestamp_offset_as_int_with_formatting() {
|
||||
let date_int = Value::test_int(1_614_434_140);
|
||||
let timezone_option = Some(Spanned {
|
||||
item: Zone::East(8),
|
||||
span: Span::test_data(),
|
||||
});
|
||||
let fmt_options = Some(DatetimeFormat("%s".to_string()));
|
||||
let args = Arguments {
|
||||
zone_options: timezone_option,
|
||||
format_options: fmt_options,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_int, &args, Span::test_data());
|
||||
let expected = Value::date(
|
||||
DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z").unwrap(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn takes_timestamp_offset_as_int_with_local_timezone() {
|
||||
let date_int = Value::test_int(1_614_434_140);
|
||||
let timezone_option = Some(Spanned {
|
||||
item: Zone::Local,
|
||||
span: Span::test_data(),
|
||||
});
|
||||
let fmt_options = Some(DatetimeFormat("%s".to_string()));
|
||||
let args = Arguments {
|
||||
zone_options: timezone_option,
|
||||
format_options: fmt_options,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_int, &args, Span::test_data());
|
||||
let expected = Value::date(
|
||||
Utc.timestamp_opt(1_614_434_140, 0).unwrap().into(),
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn takes_timestamp() {
|
||||
let date_str = Value::test_string("1614434140000000000");
|
||||
@ -643,7 +734,7 @@ mod tests {
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::date(
|
||||
Local.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||
Local.timestamp_opt(1_614_434_140, 0).unwrap().into(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
@ -662,7 +753,7 @@ mod tests {
|
||||
cell_paths: None,
|
||||
};
|
||||
let expected = Value::date(
|
||||
Local.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||
Local.timestamp_opt(1_614_434_140, 0).unwrap().into(),
|
||||
Span::test_data(),
|
||||
);
|
||||
let actual = action(&expected, &args, Span::test_data());
|
||||
@ -681,7 +772,7 @@ mod tests {
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
|
||||
let expected = Value::date(
|
||||
Utc.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||
Utc.timestamp_opt(1_614_434_140, 0).unwrap().into(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user