mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
Date utility commands (#2780)
* updated & added date related commands based on the new design * added proper error handling when date format string is invalid * fixed format issue * fixed an issue caused due to the change in primitive Date type * added `date list-timezone` command to list all supported time zones and updated `date to-timezone` accordingly
This commit is contained in:
parent
e000ed47cd
commit
83c874666a
21 changed files with 538 additions and 187 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -3144,6 +3144,7 @@ dependencies = [
|
||||||
"bytes 0.5.6",
|
"bytes 0.5.6",
|
||||||
"calamine",
|
"calamine",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"chrono-tz",
|
||||||
"clap",
|
"clap",
|
||||||
"clipboard",
|
"clipboard",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
|
@ -3217,6 +3218,7 @@ dependencies = [
|
||||||
"term",
|
"term",
|
||||||
"term_size",
|
"term_size",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
|
"titlecase",
|
||||||
"toml",
|
"toml",
|
||||||
"trash",
|
"trash",
|
||||||
"umask",
|
"umask",
|
||||||
|
@ -5624,6 +5626,16 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "titlecase"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f565e410cfc24c2f2a89960b023ca192689d7f77d3f8d4f4af50c2d8affe1117"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static 1.4.0",
|
||||||
|
"regex 1.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "0.1.22"
|
version = "0.1.22"
|
||||||
|
|
|
@ -31,6 +31,7 @@ byte-unit = "4.0.9"
|
||||||
bytes = "0.5.6"
|
bytes = "0.5.6"
|
||||||
calamine = "0.16.1"
|
calamine = "0.16.1"
|
||||||
chrono = {version = "0.4.15", features = ["serde"]}
|
chrono = {version = "0.4.15", features = ["serde"]}
|
||||||
|
chrono-tz = "0.5.3"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
clipboard = {version = "0.5.0", optional = true}
|
clipboard = {version = "0.5.0", optional = true}
|
||||||
codespan-reporting = "0.9.5"
|
codespan-reporting = "0.9.5"
|
||||||
|
@ -91,6 +92,7 @@ tempfile = "3.1.0"
|
||||||
term = {version = "0.6.1", optional = true}
|
term = {version = "0.6.1", optional = true}
|
||||||
term_size = "0.3.2"
|
term_size = "0.3.2"
|
||||||
termcolor = "1.1.0"
|
termcolor = "1.1.0"
|
||||||
|
titlecase = "1.0"
|
||||||
toml = "0.5.6"
|
toml = "0.5.6"
|
||||||
trash = {version = "1.2.0", optional = true}
|
trash = {version = "1.2.0", optional = true}
|
||||||
unicode-segmentation = "1.6.0"
|
unicode-segmentation = "1.6.0"
|
||||||
|
|
|
@ -86,8 +86,10 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||||
whole_stream_command(Touch),
|
whole_stream_command(Touch),
|
||||||
whole_stream_command(Cpy),
|
whole_stream_command(Cpy),
|
||||||
whole_stream_command(Date),
|
whole_stream_command(Date),
|
||||||
|
whole_stream_command(DateListTimeZone),
|
||||||
whole_stream_command(DateNow),
|
whole_stream_command(DateNow),
|
||||||
whole_stream_command(DateUTC),
|
whole_stream_command(DateToTable),
|
||||||
|
whole_stream_command(DateToTimeZone),
|
||||||
whole_stream_command(DateFormat),
|
whole_stream_command(DateFormat),
|
||||||
whole_stream_command(Cal),
|
whole_stream_command(Cal),
|
||||||
whole_stream_command(Mkdir),
|
whole_stream_command(Mkdir),
|
||||||
|
|
|
@ -154,7 +154,7 @@ pub(crate) use config::{
|
||||||
};
|
};
|
||||||
pub(crate) use count::Count;
|
pub(crate) use count::Count;
|
||||||
pub(crate) use cp::Cpy;
|
pub(crate) use cp::Cpy;
|
||||||
pub(crate) use date::{Date, DateFormat, DateNow, DateUTC};
|
pub(crate) use date::{Date, DateFormat, DateListTimeZone, DateNow, DateToTable, DateToTimeZone};
|
||||||
pub(crate) use debug::Debug;
|
pub(crate) use debug::Debug;
|
||||||
pub(crate) use default::Default;
|
pub(crate) use default::Default;
|
||||||
pub(crate) use describe::Describe;
|
pub(crate) use describe::Describe;
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
|
|
||||||
use crate::commands::date::utils::{date_to_value, date_to_value_raw};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
use crate::prelude::*;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{
|
||||||
|
Dictionary, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||||
|
};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
|
use std::fmt::{self, write};
|
||||||
|
|
||||||
pub struct Date;
|
pub struct Date;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct FormatArgs {
|
pub struct FormatArgs {
|
||||||
format: Tagged<String>,
|
format: Tagged<String>,
|
||||||
raw: Option<bool>,
|
table: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -24,11 +24,11 @@ impl WholeStreamCommand for Date {
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("date format")
|
Signature::build("date format")
|
||||||
.required("format", SyntaxShape::String, "strftime format")
|
.required("format", SyntaxShape::String, "strftime format")
|
||||||
.switch("raw", "print date without tables", Some('r'))
|
.switch("table", "print date in a table", Some('t'))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"format the current date using the given format string."
|
"Format a given date using the given format string."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(
|
||||||
|
@ -38,6 +38,21 @@ impl WholeStreamCommand for Date {
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
format(args, registry).await
|
format(args, registry).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Format the current date",
|
||||||
|
example: "date now | date format '%Y.%m.%d_%H %M %S,%z'",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Format the current date and print in a table",
|
||||||
|
example: "date now | date format -t '%Y-%m-%d_%H:%M:%S %z'",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn format(
|
pub async fn format(
|
||||||
|
@ -46,20 +61,46 @@ pub async fn format(
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
let (FormatArgs { format, raw }, _) = args.process(®istry).await?;
|
let (FormatArgs { format, table }, input) = args.process(®istry).await?;
|
||||||
|
|
||||||
let dt_fmt = format.to_string();
|
Ok(input
|
||||||
|
.map(move |value| match value {
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Primitive(Primitive::Date(dt)),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut output = String::new();
|
||||||
|
if let Err(fmt::Error) =
|
||||||
|
write(&mut output, format_args!("{}", dt.format(&format.item)))
|
||||||
|
{
|
||||||
|
Err(ShellError::labeled_error(
|
||||||
|
"The date format is invalid",
|
||||||
|
"invalid strftime format",
|
||||||
|
&format.tag,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let value = if table {
|
||||||
|
let mut indexmap = IndexMap::new();
|
||||||
|
indexmap.insert(
|
||||||
|
"formatted".to_string(),
|
||||||
|
UntaggedValue::string(&output).into_value(&tag),
|
||||||
|
);
|
||||||
|
|
||||||
let value = {
|
UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag)
|
||||||
let local: DateTime<Local> = Local::now();
|
} else {
|
||||||
if let Some(true) = raw {
|
UntaggedValue::string(&output).into_value(&tag)
|
||||||
UntaggedValue::string(date_to_value_raw(local, dt_fmt)).into_untagged_value()
|
};
|
||||||
} else {
|
|
||||||
date_to_value(local, tag, dt_fmt)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::one(value))
|
ReturnSuccess::value(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::labeled_error(
|
||||||
|
"Expected a date from pipeline",
|
||||||
|
"requires date input",
|
||||||
|
&tag,
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
.to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
82
crates/nu-cli/src/commands/date/list_timezone.rs
Normal file
82
crates/nu-cli/src/commands/date/list_timezone.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
use crate::commands::WholeStreamCommand;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use chrono_tz::TZ_VARIANTS;
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
|
||||||
|
pub struct Date;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WholeStreamCommand for Date {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"date list-timezone"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("date list-timezone")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"List supported time zones."
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
list_timezone(args, registry).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "List all supported time zones",
|
||||||
|
example: "date list-timezone",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "List all supported European time zones",
|
||||||
|
example: "date list-timezone | where timezone =~ Europe",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_timezone(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
let args = args.evaluate_once(®istry).await?;
|
||||||
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
|
||||||
|
let list = TZ_VARIANTS.iter().map(move |tz| {
|
||||||
|
let mut entries = IndexMap::new();
|
||||||
|
|
||||||
|
entries.insert(
|
||||||
|
"timezone".to_string(),
|
||||||
|
UntaggedValue::string(tz.name()).into_value(&tag),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(ReturnSuccess::Value(
|
||||||
|
UntaggedValue::Row(Dictionary { entries }).into_value(&tag),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(futures::stream::iter(list).to_output_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Date;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
|
Ok(test_examples(Date {})?)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,15 @@
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
pub mod list_timezone;
|
||||||
pub mod now;
|
pub mod now;
|
||||||
pub mod utc;
|
pub mod to_table;
|
||||||
|
pub mod to_timezone;
|
||||||
|
|
||||||
mod utils;
|
mod parser;
|
||||||
|
|
||||||
pub use command::Command as Date;
|
pub use command::Command as Date;
|
||||||
pub use format::Date as DateFormat;
|
pub use format::Date as DateFormat;
|
||||||
|
pub use list_timezone::Date as DateListTimeZone;
|
||||||
pub use now::Date as DateNow;
|
pub use now::Date as DateNow;
|
||||||
pub use utc::Date as DateUTC;
|
pub use to_table::Date as DateToTable;
|
||||||
|
pub use to_timezone::Date as DateToTimeZone;
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Local};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{Signature, UntaggedValue};
|
||||||
use crate::commands::date::utils::date_to_value;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use nu_protocol::Signature;
|
|
||||||
|
|
||||||
pub struct Date;
|
pub struct Date;
|
||||||
|
|
||||||
|
@ -19,7 +17,7 @@ impl WholeStreamCommand for Date {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"return the current date."
|
"Get the current date."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(
|
||||||
|
@ -35,16 +33,12 @@ pub async fn now(
|
||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
|
||||||
let args = args.evaluate_once(®istry).await?;
|
let args = args.evaluate_once(®istry).await?;
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
|
||||||
let no_fmt = "".to_string();
|
let now: DateTime<Local> = Local::now();
|
||||||
|
|
||||||
let value = {
|
let value = UntaggedValue::date(now.with_timezone(now.offset())).into_value(&tag);
|
||||||
let local: DateTime<Local> = Local::now();
|
|
||||||
date_to_value(local, tag, no_fmt)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::one(value))
|
Ok(OutputStream::one(value))
|
||||||
}
|
}
|
||||||
|
|
107
crates/nu-cli/src/commands/date/parser.rs
Normal file
107
crates/nu-cli/src/commands/date/parser.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
// Modified from chrono::format::scan
|
||||||
|
|
||||||
|
use chrono::{DateTime, FixedOffset, Local, Offset, TimeZone};
|
||||||
|
use chrono_tz::Tz;
|
||||||
|
use titlecase::titlecase;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
|
pub enum ParseErrorKind {
|
||||||
|
/// Given field is out of permitted range.
|
||||||
|
OutOfRange,
|
||||||
|
|
||||||
|
/// The input string has some invalid character sequence for given formatting items.
|
||||||
|
Invalid,
|
||||||
|
|
||||||
|
/// The input string has been prematurely ended.
|
||||||
|
TooShort,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn datetime_in_timezone(
|
||||||
|
dt: &DateTime<FixedOffset>,
|
||||||
|
s: &str,
|
||||||
|
) -> Result<DateTime<FixedOffset>, ParseErrorKind> {
|
||||||
|
match timezone_offset_internal(s, true, true) {
|
||||||
|
Ok(offset) => match FixedOffset::east_opt(offset) {
|
||||||
|
Some(offset) => Ok(dt.with_timezone(&offset)),
|
||||||
|
None => Err(ParseErrorKind::OutOfRange),
|
||||||
|
},
|
||||||
|
Err(ParseErrorKind::Invalid) => {
|
||||||
|
if s.to_lowercase() == "local" {
|
||||||
|
Ok(dt.with_timezone(Local::now().offset()))
|
||||||
|
} else {
|
||||||
|
let tz: Tz = parse_timezone_internal(s)?;
|
||||||
|
let offset = tz.offset_from_utc_datetime(&dt.naive_utc()).fix();
|
||||||
|
Ok(dt.with_timezone(&offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_timezone_internal(s: &str) -> Result<Tz, ParseErrorKind> {
|
||||||
|
if let Ok(tz) = s.parse() {
|
||||||
|
Ok(tz)
|
||||||
|
} else if let Ok(tz) = titlecase(s).parse() {
|
||||||
|
Ok(tz)
|
||||||
|
} else if let Ok(tz) = s.to_uppercase().parse() {
|
||||||
|
Ok(tz)
|
||||||
|
} else {
|
||||||
|
Err(ParseErrorKind::Invalid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timezone_offset_internal(
|
||||||
|
mut s: &str,
|
||||||
|
consume_colon: bool,
|
||||||
|
allow_missing_minutes: bool,
|
||||||
|
) -> Result<i32, ParseErrorKind> {
|
||||||
|
fn digits(s: &str) -> Result<(u8, u8), ParseErrorKind> {
|
||||||
|
let b = s.as_bytes();
|
||||||
|
if b.len() < 2 {
|
||||||
|
Err(ParseErrorKind::TooShort)
|
||||||
|
} else {
|
||||||
|
Ok((b[0], b[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let negative = match s.as_bytes().first() {
|
||||||
|
Some(&b'+') => false,
|
||||||
|
Some(&b'-') => true,
|
||||||
|
Some(_) => return Err(ParseErrorKind::Invalid),
|
||||||
|
None => return Err(ParseErrorKind::TooShort),
|
||||||
|
};
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
// hours (00--99)
|
||||||
|
let hours = match digits(s)? {
|
||||||
|
(h1 @ b'0'..=b'9', h2 @ b'0'..=b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
|
||||||
|
_ => return Err(ParseErrorKind::Invalid),
|
||||||
|
};
|
||||||
|
s = &s[2..];
|
||||||
|
|
||||||
|
// colons (and possibly other separators)
|
||||||
|
if consume_colon {
|
||||||
|
s = s.trim_start_matches(|c: char| c == ':' || c.is_whitespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
// minutes (00--59)
|
||||||
|
// if the next two items are digits then we have to add minutes
|
||||||
|
let minutes = if let Ok(ds) = digits(s) {
|
||||||
|
match ds {
|
||||||
|
(m1 @ b'0'..=b'5', m2 @ b'0'..=b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
|
||||||
|
(b'6'..=b'9', b'0'..=b'9') => return Err(ParseErrorKind::OutOfRange),
|
||||||
|
_ => return Err(ParseErrorKind::Invalid),
|
||||||
|
}
|
||||||
|
} else if allow_missing_minutes {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
return Err(ParseErrorKind::TooShort);
|
||||||
|
};
|
||||||
|
match s.len() {
|
||||||
|
len if len >= 2 => &s[2..],
|
||||||
|
len if len == 0 => s,
|
||||||
|
_ => return Err(ParseErrorKind::TooShort),
|
||||||
|
};
|
||||||
|
|
||||||
|
let seconds = hours * 3600 + minutes * 60;
|
||||||
|
Ok(if negative { -seconds } else { seconds })
|
||||||
|
}
|
113
crates/nu-cli/src/commands/date/to_table.rs
Normal file
113
crates/nu-cli/src/commands/date/to_table.rs
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
use crate::commands::WholeStreamCommand;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use chrono::{Datelike, Timelike};
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||||
|
|
||||||
|
pub struct Date;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WholeStreamCommand for Date {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"date to-table"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("date to-table")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Print the date in a structured table."
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
to_table(args, registry).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Print the current date in a table",
|
||||||
|
example: "date now | date to-table",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn to_table(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
let registry = registry.clone();
|
||||||
|
let args = args.evaluate_once(®istry).await?;
|
||||||
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
let input = args.input;
|
||||||
|
|
||||||
|
Ok(input
|
||||||
|
.map(move |value| match value {
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Primitive(Primitive::Date(dt)),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut indexmap = IndexMap::new();
|
||||||
|
|
||||||
|
indexmap.insert(
|
||||||
|
"year".to_string(),
|
||||||
|
UntaggedValue::int(dt.year()).into_value(&tag),
|
||||||
|
);
|
||||||
|
indexmap.insert(
|
||||||
|
"month".to_string(),
|
||||||
|
UntaggedValue::int(dt.month()).into_value(&tag),
|
||||||
|
);
|
||||||
|
indexmap.insert(
|
||||||
|
"day".to_string(),
|
||||||
|
UntaggedValue::int(dt.day()).into_value(&tag),
|
||||||
|
);
|
||||||
|
indexmap.insert(
|
||||||
|
"hour".to_string(),
|
||||||
|
UntaggedValue::int(dt.hour()).into_value(&tag),
|
||||||
|
);
|
||||||
|
indexmap.insert(
|
||||||
|
"minute".to_string(),
|
||||||
|
UntaggedValue::int(dt.minute()).into_value(&tag),
|
||||||
|
);
|
||||||
|
indexmap.insert(
|
||||||
|
"second".to_string(),
|
||||||
|
UntaggedValue::int(dt.second()).into_value(&tag),
|
||||||
|
);
|
||||||
|
|
||||||
|
let tz = dt.offset();
|
||||||
|
indexmap.insert(
|
||||||
|
"timezone".to_string(),
|
||||||
|
UntaggedValue::string(format!("{}", tz)).into_value(&tag),
|
||||||
|
);
|
||||||
|
|
||||||
|
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
|
||||||
|
|
||||||
|
ReturnSuccess::value(value)
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::labeled_error(
|
||||||
|
"Expected a date from pipeline",
|
||||||
|
"requires date input",
|
||||||
|
&tag,
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
.to_output_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Date;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
|
Ok(test_examples(Date {})?)
|
||||||
|
}
|
||||||
|
}
|
118
crates/nu-cli/src/commands/date/to_timezone.rs
Normal file
118
crates/nu-cli/src/commands/date/to_timezone.rs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
use crate::commands::date::parser::{datetime_in_timezone, ParseErrorKind};
|
||||||
|
use crate::commands::WholeStreamCommand;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
use nu_source::Tagged;
|
||||||
|
|
||||||
|
pub struct Date;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct DateToTimeZoneArgs {
|
||||||
|
timezone: Tagged<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WholeStreamCommand for Date {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"date to-timezone"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("date to-timezone").required(
|
||||||
|
"time zone",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"time zone description",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Convert a date to a given time zone.
|
||||||
|
|
||||||
|
Use `date list-timezone` to list all supported time zones.
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
to_timezone(args, registry).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Get the current date in UTC+05:00",
|
||||||
|
example: "date now | date to-timezone +0500",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Get the current local date",
|
||||||
|
example: "date now | date to-timezone local",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Get the current date in Hawaii",
|
||||||
|
example: "date now | date to-timezone US/Hawaii",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn to_timezone(
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
let registry = registry.clone();
|
||||||
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
let (DateToTimeZoneArgs { timezone }, input) = args.process(®istry).await?;
|
||||||
|
|
||||||
|
Ok(input
|
||||||
|
.map(move |value| match value {
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Primitive(Primitive::Date(dt)),
|
||||||
|
..
|
||||||
|
} => match datetime_in_timezone(&dt, &timezone.item) {
|
||||||
|
Ok(dt) => {
|
||||||
|
let value = UntaggedValue::date(dt).into_value(&tag);
|
||||||
|
|
||||||
|
ReturnSuccess::value(value)
|
||||||
|
}
|
||||||
|
Err(e) => Err(ShellError::labeled_error(
|
||||||
|
error_message(e),
|
||||||
|
"invalid time zone",
|
||||||
|
&timezone.tag,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
_ => Err(ShellError::labeled_error(
|
||||||
|
"Expected a date from pipeline",
|
||||||
|
"requires date input",
|
||||||
|
&tag,
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
.to_output_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_message(err: ParseErrorKind) -> &'static str {
|
||||||
|
match err {
|
||||||
|
ParseErrorKind::Invalid => "The time zone description is invalid",
|
||||||
|
ParseErrorKind::OutOfRange => "The time zone offset is out of range",
|
||||||
|
ParseErrorKind::TooShort => "The format of the time zone is invalid",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Date;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
|
Ok(test_examples(Date {})?)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,63 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
|
|
||||||
use crate::commands::date::utils::date_to_value;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use nu_protocol::Signature;
|
|
||||||
|
|
||||||
pub struct Date;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Date {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"date utc"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("date utc")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"return the current date in utc."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
utc(args, registry).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn utc(
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
let args = args.evaluate_once(®istry).await?;
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
|
|
||||||
let no_fmt = "".to_string();
|
|
||||||
|
|
||||||
let value = {
|
|
||||||
let local: DateTime<Utc> = Utc::now();
|
|
||||||
date_to_value(local, tag, no_fmt)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::one(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::Date;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Date {})?)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use chrono::DateTime;
|
|
||||||
use nu_protocol::{Dictionary, Value};
|
|
||||||
|
|
||||||
use chrono::{Datelike, TimeZone, Timelike};
|
|
||||||
use core::fmt::Display;
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use nu_protocol::UntaggedValue;
|
|
||||||
|
|
||||||
pub fn date_to_value_raw<T: TimeZone>(dt: DateTime<T>, dt_format: String) -> String
|
|
||||||
where
|
|
||||||
T::Offset: Display,
|
|
||||||
{
|
|
||||||
let result = dt.format(&dt_format);
|
|
||||||
format!("{}", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, tag: Tag, dt_format: String) -> Value
|
|
||||||
where
|
|
||||||
T::Offset: Display,
|
|
||||||
{
|
|
||||||
let mut indexmap = IndexMap::new();
|
|
||||||
|
|
||||||
if dt_format.is_empty() {
|
|
||||||
indexmap.insert(
|
|
||||||
"year".to_string(),
|
|
||||||
UntaggedValue::int(dt.year()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"month".to_string(),
|
|
||||||
UntaggedValue::int(dt.month()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"day".to_string(),
|
|
||||||
UntaggedValue::int(dt.day()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"hour".to_string(),
|
|
||||||
UntaggedValue::int(dt.hour()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"minute".to_string(),
|
|
||||||
UntaggedValue::int(dt.minute()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"second".to_string(),
|
|
||||||
UntaggedValue::int(dt.second()).into_value(&tag),
|
|
||||||
);
|
|
||||||
|
|
||||||
let tz = dt.offset();
|
|
||||||
indexmap.insert(
|
|
||||||
"timezone".to_string(),
|
|
||||||
UntaggedValue::string(format!("{}", tz)).into_value(&tag),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let result = dt.format(&dt_format);
|
|
||||||
indexmap.insert(
|
|
||||||
"formatted".to_string(),
|
|
||||||
UntaggedValue::string(format!("{}", result)).into_value(&tag),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag)
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
pub(crate) mod shape;
|
pub(crate) mod shape;
|
||||||
|
|
||||||
use bigdecimal::BigDecimal;
|
use bigdecimal::BigDecimal;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, FixedOffset, Utc};
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
@ -75,8 +75,8 @@ pub enum CompareValues {
|
||||||
Ints(BigInt, BigInt),
|
Ints(BigInt, BigInt),
|
||||||
Decimals(BigDecimal, BigDecimal),
|
Decimals(BigDecimal, BigDecimal),
|
||||||
String(String, String),
|
String(String, String),
|
||||||
Date(DateTime<Utc>, DateTime<Utc>),
|
Date(DateTime<FixedOffset>, DateTime<FixedOffset>),
|
||||||
DateDuration(DateTime<Utc>, BigInt),
|
DateDuration(DateTime<FixedOffset>, BigInt),
|
||||||
Booleans(bool, bool),
|
Booleans(bool, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,9 +94,10 @@ impl CompareValues {
|
||||||
Span::unknown(),
|
Span::unknown(),
|
||||||
)
|
)
|
||||||
.expect("Could not convert nushell Duration into chrono Duration.");
|
.expect("Could not convert nushell Duration into chrono Duration.");
|
||||||
let right: DateTime<Utc> = Utc::now()
|
let right: DateTime<FixedOffset> = Utc::now()
|
||||||
.checked_sub_signed(duration)
|
.checked_sub_signed(duration)
|
||||||
.expect("Data overflow");
|
.expect("Data overflow")
|
||||||
|
.into();
|
||||||
right.cmp(left)
|
right.cmp(left)
|
||||||
}
|
}
|
||||||
CompareValues::Booleans(left, right) => left.cmp(right),
|
CompareValues::Booleans(left, right) => left.cmp(right),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// use crate::config::{Conf, NuConfig};
|
// use crate::config::{Conf, NuConfig};
|
||||||
use bigdecimal::BigDecimal;
|
use bigdecimal::BigDecimal;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
use nu_protocol::RangeInclusion;
|
use nu_protocol::RangeInclusion;
|
||||||
use nu_protocol::{format_primitive, ColumnPath, Dictionary, Primitive, UntaggedValue, Value};
|
use nu_protocol::{format_primitive, ColumnPath, Dictionary, Primitive, UntaggedValue, Value};
|
||||||
|
@ -31,7 +31,7 @@ pub enum InlineShape {
|
||||||
ColumnPath(ColumnPath),
|
ColumnPath(ColumnPath),
|
||||||
Pattern(String),
|
Pattern(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Date(DateTime<Utc>),
|
Date(DateTime<FixedOffset>),
|
||||||
Duration(BigInt),
|
Duration(BigInt),
|
||||||
Path(PathBuf),
|
Path(PathBuf),
|
||||||
Binary(usize),
|
Binary(usize),
|
||||||
|
|
|
@ -25,7 +25,7 @@ impl Date {
|
||||||
|
|
||||||
let date = date.with_timezone(&chrono::offset::Utc);
|
let date = date.with_timezone(&chrono::offset::Utc);
|
||||||
|
|
||||||
Ok(UntaggedValue::Primitive(Primitive::Date(date)))
|
Ok(UntaggedValue::Primitive(Primitive::Date(date.into())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn naive_from_str(s: Tagged<&str>) -> Result<UntaggedValue, ShellError> {
|
pub fn naive_from_str(s: Tagged<&str>) -> Result<UntaggedValue, ShellError> {
|
||||||
|
@ -38,7 +38,7 @@ impl Date {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(UntaggedValue::Primitive(Primitive::Date(
|
Ok(UntaggedValue::Primitive(Primitive::Date(
|
||||||
DateTime::<Utc>::from_utc(date.and_hms(12, 34, 56), Utc),
|
DateTime::<Utc>::from_utc(date.and_hms(12, 34, 56), Utc).into(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::value::range::{Range, RangeInclusion};
|
||||||
use crate::ColumnPath;
|
use crate::ColumnPath;
|
||||||
use bigdecimal::BigDecimal;
|
use bigdecimal::BigDecimal;
|
||||||
use bigdecimal::FromPrimitive;
|
use bigdecimal::FromPrimitive;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, FixedOffset, Utc};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_source::{AnchorLocation, HasSpan, Span, Spanned, SpannedItem, Tag};
|
use nu_source::{AnchorLocation, HasSpan, Span, Spanned, SpannedItem, Tag};
|
||||||
|
@ -248,10 +248,11 @@ impl UntaggedValue {
|
||||||
|
|
||||||
/// Helper for creating datatime values
|
/// Helper for creating datatime values
|
||||||
pub fn system_date(s: SystemTime) -> UntaggedValue {
|
pub fn system_date(s: SystemTime) -> UntaggedValue {
|
||||||
UntaggedValue::Primitive(Primitive::Date(s.into()))
|
let utc: DateTime<Utc> = s.into();
|
||||||
|
UntaggedValue::Primitive(Primitive::Date(utc.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn date(d: impl Into<DateTime<Utc>>) -> UntaggedValue {
|
pub fn date(d: impl Into<DateTime<FixedOffset>>) -> UntaggedValue {
|
||||||
UntaggedValue::Primitive(Primitive::Date(d.into()))
|
UntaggedValue::Primitive(Primitive::Date(d.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -924,7 +925,7 @@ pub trait DateTimeExt {
|
||||||
fn to_value_create_tag(&self) -> Value;
|
fn to_value_create_tag(&self) -> Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DateTimeExt for DateTime<Utc> {
|
impl DateTimeExt for DateTime<FixedOffset> {
|
||||||
fn to_value(&self, the_tag: Tag) -> Value {
|
fn to_value(&self, the_tag: Tag) -> Value {
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Date(*self)),
|
value: UntaggedValue::Primitive(Primitive::Date(*self)),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::value::column_path::ColumnPath;
|
||||||
use crate::value::range::{Range, RangeInclusion};
|
use crate::value::range::{Range, RangeInclusion};
|
||||||
use crate::value::{serde_bigdecimal, serde_bigint};
|
use crate::value::{serde_bigdecimal, serde_bigint};
|
||||||
use bigdecimal::BigDecimal;
|
use bigdecimal::BigDecimal;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, FixedOffset, Utc};
|
||||||
use nu_errors::{ExpectedRange, ShellError};
|
use nu_errors::{ExpectedRange, ShellError};
|
||||||
use nu_source::{PrettyDebug, Span, SpannedItem};
|
use nu_source::{PrettyDebug, Span, SpannedItem};
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
|
@ -42,8 +42,8 @@ pub enum Primitive {
|
||||||
Pattern(String),
|
Pattern(String),
|
||||||
/// A boolean value
|
/// A boolean value
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
/// A date value, in UTC
|
/// A date value
|
||||||
Date(DateTime<Utc>),
|
Date(DateTime<FixedOffset>),
|
||||||
/// A count in the number of nanoseconds
|
/// A count in the number of nanoseconds
|
||||||
#[serde(with = "serde_bigint")]
|
#[serde(with = "serde_bigint")]
|
||||||
Duration(BigInt),
|
Duration(BigInt),
|
||||||
|
@ -385,8 +385,8 @@ pub fn format_duration(duration: &BigInt) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
/// Format a UTC date value into a humanized string (eg "1 week ago" instead of a formal date string)
|
/// Format a date value into a humanized string (eg "1 week ago" instead of a formal date string)
|
||||||
pub fn format_date(d: &DateTime<Utc>) -> String {
|
pub fn format_date(d: &DateTime<FixedOffset>) -> String {
|
||||||
let utc: DateTime<Utc> = Utc::now();
|
let utc: DateTime<Utc> = Utc::now();
|
||||||
|
|
||||||
let duration = utc.signed_duration_since(*d);
|
let duration = utc.signed_duration_since(*d);
|
||||||
|
|
|
@ -40,10 +40,9 @@ pub fn date(input: impl Into<String>) -> Value {
|
||||||
let date = NaiveDate::parse_from_str(key.borrow_tagged().item, "%Y-%m-%d")
|
let date = NaiveDate::parse_from_str(key.borrow_tagged().item, "%Y-%m-%d")
|
||||||
.expect("date from string failed");
|
.expect("date from string failed");
|
||||||
|
|
||||||
UntaggedValue::Primitive(Primitive::Date(DateTime::<Utc>::from_utc(
|
UntaggedValue::Primitive(Primitive::Date(
|
||||||
date.and_hms(12, 34, 56),
|
DateTime::<Utc>::from_utc(date.and_hms(12, 34, 56), Utc).into(),
|
||||||
Utc,
|
))
|
||||||
)))
|
|
||||||
.into_untagged_value()
|
.into_untagged_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,9 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into<Tag>) -> Result<Value
|
||||||
);
|
);
|
||||||
collected.into_value()
|
collected.into_value()
|
||||||
}
|
}
|
||||||
Bson::UtcDatetime(dt) => UntaggedValue::Primitive(Primitive::Date(*dt)).into_value(&tag),
|
Bson::UtcDatetime(dt) => {
|
||||||
|
UntaggedValue::Primitive(Primitive::Date((*dt).into())).into_value(&tag)
|
||||||
|
}
|
||||||
Bson::Symbol(s) => {
|
Bson::Symbol(s) => {
|
||||||
let mut collected = TaggedDictBuilder::new(tag.clone());
|
let mut collected = TaggedDictBuilder::new(tag.clone());
|
||||||
collected.insert_value(
|
collected.insert_value(
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub fn value_to_bson_value(v: &Value) -> Result<Bson, ShellError> {
|
||||||
.expect("Unimplemented BUG: What about big decimals?"),
|
.expect("Unimplemented BUG: What about big decimals?"),
|
||||||
),
|
),
|
||||||
UntaggedValue::Primitive(Primitive::Duration(i)) => Bson::String(i.to_string()),
|
UntaggedValue::Primitive(Primitive::Duration(i)) => Bson::String(i.to_string()),
|
||||||
UntaggedValue::Primitive(Primitive::Date(d)) => Bson::UtcDatetime(*d),
|
UntaggedValue::Primitive(Primitive::Date(d)) => Bson::UtcDatetime((*d).into()),
|
||||||
UntaggedValue::Primitive(Primitive::EndOfStream) => Bson::Null,
|
UntaggedValue::Primitive(Primitive::EndOfStream) => Bson::Null,
|
||||||
UntaggedValue::Primitive(Primitive::BeginningOfStream) => Bson::Null,
|
UntaggedValue::Primitive(Primitive::BeginningOfStream) => Bson::Null,
|
||||||
UntaggedValue::Primitive(Primitive::Decimal(d)) => {
|
UntaggedValue::Primitive(Primitive::Decimal(d)) => {
|
||||||
|
|
Loading…
Reference in a new issue