/* date - print or set the system date and time This is the date utility
Copyright (C) 1989-2018 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
David MacKenzie <djm@gnu.ai.mit.edu> */ The GNUv3 license
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#if HAVE_LANGINFO_CODESET Line 23
# include <langinfo.h> ...!includes auto-comment...
#endif Line 25
#include "system.h" ...!includes auto-comment...
#include "argmatch.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "parse-datetime.h" ...!includes auto-comment...
#include "posixtm.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "stat-time.h" ...!includes auto-comment...
#include "fprintftime.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "date" Line 38
#define AUTHORS proper_name ("David MacKenzie") Line 40
static bool show_date (const char *, struct timespec, timezone_t); Line 42
enum Time_spec Line 44
{
/* Display only the date. */
TIME_SPEC_DATE, Line 47
/* Display date, hours, minutes, and seconds. */
TIME_SPEC_SECONDS, Line 49
/* Similar, but display nanoseconds. */
TIME_SPEC_NS, Line 51
/* Put these last, since they aren't valid for --rfc-3339. */
/* Display date and hour. */
TIME_SPEC_HOURS, Line 56
/* Display date, hours, and minutes. */
TIME_SPEC_MINUTES Line 58
};
static char const *const time_spec_string[] = Line 61
{
/* Put "hours" and "minutes" first, since they aren't valid for
--rfc-3339. */
"hours", "minutes", Line 65
"date", "seconds", "ns", NULL Line 66
};
static enum Time_spec const time_spec[] = Line 68
{
TIME_SPEC_HOURS, TIME_SPEC_MINUTES, Line 70
TIME_SPEC_DATE, TIME_SPEC_SECONDS, TIME_SPEC_NS Line 71
}; Block 3
ARGMATCH_VERIFY (time_spec_string, time_spec); Line 73
/* A format suitable for Internet RFCs 5322, 2822, and 822. */
static char const rfc_email_format[] = "%a, %d %b %Y %H:%M:%S %z"; Line 76
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 80
{
RFC_3339_OPTION = CHAR_MAX + 1, Line 82
DEBUG_DATE_PARSING Line 83
}; Block 4
static char const short_options[] = "d:f:I::r:Rs:u"; Line 86
static struct option const long_options[] = Line 88
{
{"date", required_argument, NULL, 'd'}, Line 90
{"debug", no_argument, NULL, DEBUG_DATE_PARSING}, Line 91
{"file", required_argument, NULL, 'f'}, Line 92
{"iso-8601", optional_argument, NULL, 'I'}, Line 93
{"reference", required_argument, NULL, 'r'}, Line 94
{"rfc-email", no_argument, NULL, 'R'}, Line 95
{"rfc-822", no_argument, NULL, 'R'}, Line 96
{"rfc-2822", no_argument, NULL, 'R'}, Line 97
{"rfc-3339", required_argument, NULL, RFC_3339_OPTION}, Line 98
{"set", required_argument, NULL, 's'}, Line 99
{"uct", no_argument, NULL, 'u'}, Line 100
{"utc", no_argument, NULL, 'u'}, Line 101
{"universal", no_argument, NULL, 'u'}, Line 102
{GETOPT_HELP_OPTION_DECL}, Line 103
{GETOPT_VERSION_OPTION_DECL}, Line 104
{NULL, 0, NULL, 0} Line 105
}; Block 5
/* flags for parse_datetime2 */
static unsigned int parse_datetime_flags; Line 109
#if LOCALTIME_CACHE Line 111
# define TZSET tzset () Line 112
#else Line 113
# define TZSET /* empty */ Line 114
#endif Line 115
#ifdef _DATE_FMT Line 117
# define DATE_FMT_LANGINFO() nl_langinfo (_DATE_FMT) Line 118
#else Line 119
# define DATE_FMT_LANGINFO() "" Line 120
#endif Line 121
void Line 123
usage (int status) Line 124
{
if (status != EXIT_SUCCESS) Line 126
emit_try_help (); ...!common auto-comment...
else Line 128
{
printf (_("\ Line 130
Usage: %s [OPTION]... [+FORMAT]\n\ Line 131
or: %s [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]\n\ Line 132
"), Line 133
program_name, program_name); Line 134
fputs (_("\ Line 135
Display the current time in the given FORMAT, or set the system date.\n\ Line 136
"), stdout); Line 137
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 141
-d, --date=STRING display time described by STRING, not 'now'\n\ Line 142
"), stdout); Line 143
fputs (_("\ Line 144
--debug annotate the parsed date,\n\ Line 145
and warn about questionable usage to stderr\n\ Line 146
"), stdout); Line 147
fputs (_("\ Line 148
-f, --file=DATEFILE like --date; once for each line of DATEFILE\n\ Line 149
"), stdout); Line 150
fputs (_("\ Line 151
-I[FMT], --iso-8601[=FMT] output date/time in ISO 8601 format.\n\ Line 152
FMT='date' for date only (the default),\n\ Line 153
'hours', 'minutes', 'seconds', or 'ns'\n\ Line 154
for date and time to the indicated precision.\n\ Line 155
Example: 2006-08-14T02:34:56-06:00\n\ Line 156
"), stdout); Line 157
fputs (_("\ Line 158
-R, --rfc-email output date and time in RFC 5322 format.\n\ Line 159
Example: Mon, 14 Aug 2006 02:34:56 -0600\n\ Line 160
"), stdout); Line 161
fputs (_("\ Line 162
--rfc-3339=FMT output date/time in RFC 3339 format.\n\ Line 163
FMT='date', 'seconds', or 'ns'\n\ Line 164
for date and time to the indicated precision.\n\ Line 165
Example: 2006-08-14 02:34:56-06:00\n\ Line 166
"), stdout); Line 167
fputs (_("\ Line 168
-r, --reference=FILE display the last modification time of FILE\n\ Line 169
"), stdout); Line 170
fputs (_("\ Line 171
-s, --set=STRING set time described by STRING\n\ Line 172
-u, --utc, --universal print or set Coordinated Universal Time (UTC)\n\ Line 173
"), stdout); Line 174
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 175
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 176
fputs (_("\ Line 177
\n\
FORMAT controls the output. Interpreted sequences are:\n\ Line 179
\n\
%% a literal %\n\ Line 181
%a locale's abbreviated weekday name (e.g., Sun)\n\ Line 182
"), stdout); Line 183
fputs (_("\ Line 184
%A locale's full weekday name (e.g., Sunday)\n\ Line 185
%b locale's abbreviated month name (e.g., Jan)\n\ Line 186
%B locale's full month name (e.g., January)\n\ Line 187
%c locale's date and time (e.g., Thu Mar 3 23:05:25 2005)\n\ Line 188
"), stdout); Line 189
fputs (_("\ Line 190
%C century; like %Y, except omit last two digits (e.g., 20)\n\ Line 191
%d day of month (e.g., 01)\n\ Line 192
%D date; same as %m/%d/%y\n\ Line 193
%e day of month, space padded; same as %_d\n\ Line 194
"), stdout); Line 195
fputs (_("\ Line 196
%F full date; same as %Y-%m-%d\n\ Line 197
%g last two digits of year of ISO week number (see %G)\n\ Line 198
%G year of ISO week number (see %V); normally useful only with %V\n\ Line 199
"), stdout); Line 200
fputs (_("\ Line 201
%h same as %b\n\ Line 202
%H hour (00..23)\n\ Line 203
%I hour (01..12)\n\ Line 204
%j day of year (001..366)\n\ Line 205
"), stdout); Line 206
fputs (_("\ Line 207
%k hour, space padded ( 0..23); same as %_H\n\ Line 208
%l hour, space padded ( 1..12); same as %_I\n\ Line 209
%m month (01..12)\n\ Line 210
%M minute (00..59)\n\ Line 211
"), stdout); Line 212
fputs (_("\ Line 213
%n a newline\n\ Line 214
%N nanoseconds (000000000..999999999)\n\ Line 215
%p locale's equivalent of either AM or PM; blank if not known\n\ Line 216
%P like %p, but lower case\n\ Line 217
%q quarter of year (1..4)\n\ Line 218
%r locale's 12-hour clock time (e.g., 11:11:04 PM)\n\ Line 219
%R 24-hour hour and minute; same as %H:%M\n\ Line 220
%s seconds since 1970-01-01 00:00:00 UTC\n\ Line 221
"), stdout); Line 222
fputs (_("\ Line 223
%S second (00..60)\n\ Line 224
%t a tab\n\ Line 225
%T time; same as %H:%M:%S\n\ Line 226
%u day of week (1..7); 1 is Monday\n\ Line 227
"), stdout); Line 228
fputs (_("\ Line 229
%U week number of year, with Sunday as first day of week (00..53)\n\ Line 230
%V ISO week number, with Monday as first day of week (01..53)\n\ Line 231
%w day of week (0..6); 0 is Sunday\n\ Line 232
%W week number of year, with Monday as first day of week (00..53)\n\ Line 233
"), stdout); Line 234
fputs (_("\ Line 235
%x locale's date representation (e.g., 12/31/99)\n\ Line 236
%X locale's time representation (e.g., 23:13:48)\n\ Line 237
%y last two digits of year (00..99)\n\ Line 238
%Y year\n\ Line 239
"), stdout); Line 240
fputs (_("\ Line 241
%z +hhmm numeric time zone (e.g., -0400)\n\ Line 242
%:z +hh:mm numeric time zone (e.g., -04:00)\n\ Line 243
%::z +hh:mm:ss numeric time zone (e.g., -04:00:00)\n\ Line 244
%:::z numeric time zone with : to necessary precision (e.g., -04, +05:30)\n\ Line 245
%Z alphabetic time zone abbreviation (e.g., EDT)\n\ Line 246
\n\
By default, date pads numeric fields with zeroes.\n\ Line 248
"), stdout); Line 249
fputs (_("\ Line 250
The following optional flags may follow '%':\n\ Line 251
\n\
- (hyphen) do not pad the field\n\ Line 253
_ (underscore) pad with spaces\n\ Line 254
0 (zero) pad with zeros\n\ Line 255
^ use upper case if possible\n\ Line 256
# use opposite case if possible\n\ Line 257
"), stdout); Line 258
fputs (_("\ Line 259
\n\
After any flags comes an optional field width, as a decimal number;\n\ Line 261
then an optional modifier, which is either\n\ Line 262
E to use the locale's alternate representations if available, or\n\ Line 263
O to use the locale's alternate numeric symbols if available.\n\ Line 264
"), stdout); Line 265
fputs (_("\ Line 266
\n\
Examples:\n\ Line 268
Convert seconds since the epoch (1970-01-01 UTC) to a date\n\ Line 269
$ date --date='@2147483647'\n\ Line 270
\n\
Show the time on the west coast of the US (use tzselect(1) to find TZ)\n\ Line 272
$ TZ='America/Los_Angeles' date\n\ Line 273
\n\
Show the local time for 9AM next Friday on the west coast of the US\n\ Line 275
$ date --date='TZ=\"America/Los_Angeles\" 09:00 next Fri'\n\ Line 276
"), stdout); Line 277
emit_ancillary_info (PROGRAM_NAME); Line 278
}
exit (status); Line 280
} Block 6
/* 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.
Return true if successful. */
static bool Line 288
batch_convert (const char *input_filename, const char *format, Line 289
timezone_t tz, char const *tzstring) Line 290
{
bool ok; Line 292
FILE *in_stream; Line 293
char *line; Line 294
size_t buflen; Line 295
struct timespec when; Line 296
if (STREQ (input_filename, "-")) Line 298
{
input_filename = _("standard input"); Line 300
in_stream = stdin; Line 301
}
else Line 303
{
in_stream = fopen (input_filename, "r"); Line 305...!syscalls auto-comment...
if (in_stream == NULL) Line 306
{
die (EXIT_FAILURE, errno, "%s", quotef (input_filename)); Line 308
}
}
line = NULL; Line 312
buflen = 0; Line 313
ok = true; Line 314
while (1) Line 315
{
ssize_t line_length = getline (&line, &buflen, in_stream); Line 317
if (line_length < 0) Line 318
{
/* FIXME: detect/handle error here. */
break; Line 321
}
if (! parse_datetime2 (&when, line, NULL, Line 324
parse_datetime_flags, tz, tzstring)) Line 325
{
if (line[line_length - 1] == '\n') Line 327
line[line_length - 1] = '\0'; Line 328
error (0, 0, _("invalid date %s"), quote (line)); Line 329
ok = false; Line 330
}
else Line 332
{
ok &= show_date (format, when, tz); Line 334
}
}
if (fclose (in_stream) == EOF) Line 338...!syscalls auto-comment...
die (EXIT_FAILURE, errno, "%s", quotef (input_filename)); Line 339
free (line); Line 341
return ok; Line 343
} Block 7
int
main (int argc, char **argv) Line 347
{
int optc; Line 349
const char *datestr = NULL; Line 350
const char *set_datestr = NULL; Line 351
struct timespec when; Line 352
bool set_date = false; Line 353
char const *format = NULL; Line 354
char *batch_file = NULL; Line 355
char *reference = NULL; Line 356
struct stat refstats; Line 357
bool ok; Line 358
int option_specified_date; Line 359
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
while ((optc = getopt_long (argc, argv, short_options, long_options, NULL)) Line 369
!= -1) Line 370
{
char const *new_format = NULL; Line 372
switch (optc) Line 374
{
case 'd': Line 376
datestr = optarg; Line 377
break; Line 378
case DEBUG_DATE_PARSING: Line 379
parse_datetime_flags |= PARSE_DATETIME_DEBUG; Line 380
break; Line 381
case 'f': Line 382
batch_file = optarg; Line 383
break; Line 384
case RFC_3339_OPTION: Line 385
{
static char const rfc_3339_format[][32] = Line 387
{
"%Y-%m-%d", Line 389
"%Y-%m-%d %H:%M:%S%:z", Line 390
"%Y-%m-%d %H:%M:%S.%N%:z" Line 391
};
enum Time_spec i = Line 393
XARGMATCH ("--rfc-3339", optarg, Line 394
time_spec_string + 2, time_spec + 2); Line 395
new_format = rfc_3339_format[i]; Line 396
break; Line 397
}
case 'I': Line 399
{
static char const iso_8601_format[][32] = Line 401
{
"%Y-%m-%d", Line 403
"%Y-%m-%dT%H:%M:%S%:z", Line 404
"%Y-%m-%dT%H:%M:%S,%N%:z", Line 405
"%Y-%m-%dT%H%:z", Line 406
"%Y-%m-%dT%H:%M%:z" Line 407
};
enum Time_spec i = Line 409
(optarg Line 410
? XARGMATCH ("--iso-8601", optarg, time_spec_string, time_spec) Line 411
: TIME_SPEC_DATE); Line 412
new_format = iso_8601_format[i]; Line 413
break; Line 414
}
case 'r': Line 416
reference = optarg; Line 417
break; Line 418
case 'R': Line 419
new_format = rfc_email_format; Line 420
break; Line 421
case 's': Line 422
set_datestr = optarg; Line 423
set_date = true; Line 424
break; Line 425
case 'u': Line 426
/* 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 (bad_cast ("TZ=UTC0")) != 0) Line 430
xalloc_die (); ...!common auto-comment...
TZSET; Line 432
break; Line 433
case_GETOPT_HELP_CHAR; Line 434
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 435
default: Line 436
usage (EXIT_FAILURE); Line 437
}
if (new_format) Line 440
{
if (format) Line 442
die (EXIT_FAILURE, 0, _("multiple output formats specified")); Line 443
format = new_format; Line 444
}
}
option_specified_date = ((datestr ? 1 : 0) Line 448
+ (batch_file ? 1 : 0) Line 449
+ (reference ? 1 : 0)); Line 450
if (option_specified_date > 1) Line 452
{
error (0, 0, Line 454
_("the options to specify dates for printing are mutually exclusive")); Line 455
usage (EXIT_FAILURE); Line 456
}
if (set_date && option_specified_date) Line 459
{
error (0, 0, Line 461
_("the options to print and set the time may not be used together")); Line 462
usage (EXIT_FAILURE); Line 463
}
if (optind < argc) Line 466
{
if (optind + 1 < argc) Line 468
{
error (0, 0, _("extra operand %s"), quote (argv[optind + 1])); Line 470
usage (EXIT_FAILURE); Line 471
}
if (argv[optind][0] == '+') Line 474
{
if (format) Line 476
die (EXIT_FAILURE, 0, _("multiple output formats specified")); Line 477
format = argv[optind++] + 1; Line 478
}
else if (set_date || option_specified_date) Line 480
{
error (0, 0, Line 482
_("the argument %s lacks a leading '+';\n" Line 483
"when using an option to specify date(s), any non-option\n" Line 484
"argument must be a format string beginning with '+'"), Line 485
quote (argv[optind])); Line 486
usage (EXIT_FAILURE); Line 487
}
}
if (!format) Line 491
{
format = DATE_FMT_LANGINFO (); Line 493
if (! *format) Line 494
{
/* 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"; Line 503
}
}
char const *tzstring = getenv ("TZ"); Line 507
timezone_t tz = tzalloc (tzstring); Line 508
if (batch_file != NULL) Line 510
ok = batch_convert (batch_file, format, tz, tzstring); Line 511
else Line 512
{
bool valid_date = true; Line 514
ok = true; Line 515
if (!option_specified_date && !set_date) Line 517
{
if (optind < argc) Line 519
{
/* Prepare to set system clock to the specified date/time
given in the POSIX-format. */
set_date = true; Line 523
datestr = argv[optind]; Line 524
valid_date = posixtime (&when.tv_sec, Line 525
datestr, Line 526
(PDS_TRAILING_YEAR Line 527
| PDS_CENTURY | PDS_SECONDS)); Line 528
when.tv_nsec = 0; /* FIXME: posixtime should set this. */ Line 529
}
else Line 531
{
/* Prepare to print the current date/time. */
gettime (&when); Line 534
}
}
else Line 537
{
/* (option_specified_date || set_date) */
if (reference != NULL) Line 540
{
if (stat (reference, &refstats) != 0) Line 542...!syscalls auto-comment...
die (EXIT_FAILURE, errno, "%s", quotef (reference)); Line 543
when = get_stat_mtime (&refstats); Line 544
}
else Line 546
{
if (set_datestr) Line 548
datestr = set_datestr; Line 549
valid_date = parse_datetime2 (&when, datestr, NULL, Line 550
parse_datetime_flags, Line 551
tz, tzstring); Line 552
}
}
if (! valid_date) Line 556
die (EXIT_FAILURE, 0, _("invalid date %s"), quote (datestr)); Line 557
if (set_date) Line 559
{
/* Set the system clock to the specified date, then regardless of
the success of that operation, format and print that date. */
if (settime (&when) != 0) Line 563
{
error (0, errno, _("cannot set date")); Line 565
ok = false; Line 566
}
}
ok &= show_date (format, when, tz); Line 570
}
IF_LINT (tzfree (tz)); Line 573
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 575
} Block 8
/* Display the date and/or time in WHEN according to the format specified
in FORMAT, followed by a newline. Return true if successful. */
static bool Line 581
show_date (const char *format, struct timespec when, timezone_t tz) Line 582
{
struct tm tm; Line 584
if (localtime_rz (tz, &when.tv_sec, &tm)) Line 586
{
if (format == rfc_email_format) Line 588
setlocale (LC_TIME, "C"); Sets up internationalization (i18n)
fprintftime (stdout, format, &tm, tz, when.tv_nsec); Line 590
if (format == rfc_email_format) Line 591
setlocale (LC_TIME, ""); Sets up internationalization (i18n)
fputc ('\n', stdout); Line 593
return true; Line 594
}
else Line 596
{
char buf[INT_BUFSIZE_BOUND (intmax_t)]; Line 598
error (0, 0, _("time %s is out of range"), Line 599
quote (timetostr (when.tv_sec, buf))); Line 600
return false; Line 601
}
} Block 9