LCOV - code coverage report
Current view: top level - src - date.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 147 163 90.2 %
Date: 2018-01-30 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* date - print or set the system date and time
       2             :    Copyright (C) 1989-2007 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             : 
      17             :    David MacKenzie <djm@gnu.ai.mit.edu> */
      18             : 
      19             : #include <config.h>
      20             : #include <stdio.h>
      21             : #include <getopt.h>
      22             : #include <sys/types.h>
      23             : #if HAVE_LANGINFO_CODESET
      24             : # include <langinfo.h>
      25             : #endif
      26             : 
      27             : #include "system.h"
      28             : #include "argmatch.h"
      29             : #include "error.h"
      30             : #include "getdate.h"
      31             : #include "inttostr.h"
      32             : #include "posixtm.h"
      33             : #include "quote.h"
      34             : #include "stat-time.h"
      35             : #include "fprintftime.h"
      36             : 
      37             : /* The official name of this program (e.g., no `g' prefix).  */
      38             : #define PROGRAM_NAME "date"
      39             : 
      40             : #define AUTHORS "David MacKenzie"
      41             : 
      42             : int putenv ();
      43             : 
      44             : static bool show_date (const char *format, struct timespec when);
      45             : 
      46             : enum Time_spec
      47             : {
      48             :   /* Display only the date.  */
      49             :   TIME_SPEC_DATE,
      50             :   /* Display date, hours, minutes, and seconds.  */
      51             :   TIME_SPEC_SECONDS,
      52             :   /* Similar, but display nanoseconds. */
      53             :   TIME_SPEC_NS,
      54             : 
      55             :   /* Put these last, since they aren't valid for --rfc-3339.  */
      56             : 
      57             :   /* Display date and hour.  */
      58             :   TIME_SPEC_HOURS,
      59             :   /* Display date, hours, and minutes.  */
      60             :   TIME_SPEC_MINUTES
      61             : };
      62             : 
      63             : static char const *const time_spec_string[] =
      64             : {
      65             :   /* Put "hours" and "minutes" first, since they aren't valid for
      66             :      --rfc-3339.  */
      67             :   "hours", "minutes",
      68             :   "date", "seconds", "ns", NULL
      69             : };
      70             : static enum Time_spec const time_spec[] =
      71             : {
      72             :   TIME_SPEC_HOURS, TIME_SPEC_MINUTES,
      73             :   TIME_SPEC_DATE, TIME_SPEC_SECONDS, TIME_SPEC_NS
      74             : };
      75             : ARGMATCH_VERIFY (time_spec_string, time_spec);
      76             : 
      77             : /* A format suitable for Internet RFC 2822.  */
      78             : static char const rfc_2822_format[] = "%a, %d %b %Y %H:%M:%S %z";
      79             : 
      80             : /* The name this program was run with, for error messages. */
      81             : char *program_name;
      82             : 
      83             : /* For long options that have no equivalent short option, use a
      84             :    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
      85             : enum
      86             : {
      87             :   RFC_3339_OPTION = CHAR_MAX + 1
      88             : };
      89             : 
      90             : static char const short_options[] = "d:f:I::r:Rs:u";
      91             : 
      92             : static struct option const long_options[] =
      93             : {
      94             :   {"date", required_argument, NULL, 'd'},
      95             :   {"file", required_argument, NULL, 'f'},
      96             :   {"iso-8601", optional_argument, NULL, 'I'}, /* Deprecated.  */
      97             :   {"reference", required_argument, NULL, 'r'},
      98             :   {"rfc-822", no_argument, NULL, 'R'},
      99             :   {"rfc-2822", no_argument, NULL, 'R'},
     100             :   {"rfc-3339", required_argument, NULL, RFC_3339_OPTION},
     101             :   {"set", required_argument, NULL, 's'},
     102             :   {"uct", no_argument, NULL, 'u'},
     103             :   {"utc", no_argument, NULL, 'u'},
     104             :   {"universal", no_argument, NULL, 'u'},
     105             :   {GETOPT_HELP_OPTION_DECL},
     106             :   {GETOPT_VERSION_OPTION_DECL},
     107             :   {NULL, 0, NULL, 0}
     108             : };
     109             : 
     110             : #if LOCALTIME_CACHE
     111             : # define TZSET tzset ()
     112             : #else
     113             : # define TZSET /* empty */
     114             : #endif
     115             : 
     116             : #ifdef _DATE_FMT
     117             : # define DATE_FMT_LANGINFO() nl_langinfo (_DATE_FMT)
     118             : #else
     119             : # define DATE_FMT_LANGINFO() ""
     120             : #endif
     121             : 
     122             : void
     123          29 : usage (int status)
     124             : {
     125          29 :   if (status != EXIT_SUCCESS)
     126          28 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
     127             :              program_name);
     128             :   else
     129             :     {
     130           1 :       printf (_("\
     131             : Usage: %s [OPTION]... [+FORMAT]\n\
     132             :   or:  %s [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]\n\
     133             : "),
     134             :               program_name, program_name);
     135           1 :       fputs (_("\
     136             : Display the current time in the given FORMAT, or set the system date.\n\
     137             : \n\
     138             :   -d, --date=STRING         display time described by STRING, not `now'\n\
     139             :   -f, --file=DATEFILE       like --date once for each line of DATEFILE\n\
     140             : "), stdout);
     141           1 :       fputs (_("\
     142             :   -r, --reference=FILE      display the last modification time of FILE\n\
     143             :   -R, --rfc-2822            output date and time in RFC 2822 format.\n\
     144             :                             Example: Mon, 07 Aug 2006 12:34:56 -0600\n\
     145             : "), stdout);
     146           1 :       fputs (_("\
     147             :       --rfc-3339=TIMESPEC   output date and time in RFC 3339 format.\n\
     148             :                             TIMESPEC=`date', `seconds', or `ns' for\n\
     149             :                             date and time to the indicated precision.\n\
     150             :                             Date and time components are separated by\n\
     151             :                             a single space: 2006-08-07 12:34:56-06:00\n\
     152             :   -s, --set=STRING          set time described by STRING\n\
     153             :   -u, --utc, --universal    print or set Coordinated Universal Time\n\
     154             : "), stdout);
     155           1 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     156           1 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     157           1 :       fputs (_("\
     158             : \n\
     159             : FORMAT controls the output.  The only valid option for the second form\n\
     160             : specifies Coordinated Universal Time.  Interpreted sequences are:\n\
     161             : \n\
     162             :   %%   a literal %\n\
     163             :   %a   locale's abbreviated weekday name (e.g., Sun)\n\
     164             : "), stdout);
     165           1 :       fputs (_("\
     166             :   %A   locale's full weekday name (e.g., Sunday)\n\
     167             :   %b   locale's abbreviated month name (e.g., Jan)\n\
     168             :   %B   locale's full month name (e.g., January)\n\
     169             :   %c   locale's date and time (e.g., Thu Mar  3 23:05:25 2005)\n\
     170             : "), stdout);
     171           1 :       fputs (_("\
     172             :   %C   century; like %Y, except omit last two digits (e.g., 21)\n\
     173             :   %d   day of month (e.g, 01)\n\
     174             :   %D   date; same as %m/%d/%y\n\
     175             :   %e   day of month, space padded; same as %_d\n\
     176             : "), stdout);
     177           1 :       fputs (_("\
     178             :   %F   full date; same as %Y-%m-%d\n\
     179             :   %g   last two digits of year of ISO week number (see %G)\n\
     180             :   %G   year of ISO week number (see %V); normally useful only with %V\n\
     181             : "), stdout);
     182           1 :       fputs (_("\
     183             :   %h   same as %b\n\
     184             :   %H   hour (00..23)\n\
     185             :   %I   hour (01..12)\n\
     186             :   %j   day of year (001..366)\n\
     187             : "), stdout);
     188           1 :       fputs (_("\
     189             :   %k   hour ( 0..23)\n\
     190             :   %l   hour ( 1..12)\n\
     191             :   %m   month (01..12)\n\
     192             :   %M   minute (00..59)\n\
     193             : "), stdout);
     194           1 :       fputs (_("\
     195             :   %n   a newline\n\
     196             :   %N   nanoseconds (000000000..999999999)\n\
     197             :   %p   locale's equivalent of either AM or PM; blank if not known\n\
     198             :   %P   like %p, but lower case\n\
     199             :   %r   locale's 12-hour clock time (e.g., 11:11:04 PM)\n\
     200             :   %R   24-hour hour and minute; same as %H:%M\n\
     201             :   %s   seconds since 1970-01-01 00:00:00 UTC\n\
     202             : "), stdout);
     203           1 :       fputs (_("\
     204             :   %S   second (00..60)\n\
     205             :   %t   a tab\n\
     206             :   %T   time; same as %H:%M:%S\n\
     207             :   %u   day of week (1..7); 1 is Monday\n\
     208             : "), stdout);
     209           1 :       fputs (_("\
     210             :   %U   week number of year, with Sunday as first day of week (00..53)\n\
     211             :   %V   ISO week number, with Monday as first day of week (01..53)\n\
     212             :   %w   day of week (0..6); 0 is Sunday\n\
     213             :   %W   week number of year, with Monday as first day of week (00..53)\n\
     214             : "), stdout);
     215           1 :       fputs (_("\
     216             :   %x   locale's date representation (e.g., 12/31/99)\n\
     217             :   %X   locale's time representation (e.g., 23:13:48)\n\
     218             :   %y   last two digits of year (00..99)\n\
     219             :   %Y   year\n\
     220             : "), stdout);
     221           1 :       fputs (_("\
     222             :   %z   +hhmm numeric timezone (e.g., -0400)\n\
     223             :   %:z  +hh:mm numeric timezone (e.g., -04:00)\n\
     224             :   %::z  +hh:mm:ss numeric time zone (e.g., -04:00:00)\n\
     225             :   %:::z  numeric time zone with : to necessary precision (e.g., -04, +05:30)\n\
     226             :   %Z   alphabetic time zone abbreviation (e.g., EDT)\n\
     227             : \n\
     228             : By default, date pads numeric fields with zeroes.\n\
     229             : "), stdout);
     230           1 :       fputs (_("\
     231             : The following optional flags may follow `%':\n\
     232             : \n\
     233             :   -  (hyphen) do not pad the field\n\
     234             :   _  (underscore) pad with spaces\n\
     235             :   0  (zero) pad with zeros\n\
     236             :   ^  use upper case if possible\n\
     237             :   #  use opposite case if possible\n\
     238             : "), stdout);
     239           1 :       fputs (_("\
     240             : \n\
     241             : After any flags comes an optional field width, as a decimal number;\n\
     242             : then an optional modifier, which is either\n\
     243             : E to use the locale's alternate representations if available, or\n\
     244             : O to use the locale's alternate numeric symbols if available.\n\
     245             : "), stdout);
     246           1 :       emit_bug_reporting_address ();
     247             :     }
     248          29 :   exit (status);
     249             : }
     250             : 
     251             : /* Parse each line in INPUT_FILENAME as with --date and display each
     252             :    resulting time and date.  If the file cannot be opened, tell why
     253             :    then exit.  Issue a diagnostic for any lines that cannot be parsed.
     254             :    Return true if successful.  */
     255             : 
     256             : static bool
     257          11 : batch_convert (const char *input_filename, const char *format)
     258             : {
     259             :   bool ok;
     260             :   FILE *in_stream;
     261             :   char *line;
     262             :   size_t buflen;
     263             :   struct timespec when;
     264             : 
     265          11 :   if (STREQ (input_filename, "-"))
     266             :     {
     267           3 :       input_filename = _("standard input");
     268           3 :       in_stream = stdin;
     269             :     }
     270             :   else
     271             :     {
     272           8 :       in_stream = fopen (input_filename, "r");
     273           8 :       if (in_stream == NULL)
     274             :         {
     275           1 :           error (EXIT_FAILURE, errno, "%s", quote (input_filename));
     276             :         }
     277             :     }
     278             : 
     279          10 :   line = NULL;
     280          10 :   buflen = 0;
     281          10 :   ok = true;
     282             :   while (1)
     283          16 :     {
     284          26 :       ssize_t line_length = getline (&line, &buflen, in_stream);
     285          26 :       if (line_length < 0)
     286             :         {
     287             :           /* FIXME: detect/handle error here.  */
     288          10 :           break;
     289             :         }
     290             : 
     291          16 :       if (! get_date (&when, line, NULL))
     292             :         {
     293           0 :           if (line[line_length - 1] == '\n')
     294           0 :             line[line_length - 1] = '\0';
     295           0 :           error (0, 0, _("invalid date %s"), quote (line));
     296           0 :           ok = false;
     297             :         }
     298             :       else
     299             :         {
     300          16 :           ok &= show_date (format, when);
     301             :         }
     302             :     }
     303             : 
     304          10 :   if (fclose (in_stream) == EOF)
     305           0 :     error (EXIT_FAILURE, errno, "%s", quote (input_filename));
     306             : 
     307          10 :   free (line);
     308             : 
     309          10 :   return ok;
     310             : }
     311             : 
     312             : int
     313         216 : main (int argc, char **argv)
     314             : {
     315             :   int optc;
     316         216 :   const char *datestr = NULL;
     317         216 :   const char *set_datestr = NULL;
     318             :   struct timespec when;
     319         216 :   bool set_date = false;
     320         216 :   char const *format = NULL;
     321         216 :   char *batch_file = NULL;
     322         216 :   char *reference = NULL;
     323             :   struct stat refstats;
     324             :   bool ok;
     325             :   int option_specified_date;
     326             : 
     327             :   initialize_main (&argc, &argv);
     328         216 :   program_name = argv[0];
     329         216 :   setlocale (LC_ALL, "");
     330             :   bindtextdomain (PACKAGE, LOCALEDIR);
     331             :   textdomain (PACKAGE);
     332             : 
     333         216 :   atexit (close_stdout);
     334             : 
     335         505 :   while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
     336             :          != -1)
     337             :     {
     338          84 :       char const *new_format = NULL;
     339             : 
     340          84 :       switch (optc)
     341             :         {
     342          43 :         case 'd':
     343          43 :           datestr = optarg;
     344          43 :           break;
     345          11 :         case 'f':
     346          11 :           batch_file = optarg;
     347          11 :           break;
     348           0 :         case RFC_3339_OPTION:
     349             :           {
     350             :             static char const rfc_3339_format[][32] =
     351             :               {
     352             :                 "%Y-%m-%d",
     353             :                 "%Y-%m-%d %H:%M:%S%:z",
     354             :                 "%Y-%m-%d %H:%M:%S.%N%:z"
     355             :               };
     356           0 :             enum Time_spec i =
     357           0 :               XARGMATCH ("--rfc-3339", optarg,
     358             :                          time_spec_string + 2, time_spec + 2);
     359           0 :             new_format = rfc_3339_format[i];
     360           0 :             break;
     361             :           }
     362           9 :         case 'I':
     363             :           {
     364             :             static char const iso_8601_format[][32] =
     365             :               {
     366             :                 "%Y-%m-%d",
     367             :                 "%Y-%m-%dT%H:%M:%S%z",
     368             :                 "%Y-%m-%dT%H:%M:%S,%N%z",
     369             :                 "%Y-%m-%dT%H%z",
     370             :                 "%Y-%m-%dT%H:%M%z"
     371             :               };
     372           8 :             enum Time_spec i =
     373             :               (optarg
     374          12 :                ? XARGMATCH ("--iso-8601", optarg, time_spec_string, time_spec)
     375          11 :                : TIME_SPEC_DATE);
     376           8 :             new_format = iso_8601_format[i];
     377           8 :             break;
     378             :           }
     379           7 :         case 'r':
     380           7 :           reference = optarg;
     381           7 :           break;
     382           1 :         case 'R':
     383           1 :           new_format = rfc_2822_format;
     384           1 :           break;
     385           2 :         case 's':
     386           2 :           set_datestr = optarg;
     387           2 :           set_date = true;
     388           2 :           break;
     389           2 :         case 'u':
     390             :           /* POSIX says that `date -u' is equivalent to setting the TZ
     391             :              environment variable, so this option should do nothing other
     392             :              than setting TZ.  */
     393           2 :           if (putenv ("TZ=UTC0") != 0)
     394           0 :             xalloc_die ();
     395             :           TZSET;
     396           2 :           break;
     397           1 :         case_GETOPT_HELP_CHAR;
     398           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     399           7 :         default:
     400           7 :           usage (EXIT_FAILURE);
     401             :         }
     402             : 
     403          74 :       if (new_format)
     404             :         {
     405           9 :           if (format)
     406           1 :             error (EXIT_FAILURE, 0, _("multiple output formats specified"));
     407           8 :           format = new_format;
     408             :         }
     409             :     }
     410             : 
     411         410 :   option_specified_date = ((datestr ? 1 : 0)
     412         205 :                            + (batch_file ? 1 : 0)
     413         205 :                            + (reference ? 1 : 0));
     414             : 
     415         205 :   if (option_specified_date > 1)
     416             :     {
     417           0 :       error (0, 0,
     418             :         _("the options to specify dates for printing are mutually exclusive"));
     419           0 :       usage (EXIT_FAILURE);
     420             :     }
     421             : 
     422         205 :   if (set_date && option_specified_date)
     423             :     {
     424           0 :       error (0, 0,
     425             :           _("the options to print and set the time may not be used together"));
     426           0 :       usage (EXIT_FAILURE);
     427             :     }
     428             : 
     429         205 :   if (optind < argc)
     430             :     {
     431         150 :       if (optind + 1 < argc)
     432             :         {
     433          18 :           error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
     434          18 :           usage (EXIT_FAILURE);
     435             :         }
     436             : 
     437         132 :       if (argv[optind][0] == '+')
     438             :         {
     439         125 :           if (format)
     440           1 :             error (EXIT_FAILURE, 0, _("multiple output formats specified"));
     441         124 :           format = argv[optind++] + 1;
     442             :         }
     443           7 :       else if (set_date || option_specified_date)
     444             :         {
     445           2 :           error (0, 0,
     446             :                  _("the argument %s lacks a leading `+';\n"
     447             :                    "When using an option to specify date(s), any non-option\n"
     448             :                    "argument must be a format string beginning with `+'."),
     449           2 :                  quote (argv[optind]));
     450           2 :           usage (EXIT_FAILURE);
     451             :         }
     452             :     }
     453             : 
     454         184 :   if (!format)
     455             :     {
     456          54 :       format = DATE_FMT_LANGINFO ();
     457          54 :       if (! *format)
     458             :         {
     459             :           /* Do not wrap the following literal format string with _(...).
     460             :              For example, suppose LC_ALL is unset, LC_TIME="POSIX",
     461             :              and LANG="ko_KR".  In that case, POSIX says that LC_TIME
     462             :              determines the format and contents of date and time strings
     463             :              written by date, which means "date" must generate output
     464             :              using the POSIX locale; but adding _() would cause "date"
     465             :              to use a Korean translation of the format.  */
     466           0 :           format = "%a %b %e %H:%M:%S %Z %Y";
     467             :         }
     468             :     }
     469             : 
     470         184 :   if (batch_file != NULL)
     471          11 :     ok = batch_convert (batch_file, format);
     472             :   else
     473             :     {
     474         173 :       bool valid_date = true;
     475         173 :       ok = true;
     476             : 
     477         173 :       if (!option_specified_date && !set_date)
     478             :         {
     479         248 :           if (optind < argc)
     480             :             {
     481             :               /* Prepare to set system clock to the specified date/time
     482             :                  given in the POSIX-format.  */
     483           5 :               set_date = true;
     484           5 :               datestr = argv[optind];
     485           5 :               valid_date = posixtime (&when.tv_sec,
     486             :                                       datestr,
     487             :                                       (PDS_TRAILING_YEAR
     488             :                                        | PDS_CENTURY | PDS_SECONDS));
     489           5 :               when.tv_nsec = 0; /* FIXME: posixtime should set this.  */
     490             :             }
     491             :           else
     492             :             {
     493             :               /* Prepare to print the current date/time.  */
     494         119 :               gettime (&when);
     495             :             }
     496             :         }
     497             :       else
     498             :         {
     499             :           /* (option_specified_date || set_date) */
     500          49 :           if (reference != NULL)
     501             :             {
     502           7 :               if (stat (reference, &refstats) != 0)
     503           2 :                 error (EXIT_FAILURE, errno, "%s", reference);
     504           5 :               when = get_stat_mtime (&refstats);
     505             :             }
     506             :           else
     507             :             {
     508          42 :               if (set_datestr)
     509           2 :                 datestr = set_datestr;
     510          42 :               valid_date = get_date (&when, datestr, NULL);
     511             :             }
     512             :         }
     513             : 
     514         171 :       if (! valid_date)
     515          31 :         error (EXIT_FAILURE, 0, _("invalid date %s"), quote (datestr));
     516             : 
     517         140 :       if (set_date)
     518             :         {
     519             :           /* Set the system clock to the specified date, then regardless of
     520             :              the success of that operation, format and print that date.  */
     521           2 :           if (settime (&when) != 0)
     522             :             {
     523           2 :               error (0, errno, _("cannot set date"));
     524           2 :               ok = false;
     525             :             }
     526             :         }
     527             : 
     528         140 :       ok &= show_date (format, when);
     529             :     }
     530             : 
     531         150 :   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
     532             : }
     533             : 
     534             : /* Display the date and/or time in WHEN according to the format specified
     535             :    in FORMAT, followed by a newline.  Return true if successful.  */
     536             : 
     537             : static bool
     538         156 : show_date (const char *format, struct timespec when)
     539             : {
     540             :   struct tm *tm;
     541             : 
     542         156 :   tm = localtime (&when.tv_sec);
     543         156 :   if (! tm)
     544             :     {
     545             :       char buf[INT_BUFSIZE_BOUND (intmax_t)];
     546           1 :       error (0, 0, _("time %s is out of range"),
     547             :              (TYPE_SIGNED (time_t)
     548             :               ? imaxtostr (when.tv_sec, buf)
     549             :               : umaxtostr (when.tv_sec, buf)));
     550           1 :       return false;
     551             :     }
     552             : 
     553         155 :   if (format == rfc_2822_format)
     554           1 :     setlocale (LC_TIME, "C");
     555         155 :   fprintftime (stdout, format, tm, 0, when.tv_nsec);
     556         155 :   fputc ('\n', stdout);
     557         155 :   if (format == rfc_2822_format)
     558           1 :     setlocale (LC_TIME, "");
     559             : 
     560         155 :   return true;
     561             : }

Generated by: LCOV version 1.10