X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fdate.c;h=af9485b7436f2210181f5ff4bb8c6ca29d78e049;hb=158d982669d3d05079da2b9cc8d8fcd8adf35a4b;hp=c52a0292551bb97a80188ee9b3e1cc8e41641a0b;hpb=154e260c9d61264ea99a0375f36de76868264a31;p=platform%2Fupstream%2Fcoreutils.git diff --git a/src/date.c b/src/date.c index c52a029..af9485b 100644 --- a/src/date.c +++ b/src/date.c @@ -1,5 +1,5 @@ /* date - print or set the system date and time - Copyright (C) 1989-2002 Free Software Foundation, Inc. + Copyright (C) 1989-2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,7 +13,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. David MacKenzie */ @@ -21,71 +21,85 @@ #include #include #include -#if HAVE_LANGINFO_H +#if HAVE_LANGINFO_CODESET # include #endif #include "system.h" #include "argmatch.h" -#include "closeout.h" #include "error.h" #include "getdate.h" #include "getline.h" +#include "inttostr.h" #include "posixtm.h" +#include "quote.h" +#include "stat-time.h" +#include "fprintftime.h" /* The official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "date" #define AUTHORS "David MacKenzie" -#ifndef STDC_HEADERS -size_t strftime (); -time_t time (); -#endif - int putenv (); -int stime (); -static void show_date PARAMS ((const char *format, time_t when)); +static bool show_date (const char *format, struct timespec when); enum Time_spec { - /* display only the date: 1999-03-25 */ - TIME_SPEC_DATE=1, - /* display date and hour: 1999-03-25T03-0500 */ + /* Display only the date. */ + TIME_SPEC_DATE, + /* Display date, hours, minutes, and seconds. */ + TIME_SPEC_SECONDS, + /* 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: 1999-03-25T03:23-0500 */ - TIME_SPEC_MINUTES, - /* display date, hours, minutes, and seconds: 1999-03-25T03:23:14-0500 */ - TIME_SPEC_SECONDS + /* Display date, hours, and minutes. */ + TIME_SPEC_MINUTES }; static char const *const time_spec_string[] = { - "date", "hours", "minutes", "seconds", 0 + /* 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_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; +/* 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 +}; -/* If non-zero, display time in RFC-(2)822 format for mail or news. */ -static int rfc_format = 0; +static char const short_options[] = "d:f:I::r:Rs:u"; 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'}, @@ -110,7 +124,7 @@ static struct option const long_options[] = void usage (int status) { - if (status != 0) + if (status != EXIT_SUCCESS) fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name); else @@ -125,14 +139,13 @@ Display the current time in the given FORMAT, or set the system date.\n\ \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, --iso-8601[=TIMESPEC] output an ISO-8601 compliant date/time string.\n\ - TIMESPEC=`date' (or missing) for date only,\n\ - `hours', `minutes', or `seconds' 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-822 output RFC-822 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); @@ -144,24 +157,24 @@ FORMAT controls the output. The only valid option for the second form\n\ specifies Coordinated Universal Time. Interpreted sequences are:\n\ \n\ %% a literal %\n\ - %a locale's abbreviated weekday name (Sun..Sat)\n\ + %a locale's abbreviated weekday name (e.g., Sun)\n\ "), stdout); fputs (_("\ - %A locale's full weekday name, variable length (Sunday..Saturday)\n\ - %b locale's abbreviated month name (Jan..Dec)\n\ - %B locale's full month name, variable length (January..December)\n\ - %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)\n\ + %A locale's full weekday name (e.g., Sunday)\n\ + %b locale's abbreviated month name (e.g., Jan)\n\ + %B locale's full month name (e.g., January)\n\ + %c locale's date and time (e.g., Thu Mar 3 23:05:25 2005)\n\ "), stdout); fputs (_("\ - %C century (year divided by 100 and truncated to an integer) [00-99]\n\ - %d day of month (01..31)\n\ - %D date (mm/dd/yy)\n\ - %e day of month, blank padded ( 1..31)\n\ + %C century; like %Y, except omit last two digits (e.g., 21)\n\ + %d day of month (e.g, 01)\n\ + %D date; same as %m/%d/%y\n\ + %e day of month, space padded; same as %_d\n\ "), stdout); fputs (_("\ - %F same as %Y-%m-%d\n\ - %g the 2-digit year corresponding to the %V week number\n\ - %G the 4-digit year corresponding to the %V week number\n\ + %F full date; same as %Y-%m-%d\n\ + %g the last two digits of the year corresponding to the %V week number\n\ + %G the year corresponding to the %V week number\n\ "), stdout); fputs (_("\ %h same as %b\n\ @@ -177,41 +190,55 @@ specifies Coordinated Universal Time. Interpreted sequences are:\n\ "), stdout); fputs (_("\ %n a newline\n\ - %p locale's upper case AM or PM indicator\n\ - %P locale's lower case am or pm indicator\n\ - %r time, 12-hour (hh:mm:ss [AP]M)\n\ - %R time, 24-hour (hh:mm)\n\ - %s seconds since `00:00:00 1970-01-01 UTC' (a GNU extension)\n\ + %N nanoseconds (000000000..999999999)\n\ + %p locale's equivalent of either AM or PM; blank if not known\n\ + %P like %p, but lower case\n\ + %r locale's 12-hour clock time (e.g., 11:11:04 PM)\n\ + %R 24-hour hour and minute; same as %H:%M\n\ + %s seconds since 1970-01-01 00:00:00 UTC\n\ "), stdout); fputs (_("\ %S second (00..60)\n\ - %t a horizontal tab\n\ - %T time, 24-hour (hh:mm:ss)\n\ - %u day of week (1..7); 1 represents Monday\n\ + %t a tab\n\ + %T time; same as %H:%M:%S\n\ + %u day of week (1..7); 1 is Monday\n\ "), stdout); fputs (_("\ %U week number of year with Sunday as first day of week (00..53)\n\ %V week number of year with Monday as first day of week (01..53)\n\ - %w day of week (0..6); 0 represents Sunday\n\ + %w day of week (0..6); 0 is Sunday\n\ %W week number of year with Monday as first day of week (00..53)\n\ "), stdout); fputs (_("\ - %x locale's date representation (mm/dd/yy)\n\ - %X locale's time representation (%H:%M:%S)\n\ + %x locale's date representation (e.g., 12/31/99)\n\ + %X locale's time representation (e.g., 23:13:48)\n\ %y last two digits of year (00..99)\n\ - %Y year (1970...)\n\ + %Y year\n\ "), stdout); fputs (_("\ - %z RFC-822 style numeric timezone (-0500) (a nonstandard extension)\n\ - %Z time zone (e.g., EDT), or nothing if no time zone is determinable\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\ +The following optional flags may follow `%':\n\ \n\ -By default, date pads numeric fields with zeroes. GNU date recognizes\n\ -the following modifiers between `%' and a numeric directive.\n\ + - (hyphen) do not pad the field\n\ + _ (underscore) pad with spaces\n\ + 0 (zero) pad with zeros\n\ + ^ use upper case if possible\n\ + # use opposite case if possible\n\ +"), stdout); + fputs (_("\ \n\ - `-' (hyphen) do not pad the field\n\ - `_' (underscore) pad the field with spaces\n\ +After any flags comes an optional field width, as a decimal number;\n\ +then an optional modifier, which is either\n\ +E to use the locale's alternate representations if available, or\n\ +O to use the locale's alternate numeric symbols if available.\n\ "), stdout); - puts (_("\nReport bugs to .")); + printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); } exit (status); } @@ -219,19 +246,18 @@ the following modifiers between `%' and a numeric directive.\n\ /* Parse each line in INPUT_FILENAME as with --date and display each resulting time and date. If the file cannot be opened, tell why then exit. Issue a diagnostic for any lines that cannot be parsed. - If any line cannot be parsed, return nonzero; otherwise return zero. */ + Return true if successful. */ -static int +static bool batch_convert (const char *input_filename, const char *format) { - int status; + bool ok; FILE *in_stream; char *line; - int line_length; size_t buflen; - time_t when; + struct timespec when; - if (strcmp (input_filename, "-") == 0) + if (STREQ (input_filename, "-")) { input_filename = _("standard input"); in_stream = stdin; @@ -241,44 +267,41 @@ batch_convert (const char *input_filename, const char *format) in_stream = fopen (input_filename, "r"); if (in_stream == NULL) { - error (1, errno, "`%s'", input_filename); + error (EXIT_FAILURE, errno, "%s", quote (input_filename)); } } line = NULL; buflen = 0; - status = 0; + ok = true; while (1) { - line_length = getline (&line, &buflen, in_stream); + ssize_t line_length = getline (&line, &buflen, in_stream); if (line_length < 0) { /* FIXME: detect/handle error here. */ break; } - when = get_date (line, NULL); - - if (when == -1) + if (! get_date (&when, line, NULL)) { if (line[line_length - 1] == '\n') line[line_length - 1] = '\0'; - error (0, 0, _("invalid date `%s'"), line); - status = 1; + error (0, 0, _("invalid date %s"), quote (line)); + ok = false; } else { - show_date (format, when); + ok &= show_date (format, when); } } if (fclose (in_stream) == EOF) - error (2, errno, "`%s'", input_filename); + error (EXIT_FAILURE, errno, "%s", quote (input_filename)); - if (line != NULL) - free (line); + free (line); - return status; + return ok; } int @@ -287,67 +310,98 @@ main (int argc, char **argv) int optc; const char *datestr = NULL; const char *set_datestr = NULL; - time_t when; - int set_date = 0; - char *format; + struct timespec when; + bool set_date = false; + char const *format = NULL; char *batch_file = NULL; char *reference = NULL; struct stat refstats; - int n_args; - int status; + bool ok; int option_specified_date; + initialize_main (&argc, &argv); program_name = argv[0]; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); - close_stdout_set_status (2); atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "d:f:I::r:Rs:u", long_options, NULL)) + while ((optc = getopt_long (argc, argv, short_options, long_options, NULL)) != -1) - switch (optc) - { - case 0: - break; - 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 = 1; - break; - case 's': - set_datestr = optarg; - set_date = 1; - 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 (1); - } - - n_args = argc - optind; + { + char const *new_format = NULL; + + 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) @@ -357,73 +411,82 @@ main (int argc, char **argv) { error (0, 0, _("the options to specify dates for printing are mutually exclusive")); - usage (1); + usage (EXIT_FAILURE); } if (set_date && option_specified_date) { error (0, 0, _("the options to print and set the time may not be used together")); - usage (1); + usage (EXIT_FAILURE); } - if (n_args > 1) + if (optind < argc) { - error (0, 0, _("too many non-option arguments: %s%s"), - argv[optind + 1], n_args == 2 ? "" : " ..."); - usage (1); - } + 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 `+'."), - argv[optind]); - usage (1); + 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-822 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-822 (-R) option")); - usage (1); + 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) - { - status = batch_convert (batch_file, - (n_args == 1 ? argv[optind] + 1 : NULL)); - } + ok = batch_convert (batch_file, format); else { - status = 0; + bool valid_date = true; + ok = true; 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. */ - set_date = 1; + set_date = true; datestr = argv[optind]; - when = posixtime (datestr, - PDS_TRAILING_YEAR | PDS_CENTURY | PDS_SECONDS); - format = NULL; + valid_date = posixtime (&when.tv_sec, + datestr, + (PDS_TRAILING_YEAR + | PDS_CENTURY | PDS_SECONDS)); + when.tv_nsec = 0; /* FIXME: posixtime should set this. */ } else { /* Prepare to print the current date/time. */ - datestr = _("undefined"); - time (&when); - format = (n_args == 1 ? argv[optind] + 1 : NULL); + gettime (&when); } } else @@ -431,115 +494,66 @@ argument must be a format string beginning with `+'."), /* (option_specified_date || set_date) */ if (reference != NULL) { - if (stat (reference, &refstats)) - error (1, errno, "%s", reference); - when = refstats.st_mtime; + if (stat (reference, &refstats) != 0) + error (EXIT_FAILURE, errno, "%s", reference); + when = get_stat_mtime (&refstats); } else { - when = get_date (datestr, NULL); + if (set_datestr) + datestr = set_datestr; + valid_date = get_date (&when, datestr, NULL); } - - format = (n_args == 1 ? argv[optind] + 1 : NULL); } - if (when == -1) - error (1, 0, _("invalid date `%s'"), datestr); + if (! valid_date) + error (EXIT_FAILURE, 0, _("invalid date %s"), quote (datestr)); if (set_date) { /* Set the system clock to the specified date, then regardless of the success of that operation, format and print that date. */ - if (stime (&when) == -1) + if (settime (&when) != 0) { error (0, errno, _("cannot set date")); - status = 1; + ok = false; } } - show_date (format, when); + ok &= show_date (format, when); } - exit (status); + exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); } /* Display the date and/or time in WHEN according to the format specified in FORMAT, followed by a newline. If FORMAT is NULL, use the - standard output format (ctime style but with a timezone inserted). */ + standard output format (ctime style but with a timezone inserted). + Return true if successful. */ -static void -show_date (const char *format, time_t when) +static bool +show_date (const char *format, struct timespec when) { struct tm *tm; - char *out = NULL; - size_t out_length = 0; - /* 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" - }; - - tm = localtime (&when); - if (format == NULL) + tm = localtime (&when.tv_sec); + if (! tm) { - /* Print the date in the default format. Vanilla ANSI C strftime - doesn't support %e, but POSIX requires it. If you don't use - a GNU strftime, make sure yours supports %e. - If you are not using GNU strftime, you want to change %z - in the RFC format to %Z; this gives, however, an invalid - RFC time format outside the continental United States and GMT. */ - - 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"; - } - } - else if (*format == '\0') - { - printf ("\n"); - return; + char buf[INT_BUFSIZE_BOUND (intmax_t)]; + error (0, 0, _("time %s is out of range"), + (TYPE_SIGNED (time_t) + ? imaxtostr (when.tv_sec, buf) + : umaxtostr (when.tv_sec, buf))); + return false; } - while (1) - { - int done; - out_length += 200; - out = (char *) xrealloc (out, out_length); - - /* Mark the first byte of the buffer so we can detect the case - of strftime producing an empty string. Otherwise, this loop - would not terminate when date was invoked like this - `LANG=de date +%p' on a system with good language support. */ - out[0] = '\1'; - - if (rfc_format) - setlocale (LC_ALL, "C"); - - done = (strftime (out, out_length, format, tm) || out[0] == '\0'); - - if (rfc_format) - setlocale (LC_ALL, ""); - - if (done) - break; - } - - printf ("%s\n", out); - free (out); + { + if (format == rfc_2822_format) + setlocale (LC_TIME, "C"); + fprintftime (stdout, format, tm, 0, when.tv_nsec); + fputc ('\n', stdout); + if (format == rfc_2822_format) + setlocale (LC_TIME, ""); + } + return true; }