JustBash.Commands.Date (JustBash v0.4.0)

View Source

The date command — display the current date and time.

Output is always UTC. Supported directives:

  • compound — %F (%Y-%m-%d), %T (%H:%M:%S), %R (%H:%M), %D (%m/%d/%y), %c, %x, %X, %r
  • date — %Y, %y, %C, %m, %d, %e (space-padded), %j, %q, %a, %A, %b, %h, %B, %u, %w, %G, %g, %U, %V, %W
  • time — %H, %M, %S, %N, %I, %k, %l, %p, %P, %Z, %s
  • zone offset — %z (+0000), %:z, %::z, %:::z
  • literal — %%, %n, %t

A directive is %, then field flags, then an optional width, then an optional locale modifier, then the conversion — %_5Od is all four at once. The flags are - (no padding), _ (space padding), 0 (zero padding), ^ (upper case) and # (swap the conversion's default case). The modifiers are E and O, which select nothing in the C locale but are accepted only for the conversions GNU accepts them for; the rest pass through verbatim.

None of that composes the way it reads. A flag reaches a numeric field and not a compound one, so %-T is "09:05:03" and only %-D loses its year padding; the last padding flag wins outright, so %-0d is "05"; a width replaces a conversion's own width rather than raising it, so %1d is "5"; and a modifier drops the padding flag entirely, so %-Od is "05". Every one of those rules is recorded in test/fixtures/bash_cases/date_matrix.json against real GNU date rather than reasoned about here — this paragraph describes the recording, it does not define it.

An unrecognized directive is emitted verbatim (%J → %J), as GNU date does, so a caller can tell the difference between "not supported" and a real value. That passthrough is only safe because the supported set is complete enough that reaching it means the directive really does not exist: a directive that is real but unimplemented would print itself at exit 0, and a caller cannot tell that from a legitimate literal. The matrix enumerates the whole conversion alphabet — crossed with every flag, width and modifier — to keep it so.

Flags: -d / --date, -r SECONDS|FILE / --reference FILE, -I[FMT] / --iso-8601[=FMT], -R / --rfc-email, -u / --utc / --universal, the BSD -v adjustments, and the BSD -j / -f pair. A value may be attached or separate (-d2024-06-15, --date=2024-06-15), no-argument flags may cluster (-ju), and -- ends option parsing — the getopt conventions real date inherits.

-r reads both spellings the flag has in the wild, as FreeBSD's date does: a numeric value is epoch seconds, and anything else names a file whose modification time to report. GNU's --reference is always a file. The VFS records mtimes, so the file's time comes from the sandbox rather than the host clock, and a failure to read it reports the real error kind — descending through a regular file is ENOTDIR, not "no such file".

-d and -r each name the instant to print, so giving both is an error rather than a silent choice between two answers.

Everything else is an error. An unimplemented flag must not be dropped: ignoring it would print the current date at exit 0, which a caller cannot tell apart from a real answer.

Errors follow whichever real implementation spells the flag: GNU's wording for a long option (unrecognized option '--foo'), BSD's for a short one (illegal option -- X), since BSD names a long option by its second - and so identifies nothing.

One BSD feature is deliberately partial, and says so rather than guessing: -v implements only the relative form ([+-]val[ymwdHMS]), not the set-a-field form (-v1d, -vfri).