2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 1985, 1987, 1988, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/param.h>
50 #define TM_YEAR_BASE 1900
55 static void badformat(void);
56 static void iso8601_usage(const char *) __dead2;
57 static void multipleformats(void);
58 static void printdate(const char *);
59 static void printisodate(struct tm *);
60 static void setthetime(const char *, const char *, int);
61 static void usage(void) __dead2;
63 static const struct iso8601_fmt {
65 const char *format_string;
67 { "date", "%Y-%m-%d" },
72 static const struct iso8601_fmt *iso8601_selected;
74 static const char *rfc2822_format = "%a, %d %b %Y %T %z";
77 main(int argc, char *argv[])
80 bool Iflag, jflag, Rflag;
83 char *fmt, *outzone = NULL;
86 const struct vary *badv;
93 (void) setlocale(LC_TIME, "");
95 Iflag = jflag = Rflag = 0;
96 while ((ch = getopt(argc, argv, "f:I::jnRr:uv:z:")) != -1)
105 if (optarg == NULL) {
106 iso8601_selected = iso8601_fmts;
109 for (i = 0; i < nitems(iso8601_fmts); i++)
110 if (strcmp(optarg, iso8601_fmts[i].refname) == 0)
112 if (i == nitems(iso8601_fmts))
113 iso8601_usage(optarg);
115 iso8601_selected = &iso8601_fmts[i];
118 jflag = 1; /* don't set time */
122 case 'R': /* RFC 2822 datetime format */
127 case 'r': /* user specified seconds */
129 tval = strtoq(optarg, &tmp, 0);
131 if (stat(optarg, &sb) == 0)
132 tval = sb.st_mtim.tv_sec;
137 case 'u': /* do everything in UTC */
138 (void)setenv("TZ", "UTC0", 1);
144 v = vary_append(v, optarg);
152 if (!rflag && time(&tval) == -1)
158 format = rfc2822_format;
160 /* allow the operands in any order */
161 if (*argv && **argv == '+') {
169 setthetime(fmt, *argv, jflag);
171 } else if (fmt != NULL)
174 if (*argv && **argv == '+') {
180 if (outzone != NULL && setenv("TZ", outzone, 1) != 0)
181 err(1, "setenv(TZ)");
182 lt = localtime(&tval);
184 errx(1, "invalid time");
185 badv = vary_apply(v, lt);
187 fprintf(stderr, "%s: Cannot apply date adjustment\n",
197 if (format == rfc2822_format)
199 * When using RFC 2822 datetime format, don't honor the
202 setlocale(LC_TIME, "C");
205 (void)strftime(buf, sizeof(buf), format, lt);
210 printdate(const char *buf)
212 (void)printf("%s\n", buf);
219 printisodate(struct tm *lt)
221 const struct iso8601_fmt *it;
222 char fmtbuf[32], buf[32], tzbuf[8];
225 for (it = iso8601_fmts; it <= iso8601_selected; it++)
226 strlcat(fmtbuf, it->format_string, sizeof(fmtbuf));
228 (void)strftime(buf, sizeof(buf), fmtbuf, lt);
230 if (iso8601_selected > iso8601_fmts) {
231 (void)strftime(tzbuf, sizeof(tzbuf), "%z", lt);
232 memmove(&tzbuf[4], &tzbuf[3], 3);
234 strlcat(buf, tzbuf, sizeof(buf));
240 #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
243 setthetime(const char *fmt, const char *p, int jflag)
251 lt = localtime(&tval);
253 errx(1, "invalid time");
254 lt->tm_isdst = -1; /* divine correct DST */
257 t = strptime(p, fmt, lt);
259 fprintf(stderr, "Failed conversion of ``%s''"
260 " using format ``%s''\n", p, fmt);
262 } else if (*t != '\0')
263 fprintf(stderr, "Warning: Ignoring %ld extraneous"
264 " characters in date string (%s)\n",
265 (long) strlen(t), t);
267 for (t = p, dot = NULL; *t; ++t) {
270 if (*t == '.' && dot == NULL) {
277 if (dot != NULL) { /* .ss */
278 dot++; /* *dot++ = '\0'; */
279 if (strlen(dot) != 2)
281 lt->tm_sec = ATOI2(dot);
288 /* if p has a ".ss" field then let's pretend it's not there */
289 switch (strlen(p) - ((dot != NULL) ? 3 : 0)) {
291 lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE;
296 lt->tm_year += ATOI2(p);
298 lt->tm_year = ATOI2(p);
299 if (lt->tm_year < 69) /* hack for 2000 ;-} */
300 lt->tm_year += 2000 - TM_YEAR_BASE;
302 lt->tm_year += 1900 - TM_YEAR_BASE;
306 lt->tm_mon = ATOI2(p);
309 --lt->tm_mon; /* time struct is 0 - 11 */
312 lt->tm_mday = ATOI2(p);
313 if (lt->tm_mday > 31)
317 lt->tm_hour = ATOI2(p);
318 if (lt->tm_hour > 23)
322 lt->tm_min = ATOI2(p);
331 /* convert broken-down time to GMT clock time */
332 if ((tval = mktime(lt)) == -1)
333 errx(1, "nonexistent time");
336 utx.ut_type = OLD_TIME;
337 memset(utx.ut_id, 0, sizeof(utx.ut_id));
338 (void)gettimeofday(&utx.ut_tv, NULL);
342 if (settimeofday(&tv, NULL) != 0)
343 err(1, "settimeofday (timeval)");
344 utx.ut_type = NEW_TIME;
345 (void)gettimeofday(&utx.ut_tv, NULL);
348 if ((p = getlogin()) == NULL)
350 syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p);
357 warnx("illegal time format");
362 iso8601_usage(const char *badarg)
364 errx(1, "invalid argument '%s' for -I", badarg);
368 multipleformats(void)
370 errx(1, "multiple output formats specified");
376 (void)fprintf(stderr, "%s\n%s\n%s\n",
377 "usage: date [-jnRu] [-I[date|hours|minutes|seconds]] [-f input_fmt]",
379 "[ -z output_zone ] [-r filename|seconds] [-v[+|-]val[y|m|w|d|H|M|S]]",
381 "[[[[[[cc]yy]mm]dd]HH]MM[.SS] | new_date] [+output_fmt]"