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