diff --git a/NEWS b/NEWS index d9a8030a3..66099f6ef 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,17 @@ For full details, see the git log at: https://github.com/ksh93/ksh Any uppercase BUG_* names are modernish shell bug IDs. +2020-07-07: + +- Four of the date formats accepted by 'printf %()T' have had their + functionality altered to the common behavior of date(1): + - '%k' and '%l' print the current hour with blank padding, the former + based on a 24-hour clock and the latter a twelve hour clock. These + are common extensions present on Linux and *BSD. + - '%l' prints the current hour (0-12) with blank padding (GNU and BSD). + - '%f' prints a date with the format string '%Y.%m.%d-%H:%M:%S' (BusyBox). + - '%q' prints the quarter of the year (GNU). + 2020-07-06: - 'notty' is now written to the ksh auditing file instead of '(null)' if diff --git a/src/cmd/ksh93/data/builtins.c b/src/cmd/ksh93/data/builtins.c index b06054e53..0eed506b2 100644 --- a/src/cmd/ksh93/data/builtins.c +++ b/src/cmd/ksh93/data/builtins.c @@ -1221,8 +1221,74 @@ USAGE_LICENSE "convert it to an extended regular expression.]" "[+%T?Treat \astring\a as a date/time string and format it. The " "\bT\b can be preceded by \b(\b\adformat\a\b)\b, where " - "\adformat\a is a date format as defined by the \bdate\b " - "command.]" + "\adformat\a is a date format. The accepted date formats are " + "as follows:]{" + "[+%?% character]" + "[+a?abbreviated weekday name]" + "[+A?full weekday name]" + "[+b?abbreviated month name]" + "[+B?full month name]" + "[+c?\bctime\b(3) style date without the trailing newline]" + "[+C?2-digit century]" + "[+d?day of month number]" + "[+D?date as \amm/dd/yy\a]" + "[+e?blank padded day of month number]" + "[+f?print a date with the format '\%Y.\%m.\%d-\%H:\%M:\%S']" + "[+F?%ISO 8601:2000 standard date format; equivalent to Y-%m-%d]" + "[+g?\bls\b(1) \b-l\b recent date with \ahh:mm\a]" + "[+G?\bls\b(1) \b-l\b distant date with \ayyyy\a]" + "[+h?abbreviated month name]" + "[+H?24-hour clock hour, zero-padded]" + "[+i?international \bdate\b(1) date with time zone type name]" + "[+I?12-hour clock hour, zero-padded]" + "[+j?1-offset Julian date]" + "[+J?0-offset Julian date]" + "[+k?24-hour clock hour, blank-padded]" + "[+K?all numeric date; equivalent to \b%Y-%m-%d+%H:%M:%S\b; \b%_[EO]]K\b for space separator, %OK adds \b.%N\b, \b%EK\b adds \b%.N%z\b, \b%_EK\b adds \b.%N %z\b]" + "[+l?12-hour clock hour, blank-padded]" + "[+L?locale default date format]" + "[+m?month number]" + "[+M?minutes]" + "[+n?newline character]" + "[+N?nanoseconds 000000000-999999999]" + "[+p?meridian (e.g., \bAM\b or \bPM\b)]" + "[+q?quarter of the year]" + "[+Q?\arecentdistant\a: \a\a is a unique " + "delimter character; \arecent\a format for recent " + "dates, \adistant\a format otherwise]" + "[+r?12-hour time as \ahh:mm:ss meridian\a]" + "[+R?24-hour time as \ahh:mm\a]" + "[+s?number of seconds since the epoch; \a.prec\a preceding " + "\bs\b appends \aprec\a nanosecond digits, \b9\b if " + "\aprec\a is omitted]" + "[+S?seconds 00-60]" + "[+t?tab character]" + "[+T?24-hour time as \ahh:mm:ss\a]" + "[+u?weekday number 1(Monday)-7]" + "[+U?week number with Sunday as the first day]" + "[+V?ISO week number (i18n is \afun\a)]" + "[+w?weekday number 0(Sunday)-6]" + "[+W?week number with Monday as the first day]" + "[+x?locale date style that includes month, day and year]" + "[+X?locale time style that includes hours and minutes]" + "[+y?2-digit year (you'll be sorry)]" + "[+Y?4-digit year]" + "[+z?time zone \aSHHMM\a west of GMT offset where S is " + "\b+\b or \b-\b, use pad _ for \aSHH:MM\a]" + "[+Z?time zone name]" + "[+=[=]][-+]]flag?set (default or +) or clear (-) \aflag\a " + "for the remainder of \aformat\a, or for the remainder " + "of the process if \b==\b is specified. \aflag\a may be:]{" + "[+l?enable leap second adjustments]" + "[+n?convert \b%S\b as \b%S.%N\b]" + "[+u?UTC time zone]" + "}" + "[+#?equivalent to %s]" + "[+??alternate?use \aalternate\a format if a default format " + "override has not been specified, e.g., \bls\b(1) uses " + "\"%?%l\"; export TM_OPTIONS=\"format='\aoverride\a'\" " + "to override the default]" + "}" "[+%Z?Output a byte whose value is \b0\b.]" "\fextra\f" "}" diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 575941029..394dc9257 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -17,4 +17,4 @@ * David Korn * * * ***********************************************************************/ -#define SH_RELEASE "93u+m 2020-07-06" +#define SH_RELEASE "93u+m 2020-07-07" diff --git a/src/cmd/ksh93/sh.1 b/src/cmd/ksh93/sh.1 index 6bfaf96a7..38ba1b5d4 100644 --- a/src/cmd/ksh93/sh.1 +++ b/src/cmd/ksh93/sh.1 @@ -6527,10 +6527,7 @@ A .BI %( date-format )T format can be used to treat an argument as a date/time string and to format the date/time according to the -.I date-format\^ -as defined for the -.BR date (1) -command. +.IR date-format . .TP .B %Z A diff --git a/src/cmd/ksh93/tests/builtins.sh b/src/cmd/ksh93/tests/builtins.sh index fd093c66b..66d465e0a 100755 --- a/src/cmd/ksh93/tests/builtins.sh +++ b/src/cmd/ksh93/tests/builtins.sh @@ -749,5 +749,12 @@ unset foo test_func ) +# ====== +# Test the output of nonstandard date formats with 'printf %T' +[[ $(printf '%(%l)T') == $(printf '%(%_I)T') ]] || err_exit 'date format %l is not the same as %_I' +[[ $(printf '%(%k)T') == $(printf '%(%_H)T') ]] || err_exit 'date format %k is not the same as %_H' +[[ $(printf '%(%f)T') == $(printf '%(%Y.%m.%d-%H:%M:%S)T') ]] || err_exit 'date format %f is not the same as %Y.%m.%d-%H:%M:%S' +[[ $(printf '%(%q)T') == $(printf '%(%Qz)T') ]] && err_exit 'date format %q is the same as %Qz' + # ====== exit $((Errors<125?Errors:125)) diff --git a/src/lib/libast/tm/tmxfmt.c b/src/lib/libast/tm/tmxfmt.c index 2010c0804..bc642ad4e 100644 --- a/src/lib/libast/tm/tmxfmt.c +++ b/src/lib/libast/tm/tmxfmt.c @@ -271,8 +271,8 @@ tmxfmt(char* buf, size_t len, const char* format, Time_t t) case 'e': /* blank padded day of month */ cp = number(cp, ep, (long)tm->tm_mday, -2, width, pad); continue; - case 'f': /* (AST) OBSOLETE use %Qf */ - p = "%Qf"; + case 'f': /* Output the date in a format compatible with BusyBox %f */ + p = "%Y.%m.%d-%H:%M:%S"; goto push; case 'F': /* ISO 8601:2000 standard date format */ p = "%Y-%m-%d"; @@ -316,8 +316,8 @@ tmxfmt(char* buf, size_t len, const char* format, Time_t t) case 'J': /* Julian date (0 offset) */ cp = number(cp, ep, (long)tm->tm_yday, 3, width, pad); continue; - case 'k': /* (AST) OBSOLETE use %QD */ - p = "%QD"; + case 'k': /* hour (0 - 23) with blank padding */ + p = "%_H"; goto push; case 'K': /* (AST) largest to smallest */ switch (alt) @@ -333,8 +333,8 @@ tmxfmt(char* buf, size_t len, const char* format, Time_t t) break; } goto push; - case 'l': /* (AST) OBSOLETE use %QL */ - p = "%QL"; + case 'l': /* hour (0 - 12) with blank padding */ + p = "%_I"; goto push; case 'L': /* (AST) OBSOLETE use %Ql */ p = "%Ql"; @@ -364,9 +364,9 @@ tmxfmt(char* buf, size_t len, const char* format, Time_t t) while (cp < ep && (n = *p++)) *cp++ = isupper(n) ? tolower(n) : n; continue; - case 'q': /* (AST) OBSOLETE use %Qz */ - p = "%Qz"; - goto push; + case 'q': /* quarter of the year (1-4) */ + cp = number(cp, ep, (long)(tm->tm_mon / 3) + 1, 0, width, pad); + continue; case 'Q': /* (AST) %Q or %Qrecentdistant */ if (c = *format) { diff --git a/src/lib/libcmd/date.c b/src/lib/libcmd/date.c index 3f9a3031f..dd8ad9cd4 100644 --- a/src/lib/libcmd/date.c +++ b/src/lib/libcmd/date.c @@ -95,26 +95,26 @@ USAGE_LICENSE " [+d?day of month number]" " [+D?date as \amm/dd/yy\a]" " [+e?blank padded day of month number]" -" [+f?locale default override date format]" +" [+f?print a date with the format '\%Y.\%m.\%d-\%H:\%M:\%S']" " [+F?%ISO 8601:2000 standard date format; equivalent to Y-%m-%d]" " [+g?\bls\b(1) \b-l\b recent date with \ahh:mm\a]" " [+G?\bls\b(1) \b-l\b distant date with \ayyyy\a]" " [+h?abbreviated month name]" -" [+H?24-hour clock hour]" +" [+H?24-hour clock hour, zero-padded]" " [+i?international \bdate\b(1) date with time zone type name]" -" [+I?12-hour clock hour]" +" [+I?12-hour clock hour, zero-padded]" " [+j?1-offset Julian date]" " [+J?0-offset Julian date]" -" [+k?\bdate\b(1) style date]" +" [+k?24-hour clock hour, blank-padded]" " [+K?all numeric date; equivalent to \b%Y-%m-%d+%H:%M:%S\b; \b%_[EO]]K\b for space separator, %OK adds \b.%N\b, \b%EK\b adds \b%.N%z\b, \b%_EK\b adds \b.%N %z\b]" -" [+l?\bls\b(1) \b-l\b date; equivalent to \b%Q/%g/%G/\b]" +" [+l?12-hour clock hour, blank-padded]" " [+L?locale default date format]" " [+m?month number]" " [+M?minutes]" " [+n?newline character]" " [+N?nanoseconds 000000000-999999999]" " [+p?meridian (e.g., \bAM\b or \bPM\b)]" -" [+q?time zone type name (nation code)]" +" [+q?quarter of the year]" " [+Q?\arecentdistant\a: \a\a is a unique" " delimter character; \arecent\a format for recent" " dates, \adistant\a format otherwise]"