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