Jim Meyering <[EMAIL PROTECTED]> writes: > Paul Eggert <[EMAIL PROTECTED]> wrote: >> How about if we do it by supporting Internet RFC 3339 instead? > I like this.
OK, I installed the patch below. Comments welcome. In particular, the new %:z, %::z, %:::z strftime formats are a bit weird-looking, but I couldn't think of anything better. >> So, for example: >> >> $ date -i >> 2005-07-22 09:13:17.959906-07:00 > > Normally I would object to a short-named option, but when > the alternative is having to distinguish --rfc-3339 from > --rfc-2822, I think it's justified. Unfortunately I ran into problems when implementing -i (--rfc-3339='clock') because the clock resolution turns out to be hard to get. On my host (Debian stable), for example, clock_getres returns a resolution of 10 ms, but the actual resolution of gettime is 1 microsecond. And time stamps may come from sources other than the hardware clock (e.g., -d, -f, -r) so it may be inappropriate to use the hardware clock resolution even if we could get it correctly. For now I left that part unimplemented, and so the patch below omits --rfc-3339='clock' and -i. 2005-09-13 Paul Eggert <[EMAIL PROTECTED]> * NEWS: date has a new --rfc-3339 option, and the old --iso-8601 option is deprecated. date and ls also have new time format specifiers %:z, %::z, %:::z. * doc/coreutils.texi (Time conversion specifiers, Options for date): Document date --rfc-3339 and new specifiers %:z, %::z, %:::z. Use "date and time" consistently; the old version sometimes said "time and date". Fix a minor bug in the documentation for --rfc-2822: it claimed day-of-month < 10 had leading space, not leading zero. Use a consistent format for terms like "RFC". * lib/strftime.c (my_strftime): Add support for %:z, %::z, %:::z. Fix bug in formats like %2N. * src/date.c (TIME_SPEC_DATE): No longer needs to be nonzero, so remove the "=1". (TIME_SOEC_HOURS, TIME_SPEC_MINUTES): Must be at end now, so put them there. (time_spec_string, time_spec): Hours and minutes must be at start now, so put them there. (rfc_2822_format): Now a string constant, not a boolean. All uses changed. (iso_8601_format, rfc_format): Remove. (RFC_3339_OPTION): New constant. (long_options): Add --rfc-3339. (usage): Add --rfc-3339. Don't mention --iso-8601. Mention %:z, %::z, %:::z. (main): Simplify calculation of 'format'; it was getting too hairy to follow. Add --rfc-3339. (show_date): Assume format arg is not NULL, which is the case now. The default code is moved to 'main'. This simplifies things and allows the default to be calculated just once. * tests/misc/date: Add tests for --rfc-3339. Index: NEWS =================================================================== RCS file: /fetish/cu/NEWS,v retrieving revision 1.308 diff -p -u -r1.308 NEWS --- NEWS 10 Sep 2005 14:07:59 -0000 1.308 +++ NEWS 13 Sep 2005 21:59:04 -0000 @@ -182,6 +182,11 @@ GNU coreutils NEWS cp and mv: the --reply=X option is deprecated + date accepts the new option --rfc-3339=TIMESPEC. The old --iso-8602 (-I) + option is deprecated; it still works, but new applications should avoid it. + date and ls's time formats now support new %:z, %::z, %:::z specifiers + for numeric time zone offsets like -07:00, -07:00:00, and -07. + dd has new iflag= and oflag= flags "binary" and "text", which have an effect only on nonstandard platforms that distinguish text from binary I/O. Index: doc/coreutils.texi =================================================================== RCS file: /fetish/cu/doc/coreutils.texi,v retrieving revision 1.279 diff -p -u -r1.279 coreutils.texi --- doc/coreutils.texi 9 Sep 2005 21:16:49 -0000 1.279 +++ doc/coreutils.texi 13 Sep 2005 21:59:07 -0000 @@ -11593,7 +11593,7 @@ is not set. @xref{TZ Variable,, Specify @cindex time formats @cindex formatting times If given an argument that starts with a @samp{+}, @command{date} prints the -current time and date (or the time and date specified by the +current date and time (or the date and time specified by the @option{--date} option, see below) in the format defined by that argument, which is similar to that of the @code{strftime} function. Except for conversion specifiers, which start with @samp{%}, characters in the @@ -11664,14 +11664,29 @@ This may be @samp{60} if leap seconds ar @item %X locale's time representation (e.g., @samp{23:13:48}) @item %z [EMAIL PROTECTED] 2822/ISO 8601} style numeric time zone (e.g., @samp{-0600} -or @samp{+0100}), or nothing if no [EMAIL PROTECTED]@acronym{RFC} 2822/@acronym{ISO} 8601} style numeric time zone +(e.g., @samp{-0600} or @samp{+0530}), or nothing if no time zone is determinable. This value reflects the numeric time zone appropriate for the current time, using the time zone rules specified by the @env{TZ} environment variable. The time (and optionally, the time zone rules) can be overridden by the @option{--date} option. This is a @acronym{GNU} extension. [EMAIL PROTECTED] %:z [EMAIL PROTECTED]@acronym{RFC} 3339/@acronym{ISO} 8601} style numeric time zone with [EMAIL PROTECTED]:} (e.g., @samp{-06:00} or @samp{+05:30}), or nothing if no time +zone is determinable. +This is a @acronym{GNU} extension. [EMAIL PROTECTED] %::z +Numeric time zone to the nearest second with @samp{:} (e.g., [EMAIL PROTECTED]:00:00} or @samp{+05:30:00}), or nothing if no time zone is +determinable. +This is a @acronym{GNU} extension. [EMAIL PROTECTED] %:::z +Numeric time zone with @samp{:} using the minimum necessary precision +(e.g., @samp{-06}, @samp{+05:30}, or @samp{-04:56:02}), or nothing if +no time zone is determinable. +This is a @acronym{GNU} extension. @item %Z alphabetic time zone abbreviation (e.g., @samp{EDT}), or nothing if no time zone is determinable. See @samp{%z} for how it is determined. @@ -11860,11 +11875,11 @@ is available, it is ignored. @cindex appropriate privileges If given an argument that does not start with @samp{+}, @command{date} sets -the system clock to the time and date specified by that argument (as +the system clock to the date and time specified by that argument (as described below). You must have appropriate privileges to set the system clock. The @option{--date} and @option{--set} options may not be used with such an argument. The @option{--universal} option may be used -with such an argument to indicate that the specified time and date are +with such an argument to indicate that the specified date and time are relative to Coordinated Universal Time rather than to the local time zone. @@ -11912,8 +11927,8 @@ The program accepts the following option @opindex tomorrow @opindex next @var{day} @opindex last @var{day} -Display the time and date specified in @var{datestr} instead of the -current time and date. @var{datestr} can be in almost any common +Display the date and time specified in @var{datestr} instead of the +current date and time. @var{datestr} can be in almost any common format. It can contain month names, time zones, @samp{am} and @samp{pm}, @samp{yesterday}, etc. For example, @option{--date="2004-02-27 14:19:13.489392193 +0530"} specifies the instant of time that is @@ -11926,38 +11941,17 @@ time zone that is 5 hours and 30 minutes @opindex -f @opindex --file Parse each line in @var{datefile} as with @option{-d} and display the -resulting time and date. If @var{datefile} is @samp{-}, use standard +resulting date and time. If @var{datefile} is @samp{-}, use standard input. This is useful when you have many dates to process, because the system overhead of starting up the @command{date} executable many times can be considerable. [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] -Display the date using the @acronym{ISO} 8601 format, @samp{%Y-%m-%d}. - -The argument @var{timespec} specifies the number of additional -terms of the time to include. It can be one of the following: [EMAIL PROTECTED] @samp [EMAIL PROTECTED] auto -Print just the date. This is the default if @var{timespec} is omitted. - [EMAIL PROTECTED] hours -Append the hour of the day to the date. - [EMAIL PROTECTED] minutes -Append the hours and minutes. - [EMAIL PROTECTED] seconds -Append the hours, minutes, and seconds. - [EMAIL PROTECTED] ns -Append the hours, minutes, seconds, and nanoseconds. [EMAIL PROTECTED] table - -If showing any time terms, then include the time zone using the format [EMAIL PROTECTED] [EMAIL PROTECTED] -r @var{file} [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] -r [EMAIL PROTECTED] --reference +Display the date and time of the last modification of @var{file}, +instead of the current date and time. @item -R @itemx --rfc-822 @@ -11965,31 +11959,58 @@ If showing any time terms, then include @opindex -R @opindex --rfc-822 @opindex --rfc-2822 -Display the time and date using the format @samp{%a, %d %b %Y %H:%M:%S +Display the date and time using the format @samp{%a, %d %b %Y %H:%M:%S %z}, evaluated in the C locale so abbreviations are always in English. For example: @example -Fri,@ @ 1 Aug 2003 23:05:56 -0700 +Fri, 09 Sep 2005 13:51:39 -0700 @end example This format conforms to [EMAIL PROTECTED]://ftp.rfc-editor.org/in-notes/rfc2822.txt, RFC 2822} and [EMAIL PROTECTED]://ftp.rfc-editor.org/in-notes/rfc822.txt, RFC 822}, the [EMAIL PROTECTED]://ftp.rfc-editor.org/in-notes/rfc2822.txt, Internet [EMAIL PROTECTED] 2822} and [EMAIL PROTECTED]://ftp.rfc-editor.org/in-notes/rfc822.txt, 822}, the current and previous standards for Internet email. [EMAIL PROTECTED] -r @var{file} [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] -r [EMAIL PROTECTED] --reference -Display the time and date reference according to the last modification -time of @var{file}, instead of the current time and date. [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] +Display the date using a format specified by [EMAIL PROTECTED]://ftp.rfc-editor.org/in-notes/rfc3339.txt, Internet [EMAIL PROTECTED] 3339}. This is a subset of the @acronym{ISO} 8601 +format, except that it also permits applications to use a space rather +than a @samp{T} to separate dates from times. Unlike the other +standard formats, @acronym{RFC} 3339 format is always suitable as +input for the @option{--date} (@option{-d}) and @option{--file} +(@option{-f}) options, regardless of the current locale. + +The argument @var{timespec} specifies how much of the time to include. +It can be one of the following: + [EMAIL PROTECTED] @samp [EMAIL PROTECTED] date +Print just the full-date, e.g., @samp{2005-09-14}. +This is equivalent to the format @samp{%Y-%m-%d}. + [EMAIL PROTECTED] seconds +Print the full-date and full-time separated by a space, e.g., [EMAIL PROTECTED] 00:56:06+05:30}. The output ends with a numeric +time-offset; here the @samp{+05:30} means that local time is five +hours and thirty minutes east of @acronym{UTC}. This is equivalent to +the format @samp{%Y-%m-%d %H:%M:%S%:z}. + [EMAIL PROTECTED] ns +Like @samp{seconds}, but also print nanoseconds, e.g., [EMAIL PROTECTED] 00:56:06.998458565+05:30}. +This is equivalent to the format @samp{%Y-%m-%d %H:%M:%S.%N%:z}. + [EMAIL PROTECTED] table @item -s @var{datestr} @itemx [EMAIL PROTECTED] @opindex -s @opindex --set -Set the time and date to @var{datestr}. See @option{-d} above. +Set the date and time to @var{datestr}. See @option{-d} above. @item -u @itemx --utc @@ -12078,11 +12099,11 @@ date --set='+2 minutes' @end example @item -To print the date in the format specified by RFC-2822, -use @samp{date --rfc-2822}. I just did and saw this: +To print the date in @acronym{RFC} 2822 format, +use @samp{date --rfc-2822}. Here is some example output: @example -Thu, 31 Jul 2003 13:13:05 -0700 +Fri, 09 Sep 2005 13:51:39 -0700 @end example @anchor{%s-examples} Index: lib/strftime.c =================================================================== RCS file: /fetish/cu/lib/strftime.c,v retrieving revision 1.87 diff -p -u -r1.87 strftime.c --- lib/strftime.c 17 Aug 2005 19:55:52 -0000 1.87 +++ lib/strftime.c 13 Sep 2005 21:59:08 -0000 @@ -480,8 +480,11 @@ my_strftime (CHAR_T *s, size_t maxsize, int digits; /* Max digits for numeric format. */ int number_value; /* Numeric value to be printed. */ unsigned int u_number_value; /* (unsigned int) number_value. */ - bool negative_number; /* 1 if the number is negative. */ + bool negative_number; /* The number is negative. */ + bool always_output_a_sign; /* +/- should always be output. */ + int tz_colon_mask; /* Bitmask of where ':' should appear. */ const CHAR_T *subfmt; + CHAR_T sign_char; CHAR_T *bufp; CHAR_T buf[1 + (sizeof (int) < sizeof (time_t) ? INT_STRLEN_BOUND (time_t) @@ -489,6 +492,7 @@ my_strftime (CHAR_T *s, size_t maxsize, int width = -1; bool to_lowcase = false; bool to_uppcase = false; + size_t colons = 0; bool change_case = false; int format_char; @@ -592,6 +596,11 @@ my_strftime (CHAR_T *s, size_t maxsize, pad = *f; continue; + /* This influences the %z format. */ + case L_(':'): + colons++; + continue; + /* This changes textual output. */ case L_('^'): to_uppcase = true; @@ -650,6 +659,11 @@ my_strftime (CHAR_T *s, size_t maxsize, digits = d; \ negative_number = negative; \ u_number_value = v; goto do_signed_number +#define DO_TZ_OFFSET(d, negative, mask, v) \ + digits = d; \ + negative_number = negative; \ + tz_colon_mask = mask; \ + u_number_value = v; goto do_tz_offset #define DO_NUMBER_SPACEPAD(d, v) \ digits = d; \ number_value = v; goto do_number_spacepad @@ -857,6 +871,10 @@ my_strftime (CHAR_T *s, size_t maxsize, /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE) and then jump to one of these three labels. */ + do_tz_offset: + always_output_a_sign = true; + goto do_number_body; + do_number_spacepad: /* Force `_' flag unless overridden by `0' or `-' flag. */ if (pad != L_('0') && pad != L_('-')) @@ -868,6 +886,10 @@ my_strftime (CHAR_T *s, size_t maxsize, u_number_value = number_value; do_signed_number: + always_output_a_sign = false; + tz_colon_mask = 0; + + do_number_body: /* Format U_NUMBER_VALUE according to the MODIFIER flag. NEGATIVE_NUMBER is nonzero if the original number was negative; in this case it was converted directly to @@ -904,17 +926,24 @@ my_strftime (CHAR_T *s, size_t maxsize, do { + if (tz_colon_mask & 1) + *--bufp = ':'; + tz_colon_mask >>= 1; *--bufp = u_number_value % 10 + L_('0'); u_number_value /= 10; } - while (u_number_value != 0); + while (u_number_value != 0 || tz_colon_mask != 0); do_number_sign_and_padding: if (digits < width) digits = width; - if (negative_number) - *--bufp = L_('-'); + sign_char = (negative_number ? L_('-') + : always_output_a_sign ? L_('+') + : 0); + + if (sign_char) + *--bufp = sign_char; if (pad != L_('-')) { @@ -938,12 +967,12 @@ my_strftime (CHAR_T *s, size_t maxsize, if ((size_t) digits >= maxsize - i) return 0; - if (negative_number) + if (sign_char) { ++bufp; if (p) - *p++ = L_('-'); + *p++ = sign_char; ++i; } @@ -1012,7 +1041,9 @@ my_strftime (CHAR_T *s, size_t maxsize, goto bad_format; number_value = ns; - if (width != -1) + if (width == -1) + width = 9; + else { /* Take an explicit width less than 9 as a precision. */ int j; @@ -1020,7 +1051,7 @@ my_strftime (CHAR_T *s, size_t maxsize, number_value /= 10; } - DO_NUMBER (9, number_value); + DO_NUMBER (width, number_value); #endif case L_('n'): @@ -1093,6 +1124,7 @@ my_strftime (CHAR_T *s, size_t maxsize, while (t != 0); digits = 1; + always_output_a_sign = false; goto do_number_sign_and_padding; } @@ -1286,6 +1318,9 @@ my_strftime (CHAR_T *s, size_t maxsize, { int diff; + int hour_diff; + int min_diff; + int sec_diff; #if HAVE_TM_GMTOFF diff = tp->tm_gmtoff; #else @@ -1324,16 +1359,32 @@ my_strftime (CHAR_T *s, size_t maxsize, } #endif - if (diff < 0) + hour_diff = diff / 60 / 60; + min_diff = diff / 60 % 60; + sec_diff = diff % 60; + + switch (colons) { - add (1, *p = L_('-')); - diff = -diff; - } - else - add (1, *p = L_('+')); + case 0: /* +hhmm */ + DO_TZ_OFFSET (5, diff < 0, 0, hour_diff * 100 + min_diff); - diff /= 60; - DO_NUMBER (4, (diff / 60) * 100 + diff % 60); + case 1: tz_hh_mm: /* +hh:mm */ + DO_TZ_OFFSET (6, diff < 0, 04, hour_diff * 100 + min_diff); + + case 2: tz_hh_mm_ss: /* +hh:mm:ss */ + DO_TZ_OFFSET (9, diff < 0, 044, + hour_diff * 10000 + min_diff * 100 + sec_diff); + + case 3: /* +hh if possible, else +hh:mm, else +hh:mm:ss */ + if (sec_diff != 0) + goto tz_hh_mm_ss; + if (min_diff != 0) + goto tz_hh_mm; + DO_TZ_OFFSET (3, diff < 0, 0, hour_diff); + + default: + goto bad_format; + } } case L_('\0'): /* GNU extension: % at end of format. */ Index: src/date.c =================================================================== RCS file: /fetish/cu/src/date.c,v retrieving revision 1.155 diff -p -u -r1.155 date.c --- src/date.c 15 Aug 2005 12:16:54 -0000 1.155 +++ src/date.c 13 Sep 2005 21:59:08 -0000 @@ -47,37 +47,47 @@ static bool show_date (const char *forma enum Time_spec { - /* display only the date: 1999-03-25 */ - TIME_SPEC_DATE=1, - /* display date and hour: 1999-03-25T03-0500 */ - TIME_SPEC_HOURS, - /* display date, hours, and minutes: 1999-03-25T03:23-0500 */ - TIME_SPEC_MINUTES, - /* display date, hours, minutes, and seconds: 1999-03-25T03:23:14-0500 */ + /* Display only the date. */ + TIME_SPEC_DATE, + /* Display date, hours, minutes, and seconds. */ TIME_SPEC_SECONDS, - /* similar, but display nanoseconds: 1999-03-25T03:23:14,123456789-0500 */ - TIME_SPEC_NS + /* Similar, but display nanoseconds. */ + TIME_SPEC_NS, + + /* Put these last, since they aren't valid for --rfc-3339. */ + + /* Display date and hour. */ + TIME_SPEC_HOURS, + /* Display date, hours, and minutes. */ + TIME_SPEC_MINUTES }; static char const *const time_spec_string[] = { - "date", "hours", "minutes", "seconds", "ns", NULL + /* Put "hours" and "minutes" first, since they aren't valid for + --rfc-3339. */ + "hours", "minutes", + "date", "seconds", "ns", NULL }; static enum Time_spec const time_spec[] = { - TIME_SPEC_DATE, TIME_SPEC_HOURS, TIME_SPEC_MINUTES, TIME_SPEC_SECONDS, - TIME_SPEC_NS + TIME_SPEC_HOURS, TIME_SPEC_MINUTES, + TIME_SPEC_DATE, TIME_SPEC_SECONDS, TIME_SPEC_NS }; ARGMATCH_VERIFY (time_spec_string, time_spec); +/* A format suitable for Internet RFC 2822. */ +static char const rfc_2822_format[] = "%a, %d %b %Y %H:%M:%S %z"; + /* The name this program was run with, for error messages. */ char *program_name; -/* If nonzero, display an ISO 8601 format date/time string */ -static int iso_8601_format = 0; - -/* If true, display time in RFC-(2)822 format for mail or news. */ -static bool rfc_format = false; +/* For long options that have no equivalent short option, use a + non-character as a pseudo short option, starting with CHAR_MAX + 1. */ +enum +{ + RFC_3339_OPTION = CHAR_MAX + 1 +}; static char const short_options[] = "d:f:I::r:Rs:u"; @@ -85,10 +95,11 @@ static struct option const long_options[ { {"date", required_argument, NULL, 'd'}, {"file", required_argument, NULL, 'f'}, - {"iso-8601", optional_argument, NULL, 'I'}, + {"iso-8601", optional_argument, NULL, 'I'}, /* Deprecated. */ {"reference", required_argument, NULL, 'r'}, {"rfc-822", no_argument, NULL, 'R'}, {"rfc-2822", no_argument, NULL, 'R'}, + {"rfc-3339", required_argument, NULL, RFC_3339_OPTION}, {"set", required_argument, NULL, 's'}, {"uct", no_argument, NULL, 'u'}, {"utc", no_argument, NULL, 'u'}, @@ -128,14 +139,13 @@ Display the current time in the given FO \n\ -d, --date=STRING display time described by STRING, not `now'\n\ -f, --file=DATEFILE like --date once for each line of DATEFILE\n\ - -I[TIMESPEC], --iso-8601[=TIMESPEC] output date/time in ISO 8601 format.\n\ - TIMESPEC=`date' for date only (the default),\n\ - `hours', `minutes', `seconds', or `ns' for date and\n\ - time to the indicated precision.\n\ "), stdout); fputs (_("\ -r, --reference=FILE display the last modification time of FILE\n\ - -R, --rfc-2822 output RFC-2822 compliant date string\n\ + -R, --rfc-2822 output date and time in RFC 2822 format\n\ + --rfc-3339=TIMESPEC output date and time in RFC 3339 format.\n\ + TIMESPEC=`date', `seconds', or `ns' for\n\ + date and time to the indicated precision.\n\ -s, --set=STRING set time described by STRING\n\ -u, --utc, --universal print or set Coordinated Universal Time\n\ "), stdout); @@ -206,7 +216,10 @@ specifies Coordinated Universal Time. I %Y year\n\ "), stdout); fputs (_("\ - %z numeric timezone (e.g., -0400)\n\ + %z +hhmm numeric timezone (e.g., -0400)\n\ + %:z +hh:mm numeric timezone (e.g., -04:00)\n\ + %::z +hh:mm:ss numeric time zone (e.g., -04:00:00)\n\ + %:::z numeric time zone with : to necessary precision (e.g., -04, +05:30)\n\ %Z alphabetic time zone abbreviation (e.g., EDT)\n\ \n\ By default, date pads numeric fields with zeroes.\n\ @@ -299,11 +312,10 @@ main (int argc, char **argv) const char *set_datestr = NULL; struct timespec when; bool set_date = false; - char *format; + char const *format = NULL; char *batch_file = NULL; char *reference = NULL; struct stat refstats; - int n_args; bool ok; int option_specified_date; @@ -317,45 +329,79 @@ main (int argc, char **argv) while ((optc = getopt_long (argc, argv, short_options, long_options, NULL)) != -1) - switch (optc) - { - case 'd': - datestr = optarg; - break; - case 'f': - batch_file = optarg; - break; - case 'I': - iso_8601_format = (optarg - ? XARGMATCH ("--iso-8601", optarg, - time_spec_string, time_spec) - : TIME_SPEC_DATE); - break; - case 'r': - reference = optarg; - break; - case 'R': - rfc_format = true; - break; - case 's': - set_datestr = optarg; - set_date = true; - break; - case 'u': - /* POSIX says that `date -u' is equivalent to setting the TZ - environment variable, so this option should do nothing other - than setting TZ. */ - if (putenv ("TZ=UTC0") != 0) - xalloc_die (); - TZSET; - break; - case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - default: - usage (EXIT_FAILURE); - } + { + char const *new_format = NULL; - n_args = argc - optind; + switch (optc) + { + case 'd': + datestr = optarg; + break; + case 'f': + batch_file = optarg; + break; + case RFC_3339_OPTION: + { + static char const rfc_3339_format[][32] = + { + "%Y-%m-%d", + "%Y-%m-%d %H:%M:%S%:z", + "%Y-%m-%d %H:%M:%S.%N%:z" + }; + enum Time_spec i = + XARGMATCH ("--rfc-3339", optarg, + time_spec_string + 2, time_spec + 2); + new_format = rfc_3339_format[i]; + break; + } + case 'I': + { + static char const iso_8601_format[][32] = + { + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S%z", + "%Y-%m-%dT%H:%M:%S,%N%z", + "%Y-%m-%dT%H%z", + "%Y-%m-%dT%H:%M%z" + }; + enum Time_spec i = + (optarg + ? XARGMATCH ("--iso-8601", optarg, time_spec_string, time_spec) + : TIME_SPEC_DATE); + new_format = iso_8601_format[i]; + break; + } + case 'r': + reference = optarg; + break; + case 'R': + new_format = rfc_2822_format; + break; + case 's': + set_datestr = optarg; + set_date = true; + break; + case 'u': + /* POSIX says that `date -u' is equivalent to setting the TZ + environment variable, so this option should do nothing other + than setting TZ. */ + if (putenv ("TZ=UTC0") != 0) + xalloc_die (); + TZSET; + break; + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: + usage (EXIT_FAILURE); + } + + if (new_format) + { + if (format) + error (EXIT_FAILURE, 0, _("multiple output formats specified")); + format = new_format; + } + } option_specified_date = ((datestr ? 1 : 0) + (batch_file ? 1 : 0) @@ -375,37 +421,49 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } - if (n_args > 1) + if (optind < argc) { - error (0, 0, _("extra operand %s"), quote (argv[optind + 1])); - usage (EXIT_FAILURE); - } + if (optind + 1 < argc) + { + error (0, 0, _("extra operand %s"), quote (argv[optind + 1])); + usage (EXIT_FAILURE); + } - if ((set_date || option_specified_date) - && n_args == 1 && argv[optind][0] != '+') - { - error (0, 0, _("\ -the argument %s lacks a leading `+';\n\ -When using an option to specify date(s), any non-option\n\ -argument must be a format string beginning with `+'."), - quote (argv[optind])); - usage (EXIT_FAILURE); + if (argv[optind][0] == '+') + { + if (format) + error (EXIT_FAILURE, 0, _("multiple output formats specified")); + format = argv[optind++] + 1; + } + else if (set_date || option_specified_date) + { + error (0, 0, + _("the argument %s lacks a leading `+';\n" + "When using an option to specify date(s), any non-option\n" + "argument must be a format string beginning with `+'."), + quote (argv[optind])); + usage (EXIT_FAILURE); + } } - /* Simply ignore --rfc-2822 if specified when setting the date. */ - if (rfc_format && !set_date && n_args > 0) + if (!format) { - error (0, 0, - _("a format string may not be specified when using\ - the --rfc-2822 (-R) option")); - usage (EXIT_FAILURE); + format = DATE_FMT_LANGINFO (); + if (! *format) + { + /* Do not wrap the following literal format string with _(...). + For example, suppose LC_ALL is unset, LC_TIME="POSIX", + and LANG="ko_KR". In that case, POSIX says that LC_TIME + determines the format and contents of date and time strings + written by date, which means "date" must generate output + using the POSIX locale; but adding _() would cause "date" + to use a Korean translation of the format. */ + format = "%a %b %e %H:%M:%S %Z %Y"; + } } - if (set_date) - datestr = set_datestr; - if (batch_file != NULL) - ok = batch_convert (batch_file, (n_args == 1 ? argv[optind] + 1 : NULL)); + ok = batch_convert (batch_file, format); else { bool valid_date = true; @@ -413,7 +471,7 @@ argument must be a format string beginni if (!option_specified_date && !set_date) { - if (n_args == 1 && argv[optind][0] != '+') + if (optind < argc) { /* Prepare to set system clock to the specified date/time given in the POSIX-format. */ @@ -424,14 +482,11 @@ argument must be a format string beginni (PDS_TRAILING_YEAR | PDS_CENTURY | PDS_SECONDS)); when.tv_nsec = 0; /* FIXME: posixtime should set this. */ - format = NULL; } else { /* Prepare to print the current date/time. */ - datestr = _("undefined"); gettime (&when); - format = (n_args == 1 ? argv[optind] + 1 : NULL); } } else @@ -446,10 +501,10 @@ argument must be a format string beginni } else { + if (set_datestr) + datestr = set_datestr; valid_date = get_date (&when, datestr, NULL); } - - format = (n_args == 1 ? argv[optind] + 1 : NULL); } if (! valid_date) @@ -481,35 +536,6 @@ static bool show_date (const char *format, struct timespec when) { struct tm *tm; - /* ISO 8601 formats. See below regarding %z */ - static char const * const iso_format_string[] = - { - "%Y-%m-%d", - "%Y-%m-%dT%H%z", - "%Y-%m-%dT%H:%M%z", - "%Y-%m-%dT%H:%M:%S%z", - "%Y-%m-%dT%H:%M:%S,%N%z" - }; - - if (format == NULL) - { - if (rfc_format) - format = "%a, %d %b %Y %H:%M:%S %z"; - else if (iso_8601_format) - format = iso_format_string[iso_8601_format - 1]; - else - { - char *date_fmt = DATE_FMT_LANGINFO (); - /* Do not wrap the following literal format string with _(...). - For example, suppose LC_ALL is unset, LC_TIME="POSIX", - and LANG="ko_KR". In that case, POSIX says that LC_TIME - determines the format and contents of date and time strings - written by date, which means "date" must generate output - using the POSIX locale; but adding _() would cause "date" - to use a Korean translation of the format. */ - format = *date_fmt ? date_fmt : "%a %b %e %H:%M:%S %Z %Y"; - } - } tm = localtime (&when.tv_sec); if (! tm) @@ -525,10 +551,10 @@ show_date (const char *format, struct ti { char *out; - if (rfc_format) + if (format == rfc_2822_format) setlocale (LC_TIME, "C"); out = xanstrftime (format, tm, 0, when.tv_nsec); - if (rfc_format) + if (format == rfc_2822_format) setlocale (LC_TIME, ""); puts (out); Index: tests/misc/date =================================================================== RCS file: /fetish/cu/tests/misc/date,v retrieving revision 1.12 diff -p -u -r1.12 date --- tests/misc/date 9 Sep 2005 07:22:27 -0000 1.12 +++ tests/misc/date 13 Sep 2005 21:59:08 -0000 @@ -166,19 +166,29 @@ my @Tests = # Ensure that we can parse MONTHNAME-DAY-YEAR. ['moname-d-y', '--iso -d May-23-2003', {OUT=>"2003-05-23"}], + ['moname-d-y-r', '--rfc-3339=date -d May-23-2003', {OUT=>"2003-05-23"}], ['epoch', '--iso=sec -d @31536000', {OUT=>"1971-01-01T00:00:00+0000"}], + ['epoch-r', '--rfc-3339=sec -d @31536000', + {OUT=>"1971-01-01 00:00:00+00:00"}], ['ns-10', '--iso=ns', '-d "1969-12-31 13:00:00.00000001-1100"', {OUT=>"1970-01-01T00:00:00,000000010+0000"}], + ['ns-10-r', '--rfc-3339=ns', '-d "1969-12-31 13:00:00.00000001-1100"', + {OUT=>"1970-01-01 00:00:00.000000010+00:00"}], ['ns-max32', '--iso=ns', '-d "2038-01-19 03:14:07.999999999"', {OUT=>"2038-01-19T03:14:07,999999999+0000"}], + ['ns-max32-r', '--rfc-3339=ns', '-d "2038-01-19 03:14:07.999999999"', + {OUT=>"2038-01-19 03:14:07.999999999+00:00"}], ['ns-relative', '--iso=ns', "-d'1970-01-01 00:00:00.1234567 UTC +961062237.987654321 sec'", {OUT=>"2000-06-15T09:43:58,111111021+0000"}], + ['ns-relativer', '--rfc-3339=ns', + "-d'1970-01-01 00:00:00.1234567 UTC +961062237.987654321 sec'", + {OUT=>"2000-06-15 09:43:58.111111021+00:00"}], # Since coreutils/lib/getdate.y revision 1.96 (post-coreutils-5.3.0), # a command like the following would mistakenly exit nonzero with an _______________________________________________ Bug-coreutils mailing list Bug-coreutils@gnu.org http://lists.gnu.org/mailman/listinfo/bug-coreutils