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