mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
fix(date): fix the date
Date didn't work out of the box. Refactor it a bit to match other programs
This commit is contained in:
parent
ecfb42ba6c
commit
850bd9c32d
1 changed files with 133 additions and 84 deletions
|
@ -4,16 +4,20 @@
|
|||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Anthony Deschamps <anthony.j.deschamps@gmail.com>
|
||||
* (c) Sylvestre Ledru <sylvestre@debian.org>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
extern crate chrono;
|
||||
#[macro_use]
|
||||
|
||||
extern crate clap;
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
use chrono::offset::Utc;
|
||||
use chrono::{DateTime, FixedOffset, Local, Offset};
|
||||
use std::fs::File;
|
||||
|
@ -25,8 +29,27 @@ const DATE: &str = "date";
|
|||
const HOURS: &str = "hours";
|
||||
const MINUTES: &str = "minutes";
|
||||
const SECONDS: &str = "seconds";
|
||||
const HOUR: &str = "hour";
|
||||
const MINUTE: &str = "minute";
|
||||
const SECOND: &str = "second";
|
||||
const NS: &str = "ns";
|
||||
|
||||
const NAME: &str = "date";
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const ABOUT: &str = "print or set the system date and time";
|
||||
|
||||
const OPT_DATE: &str = "date";
|
||||
const OPT_FORMAT: &str = "format";
|
||||
const OPT_FILE: &str = "file";
|
||||
const OPT_DEBUG: &str = "debug";
|
||||
const OPT_ISO_8601: &str = "iso-8601";
|
||||
const OPT_RFC_EMAIL: &str = "rfc-email";
|
||||
const OPT_RFC_3339: &str = "rfc-3339";
|
||||
const OPT_SET: &str = "set";
|
||||
const OPT_REFERENCE: &str = "reference";
|
||||
const OPT_UNIVERSAL: &str = "universal";
|
||||
const OPT_UNIVERSAL_2: &str = "utc";
|
||||
|
||||
// Help strings
|
||||
|
||||
static ISO_8601_HELP_STRING: &str = "output date/time in ISO 8601 format.
|
||||
|
@ -35,7 +58,7 @@ static ISO_8601_HELP_STRING: &str = "output date/time in ISO 8601 format.
|
|||
for date and time to the indicated precision.
|
||||
Example: 2006-08-14T02:34:56-06:00";
|
||||
|
||||
static RFC_2822_HELP_STRING: &str = "output date and time in RFC 2822 format.
|
||||
static RFC_5322_HELP_STRING: &str = "output date and time in RFC 5322 format.
|
||||
Example: Mon, 14 Aug 2006 02:34:56 -0600";
|
||||
|
||||
static RFC_3339_HELP_STRING: &str = "output date/time in RFC 3339 format.
|
||||
|
@ -54,7 +77,7 @@ struct Settings {
|
|||
/// Various ways of displaying the date
|
||||
enum Format {
|
||||
Iso8601(Iso8601Format),
|
||||
Rfc2822,
|
||||
Rfc5322,
|
||||
Rfc3339(Rfc3339Format),
|
||||
Custom(String),
|
||||
Default,
|
||||
|
@ -78,9 +101,9 @@ enum Iso8601Format {
|
|||
impl<'a> From<&'a str> for Iso8601Format {
|
||||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
HOURS => Iso8601Format::Hours,
|
||||
MINUTES => Iso8601Format::Minutes,
|
||||
SECONDS => Iso8601Format::Seconds,
|
||||
HOURS | HOUR => Iso8601Format::Hours,
|
||||
MINUTES | MINUTE => Iso8601Format::Minutes,
|
||||
SECONDS | SECOND => Iso8601Format::Seconds,
|
||||
NS => Iso8601Format::Ns,
|
||||
DATE => Iso8601Format::Date,
|
||||
// Should be caught by clap
|
||||
|
@ -99,7 +122,7 @@ impl<'a> From<&'a str> for Rfc3339Format {
|
|||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
DATE => Rfc3339Format::Date,
|
||||
SECONDS => Rfc3339Format::Seconds,
|
||||
SECONDS | SECOND => Rfc3339Format::Seconds,
|
||||
NS => Rfc3339Format::Ns,
|
||||
// Should be caught by clap
|
||||
_ => panic!("Invalid format: {}", s),
|
||||
|
@ -108,7 +131,108 @@ impl<'a> From<&'a str> for Rfc3339Format {
|
|||
}
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> i32 {
|
||||
let settings = parse_cli(args);
|
||||
let syntax = format!(
|
||||
"{0} [OPTION]... [+FORMAT]...
|
||||
{0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]",
|
||||
NAME
|
||||
);
|
||||
let matches = App::new(executable!())
|
||||
.version(VERSION)
|
||||
.about(ABOUT)
|
||||
.usage(&syntax[..])
|
||||
.arg(
|
||||
Arg::with_name(OPT_DATE)
|
||||
.short("d")
|
||||
.long(OPT_DATE)
|
||||
.takes_value(true)
|
||||
.help("display time described by STRING, not 'now'"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_FILE)
|
||||
.short("f")
|
||||
.long(OPT_FILE)
|
||||
.takes_value(true)
|
||||
.help("like --date; once for each line of DATEFILE"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_ISO_8601)
|
||||
.short("I")
|
||||
.long(OPT_ISO_8601)
|
||||
.takes_value(true)
|
||||
.help(ISO_8601_HELP_STRING),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_RFC_EMAIL)
|
||||
.short("R")
|
||||
.long(OPT_RFC_EMAIL)
|
||||
.help(RFC_5322_HELP_STRING),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_RFC_3339)
|
||||
.long(OPT_RFC_3339)
|
||||
.takes_value(true)
|
||||
.help(RFC_3339_HELP_STRING),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_DEBUG)
|
||||
.long(OPT_DEBUG)
|
||||
.help("annotate the parsed date, and warn about questionable usage to stderr"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_REFERENCE)
|
||||
.short("r")
|
||||
.long(OPT_REFERENCE)
|
||||
.takes_value(true)
|
||||
.help("display the last modification time of FILE"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_SET)
|
||||
.short("s")
|
||||
.long(OPT_SET)
|
||||
.takes_value(true)
|
||||
.help("set time described by STRING"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_UNIVERSAL)
|
||||
.short("u")
|
||||
.long(OPT_UNIVERSAL)
|
||||
.alias(OPT_UNIVERSAL_2)
|
||||
.help("print or set Coordinated Universal Time (UTC)"),
|
||||
)
|
||||
.arg(Arg::with_name(OPT_FORMAT).multiple(true))
|
||||
.get_matches_from(&args);
|
||||
|
||||
let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
|
||||
let form = form[1..].into();
|
||||
Format::Custom(form)
|
||||
} else if let Some(fmt) = matches
|
||||
.values_of(OPT_ISO_8601)
|
||||
.map(|mut iter| iter.next().unwrap_or(DATE).into())
|
||||
{
|
||||
Format::Iso8601(fmt)
|
||||
} else if matches.is_present(OPT_RFC_EMAIL) {
|
||||
Format::Rfc5322
|
||||
} else if let Some(fmt) = matches.value_of(OPT_RFC_3339).map(Into::into) {
|
||||
Format::Rfc3339(fmt)
|
||||
} else {
|
||||
Format::Default
|
||||
};
|
||||
|
||||
let date_source = if let Some(date) = matches.value_of(OPT_DATE) {
|
||||
DateSource::Custom(date.into())
|
||||
} else if let Some(file) = matches.value_of(OPT_FILE) {
|
||||
DateSource::File(file.into())
|
||||
} else {
|
||||
DateSource::Now
|
||||
};
|
||||
|
||||
let settings = Settings {
|
||||
utc: matches.is_present(OPT_UNIVERSAL),
|
||||
format,
|
||||
date_source,
|
||||
// TODO: Handle this option:
|
||||
set_to: None,
|
||||
};
|
||||
|
||||
if let Some(_time) = settings.set_to {
|
||||
unimplemented!();
|
||||
|
@ -174,81 +298,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
|||
0
|
||||
}
|
||||
|
||||
/// Handle command line arguments.
|
||||
fn parse_cli(args: Vec<String>) -> Settings {
|
||||
let matches = clap_app!(
|
||||
date =>
|
||||
(@group dates =>
|
||||
(@arg date: -d --date [STRING]
|
||||
"display time described by STRING, not 'now'")
|
||||
(@arg file: -f --file [DATEFILE]
|
||||
"like --date; once for each line of DATEFILE"))
|
||||
|
||||
(@group format =>
|
||||
(@arg iso_8601: -I --("iso-8601") <FMT>
|
||||
possible_value[date hours minutes seconds ns]
|
||||
#{0, 1}
|
||||
ISO_8601_HELP_STRING)
|
||||
(@arg rfc_2822: -R --("rfc-2822")
|
||||
RFC_2822_HELP_STRING)
|
||||
(@arg rfc_3339: --("rfc-3339") <FMT>
|
||||
possible_value[date seconds ns]
|
||||
RFC_3339_HELP_STRING)
|
||||
(@arg custom_format: +takes_value {
|
||||
|s| if s.starts_with('+') {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(String::from("Date formats must start with a '+' character"))
|
||||
}
|
||||
}))
|
||||
|
||||
(@arg debug: --debug
|
||||
"annotate the parsed date, and warn about questionable usage to stderr")
|
||||
(@arg reference: -r --reference [FILE]
|
||||
"display the last modification time of FILE")
|
||||
(@arg set: -s --set [STRING]
|
||||
"set time described by STRING")
|
||||
(@arg utc: -u --utc --universal
|
||||
"print or set Coordinated Universal Time (UTC)"))
|
||||
// TODO: Decide whether this is appropriate.
|
||||
// The GNU date command has an explanation of all formatting options,
|
||||
// but the `chrono` crate has a few differences (most notably, the %Z option)
|
||||
// (after_help: include_str!("usage.txt")))
|
||||
.get_matches_from(args);
|
||||
|
||||
let format = if let Some(form) = matches.value_of("custom_format") {
|
||||
let form = form[1..].into();
|
||||
Format::Custom(form)
|
||||
} else if let Some(fmt) = matches
|
||||
.values_of("iso_8601")
|
||||
.map(|mut iter| iter.next().unwrap_or(DATE).into())
|
||||
{
|
||||
Format::Iso8601(fmt)
|
||||
} else if matches.is_present("rfc_2822") {
|
||||
Format::Rfc2822
|
||||
} else if let Some(fmt) = matches.value_of("rfc_3339").map(Into::into) {
|
||||
Format::Rfc3339(fmt)
|
||||
} else {
|
||||
Format::Default
|
||||
};
|
||||
|
||||
let date_source = if let Some(date) = matches.value_of("date") {
|
||||
DateSource::Custom(date.into())
|
||||
} else if let Some(file) = matches.value_of("file") {
|
||||
DateSource::File(file.into())
|
||||
} else {
|
||||
DateSource::Now
|
||||
};
|
||||
|
||||
Settings {
|
||||
utc: matches.is_present("utc"),
|
||||
format,
|
||||
date_source,
|
||||
// TODO: Handle this option:
|
||||
set_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the appropriate format string for the given settings.
|
||||
fn make_format_string(settings: &Settings) -> &str {
|
||||
match settings.format {
|
||||
|
@ -259,7 +308,7 @@ fn make_format_string(settings: &Settings) -> &str {
|
|||
Iso8601Format::Seconds => "%FT%T%:z",
|
||||
Iso8601Format::Ns => "%FT%T,%f%:z",
|
||||
},
|
||||
Format::Rfc2822 => "%a, %d %h %Y %T %z",
|
||||
Format::Rfc5322 => "%a, %d %h %Y %T %z",
|
||||
Format::Rfc3339(ref fmt) => match *fmt {
|
||||
Rfc3339Format::Date => "%F",
|
||||
Rfc3339Format::Seconds => "%F %T%:z",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue