mirror of
https://github.com/nushell/nushell.git
synced 2025-05-05 15:32:56 +00:00
Bugfix: datetime parsing and local timezones (#15544)
Hi, This PR should close 3 issues - [DMY date format is parsed inconsistently #14123](https://github.com/nushell/nushell/issues/14123) - [into datetime doesnt't work with --format and ignores user's locale #11015](https://github.com/nushell/nushell/issues/11015) - [into datetime: iinconsistent and incrrect behaviour regarding timezones #13823](https://github.com/nushell/nushell/issues/13823) # Description - Allow to parse only dates or only times with --format - Use local timezone depending on the input. Ex: I'm in France, so show dates with +0100 in winter and +0200 in summer. ```nushell # Concerning #13823 > "2020-01-01 12:00" | into datetime Wed, 1 Jan 2020 12:00:00 +0100 (5 years ago) # OK, it's my timezone in winter time > "2020-06-01 12:00" | into datetime Mon, 1 Jun 2020 12:00:00 +0200 (4 years ago) # OK, it's my timezone in summertime > ("2024-10-27 12:00" | into datetime) - ("2024-10-27 00:00" | into datetime) 13hr # Ok, because we switched from summer to winter time on 2025-10-27, so there are actually 13h between midnight and noon > "2020-01-01 12:00" | into datetime --format "%Y-%m-%d %H:%M" Wed, 1 Jan 2020 12:00:00 +0100 (5 years ago) # OK: timezone is assumed to be local, and +0100 is my timezone in winter # Concerning #14123 and #11015 # Flexible parsing still works like before, which could be counter-intuitive, but it's flexible parsing # with one difference: the timezone is local > '12-01-2001' | into datetime Sat, 1 Dec 2001 00:00:00 +0100 (23 years ago) # OK, +0100 is my timezone in winter time. If I run it with nushell 0.103.0 in summer time, I get +0200 > '13-01-2001' | into datetime Sat, 13 Jan 2001 00:00:00 +0100 (24 years ago) ## If you want, you can use the --format option to parse a date or a time (before, it had to be a date + time) ## Notice here again the timezone is correct depending on winter/summer time ~> "06.03.2023" | into datetime -f "%d.%m.%Y" Mon, 6 Mar 2023 00:00:00 +0100 (2 years ago) ~> "06.03.2023" | into datetime -f "%m.%d.%Y" Sat, 3 Jun 2023 00:00:00 +0200 (2 years ago) > "10:00" | into datetime --format "%H:%M" Thu, 10 Apr 2025 10:00:00 +0200 (9 hours ago) ``` # User-Facing Changes See above # Tests + Formatting # After Submitting I'll down something for the release notes, if this is merged in time 😄
This commit is contained in:
parent
61dbcf3de6
commit
39edd7e080
@ -458,13 +458,8 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
match NaiveDateTime::parse_from_str(val, &dt_format.item.0) {
|
match parse_with_format(val, &dt_format.item.0, head) {
|
||||||
Ok(d) => {
|
Ok(parsed) => parsed,
|
||||||
let dt_fixed =
|
|
||||||
Local.from_local_datetime(&d).single().unwrap_or_default();
|
|
||||||
|
|
||||||
Value::date(dt_fixed.into(),head)
|
|
||||||
}
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Value::error (
|
Value::error (
|
||||||
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt_format.item.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.item.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()) },
|
||||||
@ -808,6 +803,34 @@ fn parse_timezone_from_record(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_with_format(val: &str, fmt: &str, head: Span) -> Result<Value, ()> {
|
||||||
|
// try parsing at date + time
|
||||||
|
if let Ok(dt) = NaiveDateTime::parse_from_str(val, fmt) {
|
||||||
|
let dt_native = Local.from_local_datetime(&dt).single().unwrap_or_default();
|
||||||
|
return Ok(Value::date(dt_native.into(), head));
|
||||||
|
}
|
||||||
|
|
||||||
|
// try parsing at date only
|
||||||
|
if let Ok(date) = NaiveDate::parse_from_str(val, fmt) {
|
||||||
|
if let Some(dt) = date.and_hms_opt(0, 0, 0) {
|
||||||
|
let dt_native = Local.from_local_datetime(&dt).single().unwrap_or_default();
|
||||||
|
return Ok(Value::date(dt_native.into(), head));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try parsing at time only
|
||||||
|
if let Ok(time) = NaiveTime::parse_from_str(val, fmt) {
|
||||||
|
let now = Local::now().naive_local().date();
|
||||||
|
let dt_native = Local
|
||||||
|
.from_local_datetime(&now.and_time(time))
|
||||||
|
.single()
|
||||||
|
.unwrap_or_default();
|
||||||
|
return Ok(Value::date(dt_native.into(), head));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -9,7 +9,11 @@ pub(crate) fn parse_date_from_string(
|
|||||||
Ok((native_dt, fixed_offset)) => {
|
Ok((native_dt, fixed_offset)) => {
|
||||||
let offset = match fixed_offset {
|
let offset = match fixed_offset {
|
||||||
Some(offset) => offset,
|
Some(offset) => offset,
|
||||||
None => *(Local::now().offset()),
|
None => *Local
|
||||||
|
.from_local_datetime(&native_dt)
|
||||||
|
.single()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.offset(),
|
||||||
};
|
};
|
||||||
match offset.from_local_datetime(&native_dt) {
|
match offset.from_local_datetime(&native_dt) {
|
||||||
LocalResult::Single(d) => Ok(d),
|
LocalResult::Single(d) => Ok(d),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user