Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/uu/date/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,6 @@ date-error-setting-date-not-supported-redox = setting the date is not supported
date-error-cannot-set-date = cannot set date
date-error-extra-operand = extra operand '{$operand}'
date-error-write = write error: {$error}
date-error-format-missing-plus = the argument {$arg} lacks a leading '+';
when using an option to specify date(s), any non-option
argument must be a format string beginning with '+'
3 changes: 3 additions & 0 deletions src/uu/date/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,6 @@ date-error-setting-date-not-supported-redox = la définition de la date n'est pa
date-error-cannot-set-date = impossible de définir la date
date-error-extra-operand = opérande supplémentaire '{$operand}'
date-error-write = erreur d'écriture: {$error}
date-error-format-missing-plus = l'argument {$arg} ne commence pas par un signe '+';
lorsqu'une option est utilisée pour spécifier une ou plusieurs dates, tout argument autre
qu'une option doit être une chaîne de format commençant par un signe '+'.
54 changes: 32 additions & 22 deletions src/uu/date/src/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,27 @@ fn parse_military_timezone_with_offset(s: &str) -> Option<(i32, DayDelta)> {
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;

let date_source = if let Some(date_os) = matches.get_one::<std::ffi::OsString>(OPT_DATE) {
// Convert OsString to String, handling invalid UTF-8 with GNU-compatible error
let date = date_os.to_str().ok_or_else(|| {
let bytes = date_os.as_encoded_bytes();
let escaped_str = escape_invalid_bytes(bytes);
USimpleError::new(1, format!("invalid date '{escaped_str}'"))
})?;
DateSource::Human(date.into())
} else if let Some(file) = matches.get_one::<String>(OPT_FILE) {
match file.as_ref() {
"-" => DateSource::Stdin,
_ => DateSource::File(file.into()),
}
} else if let Some(file) = matches.get_one::<String>(OPT_REFERENCE) {
DateSource::FileMtime(file.into())
} else if matches.get_flag(OPT_RESOLUTION) {
DateSource::Resolution
} else {
DateSource::Now
};

// Check for extra operands (multiple positional arguments)
if let Some(formats) = matches.get_many::<String>(OPT_FORMAT) {
let format_args: Vec<&String> = formats.collect();
Expand All @@ -298,9 +319,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {

let format = if let Some(form) = matches.get_one::<String>(OPT_FORMAT) {
if !form.starts_with('+') {
// if an optional Format String was found but the user has not provided an input date
// GNU prints an invalid date Error
if !matches!(date_source, DateSource::Human(_)) {
return Err(USimpleError::new(
1,
translate!("date-error-invalid-date", "date" => form),
));
}
// If the user did provide an input date with the --date flag and the Format String is
// not starting with '+' GNU prints the missing '+' error message
return Err(USimpleError::new(
1,
translate!("date-error-invalid-date", "date" => form),
translate!("date-error-format-missing-plus", "arg" => form),
));
}
let form = form[1..].to_string();
Expand Down Expand Up @@ -333,27 +364,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Zoned::now()
};

let date_source = if let Some(date_os) = matches.get_one::<std::ffi::OsString>(OPT_DATE) {
// Convert OsString to String, handling invalid UTF-8 with GNU-compatible error
let date = date_os.to_str().ok_or_else(|| {
let bytes = date_os.as_encoded_bytes();
let escaped_str = escape_invalid_bytes(bytes);
USimpleError::new(1, format!("invalid date '{escaped_str}'"))
})?;
DateSource::Human(date.into())
} else if let Some(file) = matches.get_one::<String>(OPT_FILE) {
match file.as_ref() {
"-" => DateSource::Stdin,
_ => DateSource::File(file.into()),
}
} else if let Some(file) = matches.get_one::<String>(OPT_REFERENCE) {
DateSource::FileMtime(file.into())
} else if matches.get_flag(OPT_RESOLUTION) {
DateSource::Resolution
} else {
DateSource::Now
};

let set_to = match matches
.get_one::<String>(OPT_SET)
.map(|s| parse_date(s, &now, DebugOptions::new(debug_mode, true)))
Expand Down
12 changes: 12 additions & 0 deletions tests/by-util/test_date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ fn test_extra_operands() {
.stderr_contains("extra operand 'extra'");
}

#[test]
fn test_bad_format_option_missing_leading_plus_after_d_flag() {
let bad_arguments = vec!["q", "a", "test", "%Y-%m-%d"];

for bad_argument in bad_arguments {
new_ucmd!()
.args(&["--date", "1996-01-31", bad_argument])
.fails_with_code(1)
.stderr_contains(format!("the argument {bad_argument} lacks a leading '+';\nwhen using an option to specify date(s), any non-option\nargument must be a format string beginning with '+'"), );
}
}

#[test]
fn test_invalid_long_option() {
new_ucmd!()
Expand Down
Loading