xref: /netbsd-src/bin/date/date.c (revision 5a5d821f81fc28cf006b39df4ccd4123e2afd82b)
1*5a5d821fSkre /* $NetBSD: date.c,v 1.70 2024/09/17 15:25:39 kre Exp $ */
249f0ad86Scgd 
361f28255Scgd /*
4667b5ea1Smycroft  * Copyright (c) 1985, 1987, 1988, 1993
5667b5ea1Smycroft  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
15b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
32634a8027Schristos #if HAVE_NBTOOL_CONFIG_H
33634a8027Schristos #include "nbtool_config.h"
34634a8027Schristos #endif
35634a8027Schristos 
36a71a8b87Sthorpej #include <sys/cdefs.h>
3761f28255Scgd #ifndef lint
38a71a8b87Sthorpej __COPYRIGHT(
392fe2731dSlukem "@(#) Copyright (c) 1985, 1987, 1988, 1993\
402fe2731dSlukem  The Regents of the University of California.  All rights reserved.");
4161f28255Scgd #endif /* not lint */
4261f28255Scgd 
4361f28255Scgd #ifndef lint
44d7290b76Scgd #if 0
455fc5415eSjtc static char sccsid[] = "@(#)date.c	8.2 (Berkeley) 4/28/95";
46d7290b76Scgd #else
47*5a5d821fSkre __RCSID("$NetBSD: date.c,v 1.70 2024/09/17 15:25:39 kre Exp $");
48d7290b76Scgd #endif
4961f28255Scgd #endif /* not lint */
5061f28255Scgd 
5161f28255Scgd #include <sys/param.h>
5261f28255Scgd #include <sys/time.h>
53667b5ea1Smycroft 
54667b5ea1Smycroft #include <ctype.h>
55667b5ea1Smycroft #include <err.h>
56667b5ea1Smycroft #include <fcntl.h>
5760c10f9bSchristos #include <errno.h>
58a824cb6aSwiz #include <locale.h>
5961f28255Scgd #include <stdio.h>
6061f28255Scgd #include <stdlib.h>
6161f28255Scgd #include <string.h>
62667b5ea1Smycroft #include <syslog.h>
63314d4116Skleink #include <time.h>
64f9a6471eSmycroft #include <tzfile.h>
65667b5ea1Smycroft #include <unistd.h>
664274523eScgd #include <util.h>
679337b5ddSchristos #if !HAVE_NBTOOL_CONFIG_H
689337b5ddSchristos #include <utmpx.h>
699337b5ddSchristos #endif
70667b5ea1Smycroft 
71667b5ea1Smycroft #include "extern.h"
7261f28255Scgd 
73a7151c0aSdsl static time_t tval;
7404f2a3c1Skim static int Rflag, aflag, jflag, rflag, nflag;
7561f28255Scgd 
7633586bccSdholland __dead static void badcanotime(const char *, const char *, size_t);
77a824cb6aSwiz static void setthetime(const char *);
78aa6d7bb5Sjoerg __dead static void usage(void);
79667b5ea1Smycroft 
80684d49e6Skre #if HAVE_NBTOOL_CONFIG_H
81684d49e6Skre static int parse_iso_datetime(time_t *, const char *);
82684d49e6Skre #else
83684d49e6Skre static char *fmt;
84684d49e6Skre #endif
85684d49e6Skre 
86ad6bcacaSkre #if !defined(isleap)
87ad6bcacaSkre # define isleap(y)   (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
88ad6bcacaSkre #endif
89ad6bcacaSkre 
90667b5ea1Smycroft int
91a824cb6aSwiz main(int argc, char *argv[])
9261f28255Scgd {
938a487b53Schristos 	char *buf;
948a487b53Schristos 	size_t bufsiz;
956310b596Schristos 	const char *format;
96a7151c0aSdsl 	int ch;
9760c10f9bSchristos 	long long val;
9860c10f9bSchristos 	struct tm *tm;
9902f83b58Skre 	char *default_tz;
10061f28255Scgd 
101bb88f10dSwiz 	setprogname(argv[0]);
102bad15fd3Scgd 	(void)setlocale(LC_ALL, "");
10318356811Sjtc 
10402f83b58Skre 	default_tz = getenv("TZ");
10502f83b58Skre 
10602f83b58Skre 	while ((ch = getopt(argc, argv, "ad:f:jnRr:Uuz:")) != -1) {
10715bc25dcShubertf 		switch (ch) {
108a7151c0aSdsl 		case 'a':		/* adjust time slowly */
109a7151c0aSdsl 			aflag = 1;
110a7151c0aSdsl 			nflag = 1;
111a7151c0aSdsl 			break;
112aa62ece7Schristos 		case 'd':
113aa62ece7Schristos 			rflag = 1;
114684d49e6Skre #ifdef HAVE_NBTOOL_CONFIG_H
115684d49e6Skre 			if (parse_iso_datetime(&tval, optarg))
116684d49e6Skre 				break;
117684d49e6Skre 			errx(EXIT_FAILURE,
118684d49e6Skre 			    "-d only supports ISO format in the tool version");
119684d49e6Skre 			break;
120684d49e6Skre #else
121684d49e6Skre 			errno = 0;
122ab56b9a7Schristos 			tval = parsedate(optarg, NULL, NULL);
123684d49e6Skre 			if (tval == -1 && errno != 0) {
12433586bccSdholland 				errx(EXIT_FAILURE,
12533586bccSdholland 				    "%s: Unrecognized date format", optarg);
12633586bccSdholland 			}
127aa62ece7Schristos 			break;
1289337b5ddSchristos 		case 'f':
1299337b5ddSchristos 			fmt = optarg;
1309337b5ddSchristos 			break;
131684d49e6Skre #endif
1325d27a9d6Sjdarrow 		case 'j':		/* don't set time */
1335d27a9d6Sjdarrow 			jflag = 1;
1345d27a9d6Sjdarrow 			break;
13561f28255Scgd 		case 'n':		/* don't set network */
13661f28255Scgd 			nflag = 1;
13761f28255Scgd 			break;
13804f2a3c1Skim 		case 'R':		/* RFC-5322 email format */
13904f2a3c1Skim 			Rflag = 1;
14004f2a3c1Skim 			break;
14161f28255Scgd 		case 'r':		/* user specified seconds */
14233586bccSdholland 			if (optarg[0] == '\0') {
14333586bccSdholland 				errx(EXIT_FAILURE, "<empty>: Invalid number");
14433586bccSdholland 			}
14560c10f9bSchristos 			errno = 0;
14660c10f9bSchristos 			val = strtoll(optarg, &buf, 0);
14733586bccSdholland 			if (errno) {
14833586bccSdholland 				err(EXIT_FAILURE, "%s", optarg);
14933586bccSdholland 			}
15033586bccSdholland 			if (optarg[0] == '\0' || *buf != '\0') {
15133586bccSdholland 				errx(EXIT_FAILURE,
15233586bccSdholland 				    "%s: Invalid number", optarg);
15333586bccSdholland 			}
15461f28255Scgd 			rflag = 1;
15560c10f9bSchristos 			tval = (time_t)val;
15661f28255Scgd 			break;
15702f83b58Skre 		case 'U':		/* reset to default timezone */
15802f83b58Skre 			if (default_tz)
15902f83b58Skre 				(void)setenv("TZ", default_tz, 1);
16002f83b58Skre 			else
16102f83b58Skre 				(void)unsetenv("TZ");
16202f83b58Skre 			break;
163d32f290dSmycroft 		case 'u':		/* do everything in UTC */
16410d3698aSgson 			(void)setenv("TZ", "UTC0", 1);
16561f28255Scgd 			break;
16602f83b58Skre 		case 'z':
16702f83b58Skre 			if (optarg[0] == '\0')
16802f83b58Skre 				(void)unsetenv("TZ");
16902f83b58Skre 			else
17002f83b58Skre 				(void)setenv("TZ", optarg, 1);
17102f83b58Skre 			break;
17261f28255Scgd 		default:
17361f28255Scgd 			usage();
17461f28255Scgd 		}
175a7151c0aSdsl 	}
17661f28255Scgd 	argc -= optind;
17761f28255Scgd 	argv += optind;
17861f28255Scgd 
179667b5ea1Smycroft 	if (!rflag && time(&tval) == -1)
180fa141339Speter 		err(EXIT_FAILURE, "time");
18161f28255Scgd 
18261f28255Scgd 
18361f28255Scgd 	/* allow the operands in any order */
18461f28255Scgd 	if (*argv && **argv == '+') {
185a617f5fdSdholland 		format = *argv;
18661f28255Scgd 		++argv;
18704f2a3c1Skim 	} else if (Rflag) {
18804f2a3c1Skim 		(void)setlocale(LC_TIME, "C");
18904f2a3c1Skim 		format = "+%a, %-e %b %Y %H:%M:%S %z";
19060c10f9bSchristos 	} else
19160c10f9bSchristos 		format = "+%a %b %e %H:%M:%S %Z %Y";
19261f28255Scgd 
19361f28255Scgd 	if (*argv) {
19461f28255Scgd 		setthetime(*argv);
19561f28255Scgd 		++argv;
196684d49e6Skre #ifndef HAVE_NBTOOL_CONFIG_H
197684d49e6Skre 	} else if (fmt) {
1989337b5ddSchristos 		usage();
199684d49e6Skre #endif
200684d49e6Skre 	}
20161f28255Scgd 
20261f28255Scgd 	if (*argv && **argv == '+')
203a617f5fdSdholland 		format = *argv;
20461f28255Scgd 
2058a487b53Schristos 	if ((buf = malloc(bufsiz = 1024)) == NULL)
2068a487b53Schristos 		goto bad;
20760c10f9bSchristos 
20860c10f9bSchristos 	if ((tm = localtime(&tval)) == NULL)
20933586bccSdholland 		err(EXIT_FAILURE, "%lld: localtime", (long long)tval);
21060c10f9bSchristos 
21160c10f9bSchristos 	while (strftime(buf, bufsiz, format, tm) == 0)
2128a487b53Schristos 		if ((buf = realloc(buf, bufsiz <<= 1)) == NULL)
2138a487b53Schristos 			goto bad;
21460c10f9bSchristos 
215a617f5fdSdholland 	(void)printf("%s\n", buf + 1);
2168a487b53Schristos 	free(buf);
2178a487b53Schristos 	return 0;
2188a487b53Schristos bad:
21960c10f9bSchristos 	err(EXIT_FAILURE, "Cannot allocate format buffer");
22061f28255Scgd }
22161f28255Scgd 
222a824cb6aSwiz static void
22333586bccSdholland badcanotime(const char *msg, const char *val, size_t where)
224a824cb6aSwiz {
22533586bccSdholland 	warnx("%s in canonical time", msg);
22633586bccSdholland 	warnx("%s", val);
22733586bccSdholland 	warnx("%*s", (int)where + 1, "^");
2289b5118f1Selad 	usage();
2299b5118f1Selad }
2309b5118f1Selad 
23165139bd9Smycroft #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
23265139bd9Smycroft 
233684d49e6Skre #if HAVE_NBTOOL_CONFIG_H
234684d49e6Skre 
235684d49e6Skre inline static int
236684d49e6Skre digitstring(const char *s, int len)
237684d49e6Skre {
238684d49e6Skre 	while (--len > 0) {
239684d49e6Skre 		if (!isdigit(*(unsigned char *)s))
240684d49e6Skre 			return 0;
241684d49e6Skre 		s++;
242684d49e6Skre 	}
243684d49e6Skre 	return 1;
244684d49e6Skre }
245684d49e6Skre 
246684d49e6Skre static int
247684d49e6Skre parse_iso_datetime(time_t * res, const char * string)
248684d49e6Skre {
249684d49e6Skre 	struct tm tm;
250684d49e6Skre 	time_t t;
251684d49e6Skre 
252684d49e6Skre 	memset(&tm, 0, sizeof tm);
253684d49e6Skre 
254684d49e6Skre 	if (!digitstring(string, 4))
255684d49e6Skre 		return 0;
256684d49e6Skre 	tm.tm_year = ATOI2(string) * 100;
257684d49e6Skre 	tm.tm_year += ATOI2(string);
258684d49e6Skre 	tm.tm_year -= 1900;
259684d49e6Skre 
260684d49e6Skre 	if (*string == '-')
261684d49e6Skre 		string++;
262684d49e6Skre 
263684d49e6Skre 	if (!digitstring(string, 2))
264684d49e6Skre 		return 0;
265684d49e6Skre 
266684d49e6Skre 	tm.tm_mon = ATOI2(string);
267684d49e6Skre 	if (tm.tm_mon < 1 || tm.tm_mon > 12)
268684d49e6Skre 		return 0;
269684d49e6Skre 	tm.tm_mon--;
270684d49e6Skre 
271684d49e6Skre 	if (*string == '-')
272684d49e6Skre 		string++;
273684d49e6Skre 
274684d49e6Skre 	if (!digitstring(string, 2))
275684d49e6Skre 		return 0;
276684d49e6Skre 
277684d49e6Skre 	tm.tm_mday = ATOI2(string);
278684d49e6Skre 	if (tm.tm_mday < 1)
279684d49e6Skre 		return 0;
280684d49e6Skre 	switch (tm.tm_mon) {
281684d49e6Skre 	case 0: case 2: case 4: case 6: case 7: case 9: case 11:
282684d49e6Skre 		if (tm.tm_mday > 31)
283684d49e6Skre 			return 0;
284684d49e6Skre 		break;
285684d49e6Skre 	case 3: case 5: case 8: case 10:
286684d49e6Skre 		if (tm.tm_mday > 30)
287684d49e6Skre 			return 0;
288684d49e6Skre 		break;
289684d49e6Skre 	case 1:
290684d49e6Skre 		if (tm.tm_mday > 28 + isleap(tm.tm_year + 1900))
291684d49e6Skre 			return 0;
292684d49e6Skre 		break;
293684d49e6Skre 	default:
294684d49e6Skre 		abort();
295684d49e6Skre 	}
296684d49e6Skre 
297684d49e6Skre 	do {
298684d49e6Skre 		if (*string == '\0')
299684d49e6Skre 			break;
300684d49e6Skre 		if (*string == 'T' || *string == 't' || *string == ' ' ||
301684d49e6Skre 		    *string == '-')
302684d49e6Skre 			string++;
303684d49e6Skre 
304684d49e6Skre 		if (!digitstring(string, 2))
305684d49e6Skre 			return 0;
306684d49e6Skre 		tm.tm_hour = ATOI2(string);
307684d49e6Skre 		if (tm.tm_hour > 23)
308684d49e6Skre 			return 0;
309684d49e6Skre 
310684d49e6Skre 		if (*string == '\0')
311684d49e6Skre 			break;
312684d49e6Skre 		if (*string == ':')
313684d49e6Skre 			string++;
314684d49e6Skre 
315684d49e6Skre 		if (!digitstring(string, 2))
316684d49e6Skre 			return 0;
317684d49e6Skre 		tm.tm_min = ATOI2(string);
318684d49e6Skre 		if (tm.tm_min >= 60)
319684d49e6Skre 			return 0;
320684d49e6Skre 
321684d49e6Skre 		if (*string == '\0')
322684d49e6Skre 			break;
323684d49e6Skre 		if (*string == ':')
324684d49e6Skre 			string++;
325684d49e6Skre 
326684d49e6Skre 		if (!digitstring(string, 2))
327684d49e6Skre 			return 0;
328684d49e6Skre 		tm.tm_sec = ATOI2(string);
329684d49e6Skre 		if (tm.tm_sec >= 60)
330684d49e6Skre 			return 0;
331684d49e6Skre 	} while (0);
332684d49e6Skre 
333684d49e6Skre 	if (*string != '\0')
334684d49e6Skre 		return 0;
335684d49e6Skre 
336684d49e6Skre 	tm.tm_isdst = -1;
337684d49e6Skre 	tm.tm_wday = -1;
338684d49e6Skre 
339684d49e6Skre 	t = mktime(&tm);
340684d49e6Skre 	if (tm.tm_wday == -1)
341684d49e6Skre 		return 0;
342684d49e6Skre 
343684d49e6Skre 	*res = t;
344684d49e6Skre 	return 1;
345684d49e6Skre }
346684d49e6Skre 
347684d49e6Skre #endif	/*NBTOOL*/
348684d49e6Skre 
34991825c12Sgmcgarry static void
350a824cb6aSwiz setthetime(const char *p)
35161f28255Scgd {
35261f28255Scgd 	struct timeval tv;
353a7151c0aSdsl 	time_t new_time;
354a824cb6aSwiz 	struct tm *lt;
35533586bccSdholland 	const char *dot, *t, *op;
3561b1ee3c7Scbiere 	size_t len;
3571b1ee3c7Scbiere 	int yearset;
35861f28255Scgd 
3599337b5ddSchristos 	if ((lt = localtime(&tval)) == NULL)
3609337b5ddSchristos 		err(EXIT_FAILURE, "%lld: localtime", (long long)tval);
3619337b5ddSchristos 
3629337b5ddSchristos 	lt->tm_isdst = -1;			/* Divine correct DST */
3639337b5ddSchristos 
364684d49e6Skre #ifndef HAVE_NBTOOL_CONFIG_H
3659337b5ddSchristos 	if (fmt) {
3669337b5ddSchristos 		t = strptime(p, fmt, lt);
3679337b5ddSchristos 		if (t == NULL) {
3689337b5ddSchristos 			warnx("Failed conversion of ``%s''"
3699337b5ddSchristos 			    " using format ``%s''\n", p, fmt);
3709337b5ddSchristos 		} else if (*t != '\0')
3719337b5ddSchristos 			warnx("Ignoring %zu extraneous"
3729337b5ddSchristos 				" characters in date string (%s)",
3739337b5ddSchristos 				strlen(t), t);
3749337b5ddSchristos 		goto setit;
3759337b5ddSchristos 	}
376*5a5d821fSkre 	if (getenv("POSIXLY_CORRECT") != NULL) {
377*5a5d821fSkre 		int yrdigs;
378*5a5d821fSkre 		const char * const e = "Bad POSIX format date ``%s''";
379*5a5d821fSkre 
380*5a5d821fSkre 		t = strptime(p, "%m%d%H%M", lt);
381*5a5d821fSkre 		if (t == NULL)
382*5a5d821fSkre 			errx(EXIT_FAILURE, e, p);
383*5a5d821fSkre 		if (*t != '\0') {
384*5a5d821fSkre 			yrdigs = strspn(t, "0123456789");
385*5a5d821fSkre 			if (yrdigs != 2 && yrdigs != 4)
386*5a5d821fSkre 				errx(EXIT_FAILURE, e, p);
387*5a5d821fSkre 			t = strptime(t, yrdigs == 2 ? "%y" : "%Y", lt);
388*5a5d821fSkre 			if (t == NULL || *t != '\0')
389*5a5d821fSkre 				errx(EXIT_FAILURE, e, p);
390*5a5d821fSkre 		}
391*5a5d821fSkre 		goto setit;
392*5a5d821fSkre 	}
393684d49e6Skre #endif
394667b5ea1Smycroft 	for (t = p, dot = NULL; *t; ++t) {
39533586bccSdholland 		if (*t == '.') {
39633586bccSdholland 			if (dot == NULL) {
397667b5ea1Smycroft 				dot = t;
39833586bccSdholland 			} else {
39933586bccSdholland 				badcanotime("Unexpected dot", p, t - p);
400667b5ea1Smycroft 			}
40133586bccSdholland 		} else if (!isdigit((unsigned char)*t)) {
40233586bccSdholland 			badcanotime("Expected digit", p, t - p);
40333586bccSdholland 		}
404667b5ea1Smycroft 	}
40561f28255Scgd 
406a17ce6adSbjh21 
407667b5ea1Smycroft 	if (dot != NULL) {			/* .ss */
408dbfa4f81Smycroft 		len = strlen(dot);
40933586bccSdholland 		if (len > 3) {
41033586bccSdholland 			badcanotime("Unexpected digit after seconds field",
41133586bccSdholland 				    p, strlen(p) - 1);
41233586bccSdholland 		} else if (len < 3) {
41333586bccSdholland 			badcanotime("Expected digit in seconds field",
41433586bccSdholland 				    p, strlen(p));
41533586bccSdholland 		}
416dbfa4f81Smycroft 		++dot;
417667b5ea1Smycroft 		lt->tm_sec = ATOI2(dot);
4189b5118f1Selad 		if (lt->tm_sec > 61)
41933586bccSdholland 			badcanotime("Seconds out of range", p, strlen(p) - 1);
420dbfa4f81Smycroft 	} else {
421dbfa4f81Smycroft 		len = 0;
42261f28255Scgd 		lt->tm_sec = 0;
423dbfa4f81Smycroft 	}
42461f28255Scgd 
42533586bccSdholland 	op = p;
426f9a6471eSmycroft 	yearset = 0;
427dbfa4f81Smycroft 	switch (strlen(p) - len) {
42865139bd9Smycroft 	case 12:				/* cc */
42965139bd9Smycroft 		lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE;
4309b5118f1Selad 		if (lt->tm_year < 0)
43133586bccSdholland 			badcanotime("Year before 1900", op, p - op + 1);
432f9a6471eSmycroft 		yearset = 1;
433f9a6471eSmycroft 		/* FALLTHROUGH */
434f9a6471eSmycroft 	case 10:				/* yy */
435f9a6471eSmycroft 		if (yearset) {
43665139bd9Smycroft 			lt->tm_year += ATOI2(p);
437f9a6471eSmycroft 		} else {
438f9a6471eSmycroft 			yearset = ATOI2(p);
43965139bd9Smycroft 			if (yearset < 69)
44065139bd9Smycroft 				lt->tm_year = yearset + 2000 - TM_YEAR_BASE;
441eeec814aSmycroft 			else
44265139bd9Smycroft 				lt->tm_year = yearset + 1900 - TM_YEAR_BASE;
443f9a6471eSmycroft 		}
44461f28255Scgd 		/* FALLTHROUGH */
44561f28255Scgd 	case 8:					/* mm */
44661f28255Scgd 		lt->tm_mon = ATOI2(p);
4479b5118f1Selad 		if (lt->tm_mon > 12 || lt->tm_mon == 0)
44833586bccSdholland 			badcanotime("Month out of range", op, p - op - 1);
44961f28255Scgd 		--lt->tm_mon;			/* time struct is 0 - 11 */
45061f28255Scgd 		/* FALLTHROUGH */
45161f28255Scgd 	case 6:					/* dd */
45261f28255Scgd 		lt->tm_mday = ATOI2(p);
4539b5118f1Selad 		switch (lt->tm_mon) {
4549b5118f1Selad 		case 0:
4559b5118f1Selad 		case 2:
4569b5118f1Selad 		case 4:
4579b5118f1Selad 		case 6:
4589b5118f1Selad 		case 7:
4599b5118f1Selad 		case 9:
4609b5118f1Selad 		case 11:
4619b5118f1Selad 			if (lt->tm_mday > 31 || lt->tm_mday == 0)
46233586bccSdholland 				badcanotime("Day out of range (max 31)",
46333586bccSdholland 					    op, p - op - 1);
4649b5118f1Selad 			break;
4659b5118f1Selad 		case 3:
4669b5118f1Selad 		case 5:
4679b5118f1Selad 		case 8:
4689b5118f1Selad 		case 10:
4699b5118f1Selad 			if (lt->tm_mday > 30 || lt->tm_mday == 0)
47033586bccSdholland 				badcanotime("Day out of range (max 30)",
47133586bccSdholland 					    op, p - op - 1);
4729b5118f1Selad 			break;
4739b5118f1Selad 		case 1:
47433586bccSdholland 			if (isleap(lt->tm_year + TM_YEAR_BASE)) {
47533586bccSdholland 				if (lt->tm_mday > 29 || lt->tm_mday == 0) {
47633586bccSdholland 					badcanotime("Day out of range "
47733586bccSdholland 						    "(max 29)",
47833586bccSdholland 						    op, p - op - 1);
47933586bccSdholland 				}
48033586bccSdholland 			} else {
48133586bccSdholland 				if (lt->tm_mday > 28 || lt->tm_mday == 0) {
48233586bccSdholland 					badcanotime("Day out of range "
48333586bccSdholland 						    "(max 28)",
48433586bccSdholland 						    op, p - op - 1);
48533586bccSdholland 				}
48633586bccSdholland 			}
4879b5118f1Selad 			break;
4889b5118f1Selad 		default:
48933586bccSdholland 			/*
49033586bccSdholland 			 * If the month was given, it's already been
49133586bccSdholland 			 * checked.  If a bad value came back from
49233586bccSdholland 			 * localtime, something's badly broken.
49333586bccSdholland 			 * (make this an assertion?)
49433586bccSdholland 			 */
49533586bccSdholland 			errx(EXIT_FAILURE, "localtime gave invalid month %d",
49633586bccSdholland 			    lt->tm_mon);
4979b5118f1Selad 		}
49861f28255Scgd 		/* FALLTHROUGH */
49961f28255Scgd 	case 4:					/* hh */
50061f28255Scgd 		lt->tm_hour = ATOI2(p);
5019b5118f1Selad 		if (lt->tm_hour > 23)
50233586bccSdholland 			badcanotime("Hour out of range", op, p - op - 1);
50361f28255Scgd 		/* FALLTHROUGH */
50461f28255Scgd 	case 2:					/* mm */
50561f28255Scgd 		lt->tm_min = ATOI2(p);
5069b5118f1Selad 		if (lt->tm_min > 59)
50733586bccSdholland 			badcanotime("Minute out of range", op, p - op - 1);
50861f28255Scgd 		break;
509a7151c0aSdsl 	case 0:					/* was just .sss */
510a7151c0aSdsl 		if (len != 0)
511a7151c0aSdsl 			break;
512a7151c0aSdsl 		/* FALLTHROUGH */
51361f28255Scgd 	default:
51433586bccSdholland 	    if (strlen(p) - len > 12) {
51533586bccSdholland 		    badcanotime("Too many digits", p, 12);
51633586bccSdholland 	    } else {
51733586bccSdholland 		    badcanotime("Not enough digits", p, strlen(p) - len);
51833586bccSdholland 	    }
51961f28255Scgd 	}
5209337b5ddSchristos setit:
521d32f290dSmycroft 	/* convert broken-down time to UTC clock time */
52233586bccSdholland 	if ((new_time = mktime(lt)) == -1) {
52333586bccSdholland 		/* Can this actually happen? */
5249337b5ddSchristos 		err(EXIT_FAILURE, "mktime");
52533586bccSdholland 	}
52661f28255Scgd 
5275d27a9d6Sjdarrow 	/* if jflag is set, don't actually change the time, just return */
5285d27a9d6Sjdarrow 	if (jflag) {
5295d27a9d6Sjdarrow 		tval = new_time;
5305d27a9d6Sjdarrow 		return;
5315d27a9d6Sjdarrow 	}
5325d27a9d6Sjdarrow 
53361f28255Scgd 	/* set the time */
534634a8027Schristos #ifndef HAVE_NBTOOL_CONFIG_H
5359337b5ddSchristos 	struct utmpx utx;
5369337b5ddSchristos 	memset(&utx, 0, sizeof(utx));
5379337b5ddSchristos 	utx.ut_type = OLD_TIME;
5389337b5ddSchristos 	(void)gettimeofday(&utx.ut_tv, NULL);
5399337b5ddSchristos 	pututxline(&utx);
5409337b5ddSchristos 
541a7151c0aSdsl 	if (nflag || netsettime(new_time)) {
54261f28255Scgd 		logwtmp("|", "date", "");
543a7151c0aSdsl 		if (aflag) {
544a7151c0aSdsl 			tv.tv_sec = new_time - tval;
545a7151c0aSdsl 			tv.tv_usec = 0;
54615bc25dcShubertf 			if (adjtime(&tv, NULL))
547a0cb2a83Sjnemeth 				err(EXIT_FAILURE, "adjtime");
548a7151c0aSdsl 		} else {
549d8580191Sdsl 			tval = new_time;
55061f28255Scgd 			tv.tv_sec = tval;
55161f28255Scgd 			tv.tv_usec = 0;
55215bc25dcShubertf 			if (settimeofday(&tv, NULL))
553a0cb2a83Sjnemeth 				err(EXIT_FAILURE, "settimeofday");
554a7151c0aSdsl 		}
55561f28255Scgd 		logwtmp("{", "date", "");
55661f28255Scgd 	}
5579337b5ddSchristos 	utx.ut_type = NEW_TIME;
5589337b5ddSchristos 	(void)gettimeofday(&utx.ut_tv, NULL);
5599337b5ddSchristos 	pututxline(&utx);
560667b5ea1Smycroft 
561667b5ea1Smycroft 	if ((p = getlogin()) == NULL)
562667b5ea1Smycroft 		p = "???";
563667b5ea1Smycroft 	syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p);
5649337b5ddSchristos #else
5659337b5ddSchristos 	errx(EXIT_FAILURE, "Can't set the time in the tools version");
5669337b5ddSchristos #endif
56761f28255Scgd }
56861f28255Scgd 
569667b5ea1Smycroft static void
570a824cb6aSwiz usage(void)
57161f28255Scgd {
57261f28255Scgd 	(void)fprintf(stderr,
57302f83b58Skre 	    "Usage: %s [-ajnRUu] [-d date] [-r seconds] [-z zone] [+format]",
57460c10f9bSchristos 	    getprogname());
57502f83b58Skre 	(void)fprintf(stderr, "\n\t%*s[[[[[[CC]yy]mm]dd]HH]MM[.SS]]\n",
57602f83b58Skre 	    (int)strlen(getprogname()), "");
5779337b5ddSchristos 	(void)fprintf(stderr,
578219c5cc7Skim 	    "       %s [-ajnRu] -f input_format new_date [+format]\n",
5799337b5ddSchristos 	    getprogname());
58015bc25dcShubertf 	exit(EXIT_FAILURE);
5819dc385beSmycroft 	/* NOTREACHED */
58261f28255Scgd }
583