diff --git a/README.md b/README.md index 48bd3ad329..d792d438f4 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | cd path | Change to a new path | | cp source path | Copy files | | ls (path) | View the contents of the current or given path | +| date (--utc) | Get the current datetime | | ps | View current processes | | sysinfo | View information about the current system | | open {filename or url} | Load a file into a cell, convert to table if possible (avoid by appending '--raw') | diff --git a/src/cli.rs b/src/cli.rs index 6437756da4..a31b6d1a36 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -180,6 +180,7 @@ pub async fn cli() -> Result<(), Box> { Arc::new(Remove), Arc::new(Copycp), Arc::new(Open), + Arc::new(Date), Arc::new(Where), Arc::new(Config), Arc::new(SkipWhile), diff --git a/src/commands.rs b/src/commands.rs index 95ec6a4005..c958ee041a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -4,12 +4,12 @@ crate mod macros; crate mod args; crate mod autoview; crate mod cd; -crate mod cp; -crate mod rm; crate mod classified; crate mod clip; crate mod command; crate mod config; +crate mod cp; +crate mod date; crate mod exit; crate mod first; crate mod from_csv; @@ -26,6 +26,7 @@ crate mod pick; crate mod plugin; crate mod ps; crate mod reject; +crate mod rm; crate mod save; crate mod size; crate mod skip_while; @@ -46,7 +47,8 @@ crate mod where_; crate use command::command; crate use config::Config; crate use cp::Copycp; -crate use rm::Remove; +crate use date::Date; crate use open::Open; +crate use rm::Remove; crate use skip_while::SkipWhile; crate use where_::Where; diff --git a/src/commands/date.rs b/src/commands/date.rs new file mode 100644 index 0000000000..364cdda60c --- /dev/null +++ b/src/commands/date.rs @@ -0,0 +1,117 @@ +use crate::errors::ShellError; +use crate::object::{Dictionary, Value}; +use crate::parser::Spanned; +use crate::prelude::*; +use chrono::{DateTime, Local, Utc}; + +use crate::parser::registry::{CommandConfig, NamedType}; +use chrono::{Datelike, TimeZone, Timelike}; +use core::fmt::Display; +use indexmap::IndexMap; + +pub struct Date; + +impl Command for Date { + fn run(&self, args: CommandArgs) -> Result { + date(args) + } + fn name(&self) -> &str { + "date" + } + + fn config(&self) -> CommandConfig { + let mut named: IndexMap = IndexMap::new(); + named.insert("utc".to_string(), NamedType::Switch); + named.insert("local".to_string(), NamedType::Switch); + + CommandConfig { + name: self.name().to_string(), + positional: vec![], + rest_positional: false, + named, + is_sink: true, + is_filter: false, + } + } +} + +pub fn date_to_value(dt: DateTime, span: Span) -> Spanned +where + T::Offset: Display, +{ + let mut indexmap = IndexMap::new(); + + indexmap.insert( + "year".to_string(), + Spanned { + item: Value::int(dt.year()), + span, + }, + ); + indexmap.insert( + "month".to_string(), + Spanned { + item: Value::int(dt.month()), + span, + }, + ); + indexmap.insert( + "day".to_string(), + Spanned { + item: Value::int(dt.day()), + span, + }, + ); + indexmap.insert( + "hour".to_string(), + Spanned { + item: Value::int(dt.hour()), + span, + }, + ); + indexmap.insert( + "minute".to_string(), + Spanned { + item: Value::int(dt.minute()), + span, + }, + ); + indexmap.insert( + "second".to_string(), + Spanned { + item: Value::int(dt.second()), + span, + }, + ); + + let tz = dt.offset(); + indexmap.insert( + "timezone".to_string(), + Spanned { + item: Value::string(format!("{}", tz)), + span, + }, + ); + + Spanned { + item: Value::Object(Dictionary::from(indexmap)), + span, + } +} + +pub fn date(args: CommandArgs) -> Result { + let mut date_out = VecDeque::new(); + let span = args.call_info.name_span.unwrap(); + + let value = if args.has("utc") { + let utc: DateTime = Utc::now(); + date_to_value(utc, span) + } else { + let local: DateTime = Local::now(); + date_to_value(local, span) + }; + + date_out.push_back(value); + + Ok(date_out.to_output_stream()) +}