1*cf5a6c84SAndroid Build Coastguard Worker /* date.c - set/get the date
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2012 Andre Renaud <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker *
5*cf5a6c84SAndroid Build Coastguard Worker * See http://opengroup.org/onlinepubs/9699919799/utilities/date.html
6*cf5a6c84SAndroid Build Coastguard Worker *
7*cf5a6c84SAndroid Build Coastguard Worker * Note: setting a 2 year date is 50 years back/forward from today,
8*cf5a6c84SAndroid Build Coastguard Worker * not posix's hardwired magic dates.
9*cf5a6c84SAndroid Build Coastguard Worker
10*cf5a6c84SAndroid Build Coastguard Worker USE_DATE(NEWTOY(date, ">1d:D:I(iso-8601):;r:s:u(utc)[!dr]", TOYFLAG_BIN))
11*cf5a6c84SAndroid Build Coastguard Worker
12*cf5a6c84SAndroid Build Coastguard Worker config DATE
13*cf5a6c84SAndroid Build Coastguard Worker bool "date"
14*cf5a6c84SAndroid Build Coastguard Worker default y
15*cf5a6c84SAndroid Build Coastguard Worker help
16*cf5a6c84SAndroid Build Coastguard Worker usage: date [-u] [-I RES] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]
17*cf5a6c84SAndroid Build Coastguard Worker
18*cf5a6c84SAndroid Build Coastguard Worker Set/get the current date/time. With no SET shows the current date.
19*cf5a6c84SAndroid Build Coastguard Worker
20*cf5a6c84SAndroid Build Coastguard Worker -d Show DATE instead of current time (convert date format)
21*cf5a6c84SAndroid Build Coastguard Worker -D +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])
22*cf5a6c84SAndroid Build Coastguard Worker -I RES ISO 8601 with RESolution d=date/h=hours/m=minutes/s=seconds/n=ns
23*cf5a6c84SAndroid Build Coastguard Worker -r Use modification time of FILE instead of current date
24*cf5a6c84SAndroid Build Coastguard Worker -s DATE Set the system clock to DATE.
25*cf5a6c84SAndroid Build Coastguard Worker -u Use UTC instead of current timezone
26*cf5a6c84SAndroid Build Coastguard Worker
27*cf5a6c84SAndroid Build Coastguard Worker Supported input formats:
28*cf5a6c84SAndroid Build Coastguard Worker
29*cf5a6c84SAndroid Build Coastguard Worker MMDDhhmm[[CC]YY][.ss] POSIX
30*cf5a6c84SAndroid Build Coastguard Worker @UNIXTIME[.FRACTION] seconds since midnight 1970-01-01
31*cf5a6c84SAndroid Build Coastguard Worker YYYY-MM-DD [hh:mm[:ss]] ISO 8601
32*cf5a6c84SAndroid Build Coastguard Worker hh:mm[:ss] 24-hour time today
33*cf5a6c84SAndroid Build Coastguard Worker
34*cf5a6c84SAndroid Build Coastguard Worker All input formats can be followed by fractional seconds, and/or a UTC
35*cf5a6c84SAndroid Build Coastguard Worker offset such as -0800.
36*cf5a6c84SAndroid Build Coastguard Worker
37*cf5a6c84SAndroid Build Coastguard Worker All input formats can be preceded by TZ="id" to set the input time zone
38*cf5a6c84SAndroid Build Coastguard Worker separately from the output time zone. Otherwise $TZ sets both.
39*cf5a6c84SAndroid Build Coastguard Worker
40*cf5a6c84SAndroid Build Coastguard Worker +FORMAT specifies display format string using strftime(3) syntax:
41*cf5a6c84SAndroid Build Coastguard Worker
42*cf5a6c84SAndroid Build Coastguard Worker %% literal % %n newline %t tab
43*cf5a6c84SAndroid Build Coastguard Worker %S seconds (00-60) %M minute (00-59) %m month (01-12)
44*cf5a6c84SAndroid Build Coastguard Worker %H hour (0-23) %I hour (01-12) %p AM/PM
45*cf5a6c84SAndroid Build Coastguard Worker %y short year (00-99) %Y year %C century
46*cf5a6c84SAndroid Build Coastguard Worker %a short weekday name %A weekday name %u day of week (1-7, 1=mon)
47*cf5a6c84SAndroid Build Coastguard Worker %b short month name %B month name %Z timezone name
48*cf5a6c84SAndroid Build Coastguard Worker %j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)
49*cf5a6c84SAndroid Build Coastguard Worker %N nanosec (output only)
50*cf5a6c84SAndroid Build Coastguard Worker
51*cf5a6c84SAndroid Build Coastguard Worker %U Week of year (0-53 start Sunday) %W Week of year (0-53 start Monday)
52*cf5a6c84SAndroid Build Coastguard Worker %V Week of year (1-53 start Monday, week < 4 days not part of this year)
53*cf5a6c84SAndroid Build Coastguard Worker
54*cf5a6c84SAndroid Build Coastguard Worker %F "%Y-%m-%d" %R "%H:%M" %T "%H:%M:%S" %z timezone (-0800)
55*cf5a6c84SAndroid Build Coastguard Worker %D "%m/%d/%y" %r "%I:%M:%S %p" %h "%b" %:z timezone (-08:00)
56*cf5a6c84SAndroid Build Coastguard Worker %x locale date %X locale time %c locale date/time %s unix epoch time
57*cf5a6c84SAndroid Build Coastguard Worker */
58*cf5a6c84SAndroid Build Coastguard Worker
59*cf5a6c84SAndroid Build Coastguard Worker #define FOR_date
60*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
61*cf5a6c84SAndroid Build Coastguard Worker
62*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
63*cf5a6c84SAndroid Build Coastguard Worker char *s, *r, *I, *D, *d;
64*cf5a6c84SAndroid Build Coastguard Worker
65*cf5a6c84SAndroid Build Coastguard Worker unsigned nano;
66*cf5a6c84SAndroid Build Coastguard Worker )
67*cf5a6c84SAndroid Build Coastguard Worker
68*cf5a6c84SAndroid Build Coastguard Worker // Handles any leading `TZ="blah" ` in the input string.
parse_date(char * str,time_t * t)69*cf5a6c84SAndroid Build Coastguard Worker static void parse_date(char *str, time_t *t)
70*cf5a6c84SAndroid Build Coastguard Worker {
71*cf5a6c84SAndroid Build Coastguard Worker char *new_tz = NULL, *old_tz QUIET, *s = str;
72*cf5a6c84SAndroid Build Coastguard Worker
73*cf5a6c84SAndroid Build Coastguard Worker if (!strncmp(str, "TZ=\"", 4)) {
74*cf5a6c84SAndroid Build Coastguard Worker // Extract the time zone and skip any whitespace.
75*cf5a6c84SAndroid Build Coastguard Worker new_tz = str+4;
76*cf5a6c84SAndroid Build Coastguard Worker if (!(str = strchr(new_tz, '"'))) xvali_date(0, s);
77*cf5a6c84SAndroid Build Coastguard Worker *str++ = 0;
78*cf5a6c84SAndroid Build Coastguard Worker while (isspace(*str)) str++;
79*cf5a6c84SAndroid Build Coastguard Worker
80*cf5a6c84SAndroid Build Coastguard Worker // Switch $TZ.
81*cf5a6c84SAndroid Build Coastguard Worker old_tz = getenv("TZ");
82*cf5a6c84SAndroid Build Coastguard Worker setenv("TZ", new_tz, 1);
83*cf5a6c84SAndroid Build Coastguard Worker tzset();
84*cf5a6c84SAndroid Build Coastguard Worker }
85*cf5a6c84SAndroid Build Coastguard Worker time(t);
86*cf5a6c84SAndroid Build Coastguard Worker xparsedate(str, t, &TT.nano, 1);
87*cf5a6c84SAndroid Build Coastguard Worker if (new_tz) {
88*cf5a6c84SAndroid Build Coastguard Worker if (old_tz) setenv("TZ", old_tz, 1);
89*cf5a6c84SAndroid Build Coastguard Worker else unsetenv("TZ");
90*cf5a6c84SAndroid Build Coastguard Worker }
91*cf5a6c84SAndroid Build Coastguard Worker }
92*cf5a6c84SAndroid Build Coastguard Worker
93*cf5a6c84SAndroid Build Coastguard Worker // Print strftime plus %N and %:z escape(s). Note: modifies fmt in those cases.
puts_time(char * fmt,struct tm * tm)94*cf5a6c84SAndroid Build Coastguard Worker static void puts_time(char *fmt, struct tm *tm)
95*cf5a6c84SAndroid Build Coastguard Worker {
96*cf5a6c84SAndroid Build Coastguard Worker char *s, *snap, *out;
97*cf5a6c84SAndroid Build Coastguard Worker
98*cf5a6c84SAndroid Build Coastguard Worker for (s = fmt;;s++) {
99*cf5a6c84SAndroid Build Coastguard Worker long n = 0;
100*cf5a6c84SAndroid Build Coastguard Worker
101*cf5a6c84SAndroid Build Coastguard Worker // Find next %N/%:z or end of format string.
102*cf5a6c84SAndroid Build Coastguard Worker if (*(snap = s)) {
103*cf5a6c84SAndroid Build Coastguard Worker if (*s != '%') continue;
104*cf5a6c84SAndroid Build Coastguard Worker if (*++s == 'N') n = 9;
105*cf5a6c84SAndroid Build Coastguard Worker else if (isdigit(*s) && s[1] == 'N') n = *s++-'0';
106*cf5a6c84SAndroid Build Coastguard Worker else if (*s == ':' && s[1] == 'z') s++, n++;
107*cf5a6c84SAndroid Build Coastguard Worker else continue;
108*cf5a6c84SAndroid Build Coastguard Worker }
109*cf5a6c84SAndroid Build Coastguard Worker
110*cf5a6c84SAndroid Build Coastguard Worker // Only modify input string if needed (default format is constant string).
111*cf5a6c84SAndroid Build Coastguard Worker if (*s) *snap = 0;
112*cf5a6c84SAndroid Build Coastguard Worker // Do we have any regular work for strftime to do?
113*cf5a6c84SAndroid Build Coastguard Worker out = toybuf;
114*cf5a6c84SAndroid Build Coastguard Worker if (*fmt) {
115*cf5a6c84SAndroid Build Coastguard Worker if (!strftime(out, sizeof(toybuf)-12, fmt, tm))
116*cf5a6c84SAndroid Build Coastguard Worker perror_exit("bad format '%s'", fmt);
117*cf5a6c84SAndroid Build Coastguard Worker out += strlen(out);
118*cf5a6c84SAndroid Build Coastguard Worker }
119*cf5a6c84SAndroid Build Coastguard Worker // Do we have any custom formatting to append to that?
120*cf5a6c84SAndroid Build Coastguard Worker if (*s == 'N') {
121*cf5a6c84SAndroid Build Coastguard Worker sprintf(out, "%09u", TT.nano);
122*cf5a6c84SAndroid Build Coastguard Worker out[n] = 0;
123*cf5a6c84SAndroid Build Coastguard Worker } else if (*s == 'z') {
124*cf5a6c84SAndroid Build Coastguard Worker strftime(out, 10, "%z", tm);
125*cf5a6c84SAndroid Build Coastguard Worker memmove(out+4, out+3, strlen(out+3)+1);
126*cf5a6c84SAndroid Build Coastguard Worker out[3] = ':';
127*cf5a6c84SAndroid Build Coastguard Worker }
128*cf5a6c84SAndroid Build Coastguard Worker xputsn(toybuf);
129*cf5a6c84SAndroid Build Coastguard Worker if (!*s || !*(fmt = s+1)) break;
130*cf5a6c84SAndroid Build Coastguard Worker }
131*cf5a6c84SAndroid Build Coastguard Worker xputc('\n');
132*cf5a6c84SAndroid Build Coastguard Worker }
133*cf5a6c84SAndroid Build Coastguard Worker
date_main(void)134*cf5a6c84SAndroid Build Coastguard Worker void date_main(void)
135*cf5a6c84SAndroid Build Coastguard Worker {
136*cf5a6c84SAndroid Build Coastguard Worker char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y",
137*cf5a6c84SAndroid Build Coastguard Worker *tz = NULL;
138*cf5a6c84SAndroid Build Coastguard Worker time_t t;
139*cf5a6c84SAndroid Build Coastguard Worker
140*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(I)) {
141*cf5a6c84SAndroid Build Coastguard Worker char *iso_formats[] = {"%F","%FT%H%:z","%FT%R%:z","%FT%T%:z","%FT%T,%N%:z"};
142*cf5a6c84SAndroid Build Coastguard Worker int i = stridx("dhmsn", (TT.I && *TT.I) ? *TT.I : 'd');
143*cf5a6c84SAndroid Build Coastguard Worker
144*cf5a6c84SAndroid Build Coastguard Worker if (i<0) help_exit("bad -I: %s", TT.I);
145*cf5a6c84SAndroid Build Coastguard Worker format_string = xstrdup(iso_formats[i]);
146*cf5a6c84SAndroid Build Coastguard Worker }
147*cf5a6c84SAndroid Build Coastguard Worker
148*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(u)) {
149*cf5a6c84SAndroid Build Coastguard Worker tz = getenv("TZ");
150*cf5a6c84SAndroid Build Coastguard Worker setenv("TZ", "UTC", 1);
151*cf5a6c84SAndroid Build Coastguard Worker tzset();
152*cf5a6c84SAndroid Build Coastguard Worker }
153*cf5a6c84SAndroid Build Coastguard Worker
154*cf5a6c84SAndroid Build Coastguard Worker if (TT.d) {
155*cf5a6c84SAndroid Build Coastguard Worker if (TT.D) {
156*cf5a6c84SAndroid Build Coastguard Worker struct tm tm = {};
157*cf5a6c84SAndroid Build Coastguard Worker char *s = strptime(TT.d, TT.D+(*TT.D=='+'), &tm);
158*cf5a6c84SAndroid Build Coastguard Worker
159*cf5a6c84SAndroid Build Coastguard Worker t = (s && !*s) ? xvali_date(&tm, s) : xvali_date(0, TT.d);
160*cf5a6c84SAndroid Build Coastguard Worker } else parse_date(TT.d, &t);
161*cf5a6c84SAndroid Build Coastguard Worker } else {
162*cf5a6c84SAndroid Build Coastguard Worker struct timespec ts;
163*cf5a6c84SAndroid Build Coastguard Worker struct stat st;
164*cf5a6c84SAndroid Build Coastguard Worker
165*cf5a6c84SAndroid Build Coastguard Worker if (TT.r) {
166*cf5a6c84SAndroid Build Coastguard Worker xstat(TT.r, &st);
167*cf5a6c84SAndroid Build Coastguard Worker ts = st.st_mtim;
168*cf5a6c84SAndroid Build Coastguard Worker } else clock_gettime(CLOCK_REALTIME, &ts);
169*cf5a6c84SAndroid Build Coastguard Worker
170*cf5a6c84SAndroid Build Coastguard Worker t = ts.tv_sec;
171*cf5a6c84SAndroid Build Coastguard Worker TT.nano = ts.tv_nsec;
172*cf5a6c84SAndroid Build Coastguard Worker }
173*cf5a6c84SAndroid Build Coastguard Worker
174*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(s)) {
175*cf5a6c84SAndroid Build Coastguard Worker if (setdate) help_exit("can't set two dates at once");
176*cf5a6c84SAndroid Build Coastguard Worker setdate = TT.s;
177*cf5a6c84SAndroid Build Coastguard Worker }
178*cf5a6c84SAndroid Build Coastguard Worker
179*cf5a6c84SAndroid Build Coastguard Worker // Fall through to display if no arguments
180*cf5a6c84SAndroid Build Coastguard Worker if (!setdate);
181*cf5a6c84SAndroid Build Coastguard Worker // Supplying a format argument means display
182*cf5a6c84SAndroid Build Coastguard Worker else if (toys.optc && *toys.optargs[0] == '+')
183*cf5a6c84SAndroid Build Coastguard Worker format_string = toys.optargs[0]+1;
184*cf5a6c84SAndroid Build Coastguard Worker
185*cf5a6c84SAndroid Build Coastguard Worker // Set the date
186*cf5a6c84SAndroid Build Coastguard Worker else {
187*cf5a6c84SAndroid Build Coastguard Worker struct timeval tv;
188*cf5a6c84SAndroid Build Coastguard Worker
189*cf5a6c84SAndroid Build Coastguard Worker parse_date(setdate, &t);
190*cf5a6c84SAndroid Build Coastguard Worker tv.tv_sec = t;
191*cf5a6c84SAndroid Build Coastguard Worker tv.tv_usec = TT.nano/1000;
192*cf5a6c84SAndroid Build Coastguard Worker if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date");
193*cf5a6c84SAndroid Build Coastguard Worker }
194*cf5a6c84SAndroid Build Coastguard Worker
195*cf5a6c84SAndroid Build Coastguard Worker puts_time(format_string, localtime(&t));
196*cf5a6c84SAndroid Build Coastguard Worker
197*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(u)) {
198*cf5a6c84SAndroid Build Coastguard Worker if (tz) setenv("TZ", tz, 1);
199*cf5a6c84SAndroid Build Coastguard Worker else unsetenv("TZ");
200*cf5a6c84SAndroid Build Coastguard Worker tzset();
201*cf5a6c84SAndroid Build Coastguard Worker }
202*cf5a6c84SAndroid Build Coastguard Worker if (CFG_TOYBOX_FREE && FLAG(I)) free(format_string);
203*cf5a6c84SAndroid Build Coastguard Worker }
204