mirror of
https://github.com/nushell/nushell
synced 2024-12-28 14:03:09 +00:00
parse most common date formats using dtparse crate (#2303)
* use dtparse crate to parse most common date formats * use dtparse crate to parse most common date formats - cargo fmt
This commit is contained in:
parent
e1c5940b04
commit
6202705eb6
2 changed files with 72 additions and 23 deletions
|
@ -34,8 +34,9 @@ codespan-reporting = "0.9.5"
|
||||||
csv = "1.1"
|
csv = "1.1"
|
||||||
ctrlc = {version = "3.1.4", optional = true}
|
ctrlc = {version = "3.1.4", optional = true}
|
||||||
derive-new = "0.5.8"
|
derive-new = "0.5.8"
|
||||||
directories = {version = "2.0.2", optional = true}
|
directories = {version = "2.0.2", optional = true}
|
||||||
dirs = {version = "2.0.2", optional = true}
|
dirs = {version = "2.0.2", optional = true}
|
||||||
|
dtparse = "1.1.0"
|
||||||
dunce = "1.0.1"
|
dunce = "1.0.1"
|
||||||
eml-parser = "0.1.0"
|
eml-parser = "0.1.0"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
|
|
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||||
use nu_source::{Tag, Tagged};
|
use nu_source::{Tag, Tagged};
|
||||||
use nu_value_ext::ValueExt;
|
use nu_value_ext::ValueExt;
|
||||||
|
|
||||||
use chrono::DateTime;
|
use chrono::{DateTime, FixedOffset, LocalResult, Offset, TimeZone};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
|
@ -51,11 +51,23 @@ impl WholeStreamCommand for SubCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Convert to datetime",
|
Example {
|
||||||
example: "echo '16.11.1984 8:00 am +0000' | str to-datetime",
|
description: "Convert to datetime",
|
||||||
result: None,
|
example: "echo '16.11.1984 8:00 am +0000' | str to-datetime",
|
||||||
}]
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert to datetime",
|
||||||
|
example: "echo '2020-08-04T16:39:18+00:00' | str to-datetime",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert to datetime using a custom format",
|
||||||
|
example: "echo '20200904_163918+0000' | str to-datetime -f '%Y%m%d_%H%M%S%z'",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,9 +85,9 @@ async fn operate(
|
||||||
let column_paths: Vec<_> = rest;
|
let column_paths: Vec<_> = rest;
|
||||||
|
|
||||||
let options = if let Some(Tagged { item: fmt, .. }) = format {
|
let options = if let Some(Tagged { item: fmt, .. }) = format {
|
||||||
DatetimeFormat(fmt)
|
Some(DatetimeFormat(fmt))
|
||||||
} else {
|
} else {
|
||||||
DatetimeFormat(String::from("%d.%m.%Y %H:%M %P %z"))
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(input
|
Ok(input
|
||||||
|
@ -102,23 +114,49 @@ async fn operate(
|
||||||
|
|
||||||
fn action(
|
fn action(
|
||||||
input: &Value,
|
input: &Value,
|
||||||
options: &DatetimeFormat,
|
options: &Option<DatetimeFormat>,
|
||||||
tag: impl Into<Tag>,
|
tag: impl Into<Tag>,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
match &input.value {
|
match &input.value {
|
||||||
UntaggedValue::Primitive(Primitive::Line(s))
|
UntaggedValue::Primitive(Primitive::Line(s))
|
||||||
| UntaggedValue::Primitive(Primitive::String(s)) => {
|
| UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||||
let dt = &options.0;
|
let out = match options {
|
||||||
|
Some(dt) => match DateTime::parse_from_str(s, &dt.0) {
|
||||||
let out = match DateTime::parse_from_str(s, dt) {
|
Ok(d) => UntaggedValue::date(d),
|
||||||
Ok(d) => UntaggedValue::date(d),
|
Err(reason) => {
|
||||||
Err(reason) => {
|
return Err(ShellError::labeled_error(
|
||||||
return Err(ShellError::labeled_error(
|
format!("could not parse as datetime using format '{}'", dt.0),
|
||||||
"could not parse as datetime",
|
reason.to_string(),
|
||||||
reason.to_string(),
|
tag.into().span,
|
||||||
tag.into().span,
|
))
|
||||||
))
|
}
|
||||||
}
|
},
|
||||||
|
None => match dtparse::parse(s) {
|
||||||
|
Ok((native_dt, fixed_offset)) => {
|
||||||
|
let offset = match fixed_offset {
|
||||||
|
Some(fo) => fo,
|
||||||
|
None => FixedOffset::east(0).fix(),
|
||||||
|
};
|
||||||
|
match offset.from_local_datetime(&native_dt) {
|
||||||
|
LocalResult::Single(d) => UntaggedValue::date(d),
|
||||||
|
LocalResult::Ambiguous(d, _) => UntaggedValue::date(d),
|
||||||
|
LocalResult::None => {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"could not convert to a timezone-aware datetime",
|
||||||
|
"local time representation is invalid",
|
||||||
|
tag.into().span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(reason) => {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"could not parse as datetime",
|
||||||
|
reason.to_string(),
|
||||||
|
tag.into().span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(out.into_value(tag))
|
Ok(out.into_value(tag))
|
||||||
|
@ -152,7 +190,7 @@ mod tests {
|
||||||
fn takes_a_date_format() {
|
fn takes_a_date_format() {
|
||||||
let date_str = string("16.11.1984 8:00 am +0000");
|
let date_str = string("16.11.1984 8:00 am +0000");
|
||||||
|
|
||||||
let fmt_options = DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string());
|
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
||||||
|
|
||||||
let actual = action(&date_str, &fmt_options, Tag::unknown()).unwrap();
|
let actual = action(&date_str, &fmt_options, Tag::unknown()).unwrap();
|
||||||
|
|
||||||
|
@ -162,11 +200,21 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn takes_iso8601_date_format() {
|
||||||
|
let date_str = string("2020-08-04T16:39:18+00:00");
|
||||||
|
let actual = action(&date_str, &None, Tag::unknown()).unwrap();
|
||||||
|
match actual.value {
|
||||||
|
UntaggedValue::Primitive(Primitive::Date(_)) => {}
|
||||||
|
_ => panic!("Didn't convert to date"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
|
fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
|
||||||
let date_str = string("16.11.1984 8:00 am Oops0000");
|
let date_str = string("16.11.1984 8:00 am Oops0000");
|
||||||
|
|
||||||
let fmt_options = DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string());
|
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
||||||
|
|
||||||
let actual = action(&date_str, &fmt_options, Tag::unknown());
|
let actual = action(&date_str, &fmt_options, Tag::unknown());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue