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