date.c 18 KB

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