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
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.
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
30 * Copyright (c) 2017, Joyent, Inc.
32 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
33 /* All Rights Reserved */
36 * University Copyright- Copyright (c) 1982, 1986, 1988
37 * The Regents of the University of California
40 * University Acknowledgment- Portions of this document are derived from
41 * software developed by the University of California, Berkeley, and its
46 * date - with format capabilities and international flair
58 #include <sys/types.h>
65 #define year_size(A) ((isleap(A)) ? 366 : 365)
66 static char buf
[BUFSIZ
];
67 static time_t clock_val
;
68 static short month_size
[12] =
69 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
70 static struct utmpx wtmpx
[2] = {
71 {"", "", OTIME_MSG
, 0, OLD_TIME
, 0, 0, 0},
72 {"", "", NTIME_MSG
, 0, NEW_TIME
, 0, 0, 0}
75 "usage:\tdate [-u] mmddHHMM[[cc]yy][.SS]\n"
76 "\tdate [-Ru] [-r seconds | filename] [+format]\n"
77 "\tdate -a [-]sss[.fff]\n";
82 static int get_adj(char *, struct timeval
*);
83 static int setdate(struct tm
*, char *);
84 static void fmt_extensions(char *, size_t,
85 const char *, const struct timespec
*);
88 main(int argc
, char **argv
)
94 int c
, aflag
= 0, illflag
= 0;
97 (void) setlocale(LC_ALL
, "");
99 #if !defined(TEXT_DOMAIN)
100 #define TEXT_DOMAIN "SYS_TEST"
102 (void) textdomain(TEXT_DOMAIN
);
104 while ((c
= getopt(argc
, argv
, "a:uRr:")) != EOF
)
108 if (get_adj(optarg
, &tv
) < 0) {
109 (void) fprintf(stderr
,
110 gettext("date: invalid argument -- %s\n"),
124 * BSD originally used -r to specify a unix time. GNU
125 * used -r to specify a reference to a file. Now, like
126 * some BSDs we attempt to parse the time. If we can,
127 * then we use that, otherwise we fall back and treat it
132 ts
.tv_sec
= strtol(optarg
, &eptr
, 0);
133 if (errno
== EINVAL
|| *eptr
!= '\0') {
135 if (stat(optarg
, &st
) == 0) {
136 ts
.tv_sec
= st
.st_mtime
;
138 (void) fprintf(stderr
,
139 gettext("date: failed to get stat "
140 "information about %s: %s\n"),
141 optarg
, strerror(errno
));
144 } else if (errno
!= 0) {
145 (void) fprintf(stderr
,
146 gettext("date: failed to parse -r "
147 "argument: %s\n"), optarg
);
156 argv
= &argv
[optind
];
158 /* -a is mutually exclusive with -u, -R, and -r */
167 (void) fprintf(stderr
, gettext(usage
));
172 if (clock_gettime(CLOCK_REALTIME
, &ts
) != 0) {
173 perror(gettext("date: Failed to obtain system time"));
177 clock_val
= ts
.tv_sec
;
180 if (adjtime(&tv
, 0) < 0) {
181 perror(gettext("date: Failed to adjust date"));
191 if (setdate(localtime(&clock_val
), argv
[0])) {
192 (void) fprintf(stderr
, gettext(usage
));
195 fmt
= nl_langinfo(_DATE_FMT
);
198 fmt
= "%a, %d %h %Y %H:%M:%S %z";
200 fmt
= nl_langinfo(_DATE_FMT
);
202 fmt_extensions(fmtbuf
, sizeof (fmtbuf
), fmt
, &ts
);
205 (void) putenv("TZ=GMT0");
207 tp
= gmtime(&clock_val
);
209 tp
= localtime(&clock_val
);
210 (void) memcpy(&tm
, tp
, sizeof (struct tm
));
211 (void) strftime(buf
, BUFSIZ
, fmtbuf
, &tm
);
219 setdate(struct tm
*current_date
, char *date
)
233 /* Parse date string */
234 if ((secptr
= strchr(date
, '.')) != NULL
&& strlen(&secptr
[1]) == 2 &&
235 isdigit(secptr
[1]) && isdigit(secptr
[2]) &&
236 (sec
= atoi(&secptr
[1])) >= 0 && sec
< 60)
237 secptr
[0] = '\0'; /* eat decimal point only on success */
241 for (i
= 0; i
< len
; i
++) {
242 if (!isdigit(date
[i
])) {
243 (void) fprintf(stderr
,
244 gettext("date: bad conversion\n"));
248 switch (strlen(date
)) {
255 * The YY format has the following representation:
256 * 00-68 = 2000 thru 2068
257 * 69-99 = 1969 thru 1999
259 if (atoi(&date
[8]) <= 68) {
260 yy
= 1900 + (atoi(&date
[8]) + 100);
262 yy
= 1900 + atoi(&date
[8]);
267 yy
= 1900 + current_date
->tm_year
;
270 yy
= 1900 + current_date
->tm_year
;
271 mm
= current_date
->tm_mon
+ 1; /* tm_mon goes from 1 to 11 */
272 dd
= current_date
->tm_mday
;
276 (void) fprintf(stderr
, gettext("date: bad conversion\n"));
280 min
= atoi(&date
[minidx
]);
282 hh
= atoi(&date
[minidx
-2]);
283 date
[minidx
-2] = '\0';
287 * if dd is 0 (not between 1 and 31), then
288 * read the value supplied by the user.
298 /* Validate date elements */
300 if (mm
>= 1 && mm
<= 12) {
301 dd_check
= month_size
[mm
- 1]; /* get days in this month */
302 if (mm
== 2 && isleap(yy
)) /* adjust for leap year */
305 if (!((mm
>= 1 && mm
<= 12) && (dd
>= 1 && dd
<= dd_check
) &&
306 (hh
>= 0 && hh
<= 23) && (min
>= 0 && min
<= 59))) {
307 (void) fprintf(stderr
, gettext("date: bad conversion\n"));
311 /* Build date and time number */
312 for (clock_val
= 0, i
= 1970; i
< yy
; i
++)
313 clock_val
+= year_size(i
);
314 /* Adjust for leap year */
315 if (isleap(yy
) && mm
>= 3)
317 /* Adjust for different month lengths */
319 clock_val
+= (time_t)month_size
[mm
- 1];
320 /* Load up the rest */
321 clock_val
+= (time_t)(dd
- 1);
323 clock_val
+= (time_t)hh
;
325 clock_val
+= (time_t)min
;
330 /* convert to GMT assuming standard time */
331 /* correction is made in localtime(3C) */
334 * call localtime to set up "timezone" variable applicable
335 * for clock_val time, to support Olson timezones which
336 * can allow timezone rules to change.
338 (void) localtime(&clock_val
);
340 clock_val
+= (time_t)timezone
;
342 /* correct if daylight savings time in effect */
344 if (localtime(&clock_val
)->tm_isdst
)
345 clock_val
= clock_val
- (time_t)(timezone
- altzone
);
348 (void) time(&wtmpx
[0].ut_xtime
);
349 if (stime(&clock_val
) < 0) {
354 /* correct the kernel's "gmt_lag" and the PC's RTC */
355 (void) system("/usr/sbin/rtc -c > /dev/null 2>&1");
357 (void) time(&wtmpx
[1].ut_xtime
);
358 (void) pututxline(&wtmpx
[0]);
359 (void) pututxline(&wtmpx
[1]);
360 (void) updwtmpx(WTMPX_FILE
, &wtmpx
[0]);
361 (void) updwtmpx(WTMPX_FILE
, &wtmpx
[1]);
366 get_adj(char *cp
, struct timeval
*tp
)
371 /* arg must be [-]sss[.fff] */
373 tp
->tv_sec
= tp
->tv_usec
= 0;
381 while (*cp
>= '0' && *cp
<= '9') {
383 tp
->tv_sec
+= *cp
++ - '0';
388 while (*cp
>= '0' && *cp
<= '9') {
389 tp
->tv_usec
+= (*cp
++ - '0') * mult
;
394 * if there's anything left in the string,
395 * the input was invalid.
407 * Extensions that cannot be interpreted by strftime are interpreted here.
410 fmt_extensions(char *fmtbuf
, size_t len
,
411 const char *fmt
, const struct timespec
*tsp
)
416 for (p
= fmt
, q
= fmtbuf
; *p
!= '\0' && q
< fmtbuf
+ len
; ++p
) {
421 q
+= snprintf(q
, len
- (q
- fmtbuf
),
422 "%09lu", tsp
->tv_nsec
);
429 if (q
< fmtbuf
+ len
)
432 fmtbuf
[len
- 1] = '\0';