xref: /illumos-gate/usr/src/cmd/date/date.c (revision 63ff6dceb8bca4c7bef31895a1286319033330d0)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
28  */
29 
30 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
31 /*	  All Rights Reserved  	*/
32 
33 /*
34  * University Copyright- Copyright (c) 1982, 1986, 1988
35  * The Regents of the University of California
36  * All Rights Reserved
37  *
38  * University Acknowledgment- Portions of this document are derived from
39  * software developed by the University of California, Berkeley, and its
40  * contributors.
41  */
42 
43 /*
44  *	date - with format capabilities and international flair
45  */
46 
47 #include	<locale.h>
48 #include	<fcntl.h>
49 #include	<langinfo.h>
50 #include	<stdio.h>
51 #include	<stdlib.h>
52 #include	<string.h>
53 #include	<time.h>
54 #include	<unistd.h>
55 #include	<sys/time.h>
56 #include	<sys/types.h>
57 #include	<ctype.h>
58 #include	<utmpx.h>
59 #include	<tzfile.h>
60 
61 #define	year_size(A)	((isleap(A)) ? 366 : 365)
62 static 	char	buf[BUFSIZ];
63 static	time_t	clock_val;
64 static  short	month_size[12] =
65 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
66 static  struct  utmpx wtmpx[2] = {
67 	{"", "", OTIME_MSG, 0, OLD_TIME, 0, 0, 0},
68 	{"", "", NTIME_MSG, 0, NEW_TIME, 0, 0, 0}
69 	};
70 static char *usage =
71 	"usage:\tdate [-u] mmddHHMM[[cc]yy][.SS]\n\tdate [-u] [+format]\n"
72 	"\tdate -a [-]sss[.fff]\n";
73 static int uflag = 0;
74 static int Rflag = 0;
75 
76 static int get_adj(char *, struct timeval *);
77 static int setdate(struct tm *, char *);
78 
79 int
80 main(int argc, char **argv)
81 {
82 	struct tm *tp, tm;
83 	struct timeval tv;
84 	char *fmt;
85 	int c, aflag = 0, illflag = 0;
86 
87 	(void) setlocale(LC_ALL, "");
88 
89 #if !defined(TEXT_DOMAIN)
90 #define	TEXT_DOMAIN "SYS_TEST"
91 #endif
92 	(void) textdomain(TEXT_DOMAIN);
93 
94 	while ((c = getopt(argc, argv, "a:uR")) != EOF)
95 		switch (c) {
96 		case 'a':
97 			aflag++;
98 			if (get_adj(optarg, &tv) < 0) {
99 				(void) fprintf(stderr,
100 				    gettext("date: invalid argument -- %s\n"),
101 				    optarg);
102 				illflag++;
103 			}
104 			break;
105 		case 'u':
106 			uflag++;
107 			break;
108 		case 'R':
109 			Rflag++;
110 			break;
111 		default:
112 			illflag++;
113 		}
114 
115 	argc -= optind;
116 	argv  = &argv[optind];
117 
118 	/* -a is mutually exclusive with -u and -R */
119 	if (uflag && aflag)
120 		illflag++;
121 	if (Rflag && aflag)
122 		illflag++;
123 
124 	if (illflag) {
125 		(void) fprintf(stderr, gettext(usage));
126 		exit(1);
127 	}
128 
129 	(void) time(&clock_val);
130 
131 	if (aflag) {
132 		if (adjtime(&tv, 0) < 0) {
133 			perror(gettext("date: Failed to adjust date"));
134 			exit(1);
135 		}
136 		exit(0);
137 	}
138 
139 	if (argc > 0) {
140 		if (*argv[0] == '+')
141 			fmt = &argv[0][1];
142 		else {
143 			if (setdate(localtime(&clock_val), argv[0])) {
144 				(void) fprintf(stderr, gettext(usage));
145 				exit(1);
146 			}
147 			fmt = nl_langinfo(_DATE_FMT);
148 		}
149 	} else if (Rflag) {
150 		fmt = "%a, %d %h %Y %H:%M:%S %z";
151 	} else
152 		fmt = nl_langinfo(_DATE_FMT);
153 
154 	if (uflag) {
155 		(void) putenv("TZ=GMT0");
156 		tzset();
157 		tp = gmtime(&clock_val);
158 	} else
159 		tp = localtime(&clock_val);
160 	(void) memcpy(&tm, tp, sizeof (struct tm));
161 	(void) strftime(buf, BUFSIZ, fmt, &tm);
162 
163 	(void) puts(buf);
164 
165 	return (0);
166 }
167 
168 int
169 setdate(struct tm *current_date, char *date)
170 {
171 	int	i;
172 	int	mm;
173 	int	hh;
174 	int	min;
175 	int	sec = 0;
176 	char	*secptr;
177 	int	yy;
178 	int	dd	= 0;
179 	int	minidx	= 6;
180 	int	len;
181 	int	dd_check;
182 
183 	/*  Parse date string  */
184 	if ((secptr = strchr(date, '.')) != NULL && strlen(&secptr[1]) == 2 &&
185 	    isdigit(secptr[1]) && isdigit(secptr[2]) &&
186 	    (sec = atoi(&secptr[1])) >= 0 && sec < 60)
187 		secptr[0] = '\0';	/* eat decimal point only on success */
188 
189 	len = strlen(date);
190 
191 	for (i = 0; i < len; i++) {
192 		if (!isdigit(date[i])) {
193 			(void) fprintf(stderr,
194 			gettext("date: bad conversion\n"));
195 			exit(1);
196 		}
197 	}
198 	switch (strlen(date)) {
199 	case 12:
200 		yy = atoi(&date[8]);
201 		date[8] = '\0';
202 		break;
203 	case 10:
204 		/*
205 		 * The YY format has the following representation:
206 		 * 00-68 = 2000 thru 2068
207 		 * 69-99 = 1969 thru 1999
208 		 */
209 		if (atoi(&date[8]) <= 68) {
210 			yy = 1900 + (atoi(&date[8]) + 100);
211 		} else {
212 			yy = 1900 + atoi(&date[8]);
213 		}
214 		date[8] = '\0';
215 		break;
216 	case 8:
217 		yy = 1900 + current_date->tm_year;
218 		break;
219 	case 4:
220 		yy = 1900 + current_date->tm_year;
221 		mm = current_date->tm_mon + 1; 	/* tm_mon goes from 1 to 11 */
222 		dd = current_date->tm_mday;
223 		minidx = 2;
224 		break;
225 	default:
226 		(void) fprintf(stderr, gettext("date: bad conversion\n"));
227 		return (1);
228 	}
229 
230 	min = atoi(&date[minidx]);
231 	date[minidx] = '\0';
232 	hh = atoi(&date[minidx-2]);
233 	date[minidx-2] = '\0';
234 
235 	if (!dd) {
236 		/*
237 		 * if dd is 0 (not between 1 and 31), then
238 		 * read the value supplied by the user.
239 		 */
240 		dd = atoi(&date[2]);
241 		date[2] = '\0';
242 		mm = atoi(&date[0]);
243 	}
244 
245 	if (hh == 24)
246 		hh = 0, dd++;
247 
248 	/*  Validate date elements  */
249 	dd_check = 0;
250 	if (mm >= 1 && mm <= 12) {
251 		dd_check = month_size[mm - 1];	/* get days in this month */
252 		if (mm == 2 && isleap(yy))	/* adjust for leap year */
253 			dd_check++;
254 	}
255 	if (!((mm >= 1 && mm <= 12) && (dd >= 1 && dd <= dd_check) &&
256 	    (hh >= 0 && hh <= 23) && (min >= 0 && min <= 59))) {
257 		(void) fprintf(stderr, gettext("date: bad conversion\n"));
258 		return (1);
259 	}
260 
261 	/*  Build date and time number  */
262 	for (clock_val = 0, i = 1970; i < yy; i++)
263 		clock_val += year_size(i);
264 	/*  Adjust for leap year  */
265 	if (isleap(yy) && mm >= 3)
266 		clock_val += 1;
267 	/*  Adjust for different month lengths  */
268 	while (--mm)
269 		clock_val += (time_t)month_size[mm - 1];
270 	/*  Load up the rest  */
271 	clock_val += (time_t)(dd - 1);
272 	clock_val *= 24;
273 	clock_val += (time_t)hh;
274 	clock_val *= 60;
275 	clock_val += (time_t)min;
276 	clock_val *= 60;
277 	clock_val += sec;
278 
279 	if (!uflag) {
280 		/* convert to GMT assuming standard time */
281 		/* correction is made in localtime(3C) */
282 
283 		/*
284 		 * call localtime to set up "timezone" variable applicable
285 		 * for clock_val time, to support Olson timezones which
286 		 * can allow timezone rules to change.
287 		 */
288 		(void) localtime(&clock_val);
289 
290 		clock_val += (time_t)timezone;
291 
292 		/* correct if daylight savings time in effect */
293 
294 		if (localtime(&clock_val)->tm_isdst)
295 			clock_val = clock_val - (time_t)(timezone - altzone);
296 	}
297 
298 	(void) time(&wtmpx[0].ut_xtime);
299 	if (stime(&clock_val) < 0) {
300 		perror("date");
301 		return (1);
302 	}
303 #if defined(i386)
304 	/* correct the kernel's "gmt_lag" and the PC's RTC */
305 	(void) system("/usr/sbin/rtc -c > /dev/null 2>&1");
306 #endif
307 	(void) time(&wtmpx[1].ut_xtime);
308 	(void) pututxline(&wtmpx[0]);
309 	(void) pututxline(&wtmpx[1]);
310 	(void) updwtmpx(WTMPX_FILE, &wtmpx[0]);
311 	(void) updwtmpx(WTMPX_FILE, &wtmpx[1]);
312 	return (0);
313 }
314 
315 int
316 get_adj(char *cp, struct timeval *tp)
317 {
318 	register int mult;
319 	int sign;
320 
321 	/* arg must be [-]sss[.fff] */
322 
323 	tp->tv_sec = tp->tv_usec = 0;
324 	if (*cp == '-') {
325 		sign = -1;
326 		cp++;
327 	} else {
328 		sign = 1;
329 	}
330 
331 	while (*cp >= '0' && *cp <= '9') {
332 		tp->tv_sec *= 10;
333 		tp->tv_sec += *cp++ - '0';
334 	}
335 	if (*cp == '.') {
336 		cp++;
337 		mult = 100000;
338 		while (*cp >= '0' && *cp <= '9') {
339 			tp->tv_usec += (*cp++ - '0') * mult;
340 			mult /= 10;
341 		}
342 	}
343 	/*
344 	 * if there's anything left in the string,
345 	 * the input was invalid.
346 	 */
347 	if (*cp) {
348 		return (-1);
349 	} else {
350 		tp->tv_sec *= sign;
351 		tp->tv_usec *= sign;
352 		return (0);
353 	}
354 }
355