• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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