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 10USE_DATE(NEWTOY(date, "d:D:r:u[!dr]", TOYFLAG_BIN)) 11 12config 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 56GLOBALS( 57 char *r, *D, *d; 58 59 unsigned nano; 60) 61 62// Handles any leading `TZ="blah" ` in the input string. 63static void parse_date(char *str, time_t *t) 64{ 65 char *new_tz = NULL, *old_tz, *s = str; 66 67 if (!strncmp(str, "TZ=\"", 4)) { 68 // Extract the time zone and skip any whitespace. 69 new_tz = str+4; 70 if (!(str = strchr(new_tz, '"'))) xvali_date(0, s); 71 *str++ = 0; 72 while (isspace(*str)) str++; 73 74 // Switch $TZ. 75 old_tz = getenv("TZ"); 76 setenv("TZ", new_tz, 1); 77 tzset(); 78 } 79 time(t); 80 xparsedate(str, t, &TT.nano, 1); 81 if (new_tz) { 82 if (old_tz) setenv("TZ", old_tz, 1); 83 else unsetenv("TZ"); 84 } 85} 86 87// Print strftime plus %N escape(s). note: modifies fmt for %N 88static void puts_time(char *fmt, struct tm *tm) 89{ 90 char *s, *snap; 91 long width = width; 92 93 for (s = fmt;;s++) { 94 95 // Find next %N or end 96 if (*(snap = s) == '%') { 97 width = isdigit(*++s) ? *(s++)-'0' : 9; 98 if (*s && *s != 'N') continue; 99 } else if (*s) continue; 100 101 // Don't modify input string if no %N (default format is constant string). 102 if (*s) *snap = 0; 103 if (!strftime(toybuf, sizeof(toybuf)-10, fmt, tm)) 104 perror_exit("bad format '%s'", fmt); 105 if (*s) { 106 snap = toybuf+strlen(toybuf); 107 sprintf(snap, "%09u", TT.nano); 108 snap[width] = 0; 109 } 110 fputs(toybuf, stdout); 111 if (!*s || !*(fmt = s+1)) break; 112 } 113 xputc('\n'); 114} 115 116void date_main(void) 117{ 118 char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y", 119 *tz = NULL; 120 time_t t; 121 122 if (FLAG(u)) { 123 tz = getenv("TZ"); 124 setenv("TZ", "UTC", 1); 125 tzset(); 126 } 127 128 if (TT.d) { 129 if (TT.D) { 130 struct tm tm = {}; 131 char *s = strptime(TT.d, TT.D+(*TT.D=='+'), &tm); 132 133 t = (s && *s) ? xvali_date(&tm, s) : xvali_date(0, TT.d); 134 } else parse_date(TT.d, &t); 135 } else { 136 struct timespec ts; 137 struct stat st; 138 139 if (TT.r) { 140 xstat(TT.r, &st); 141 ts = st.st_mtim; 142 } else clock_gettime(CLOCK_REALTIME, &ts); 143 144 t = ts.tv_sec; 145 TT.nano = ts.tv_nsec; 146 } 147 148 // Fall through if no arguments 149 if (!setdate); 150 // Display the date? 151 else if (*setdate == '+') { 152 format_string = toys.optargs[0]+1; 153 setdate = toys.optargs[1]; 154 155 // Set the date 156 } else if (setdate) { 157 struct timeval tv; 158 159 parse_date(setdate, &t); 160 tv.tv_sec = t; 161 tv.tv_usec = TT.nano/1000; 162 if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date"); 163 } 164 165 puts_time(format_string, localtime(&t)); 166 167 if (FLAG(u)) { 168 if (tz) setenv("TZ", tz, 1); 169 else unsetenv("TZ"); 170 tzset(); 171 } 172 173 return; 174} 175