Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 1 | /* date.c - set/get the date |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 2 | * |
| 3 | * Copyright 2012 Andre Renaud <andre@bluewatersys.com> |
| 4 | * |
Rob Landley | f91b7c8 | 2012-08-25 18:08:51 -0500 | [diff] [blame] | 5 | * See http://opengroup.org/onlinepubs/9699919799/utilities/date.html |
Rob Landley | 669f332 | 2014-04-10 19:40:14 -0500 | [diff] [blame] | 6 | * |
| 7 | * Note: setting a 2 year date is 50 years back/forward from today, |
| 8 | * not posix's hardwired magic dates. |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 9 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 10 | USE_DATE(NEWTOY(date, "d:s:r:u", TOYFLAG_BIN)) |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 11 | |
| 12 | config DATE |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 13 | bool "date" |
| 14 | default y |
| 15 | help |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 16 | usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-s SET_FORMAT] [SET] |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 17 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 18 | Set/get the current date/time. With no SET shows the current date. |
Rob Landley | 669f332 | 2014-04-10 19:40:14 -0500 | [diff] [blame] | 19 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 20 | Default SET format is "MMDDhhmm[[CC]YY][.ss]", that's (2 digits each) |
| 21 | month, day, hour (0-23), and minute. Optionally century, year, and second. |
Rob Landley | 669f332 | 2014-04-10 19:40:14 -0500 | [diff] [blame] | 22 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 23 | -d Show DATE instead of current time (convert date format) |
| 24 | -r Use modification time of FILE instead of current date |
| 25 | -s +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss]) |
| 26 | -u Use UTC instead of current timezone |
| 27 | |
| 28 | +FORMAT specifies display format string using these escapes: |
| 29 | |
| 30 | %% literal % %n newline %t tab |
| 31 | %S seconds (00-60) %M minute (00-59) %m month (01-12) |
| 32 | %H hour (0-23) %I hour (01-12) %p AM/PM |
| 33 | %y short year (00-99) %Y year %C century |
| 34 | %a short weekday name %A weekday name %u day of week (1-7, 1=mon) |
| 35 | %b short month name %B month name %Z timezone name |
| 36 | %j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31) |
| 37 | |
| 38 | %U Week of year (0-53 start sunday) %W Week of year (0-53 start monday) |
| 39 | %V Week of year (1-53 start monday, week < 4 days not part of this year) |
| 40 | |
| 41 | %D = "%m/%d/%y" %r = "%I : %M : %S %p" %T = "%H:%M:%S" %h = "%b" |
| 42 | %x locale date %X locale time %c locale date/time |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 43 | */ |
| 44 | |
Rob Landley | c0e56ed | 2012-10-08 00:02:30 -0500 | [diff] [blame] | 45 | #define FOR_date |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 46 | #include "toys.h" |
| 47 | |
Rob Landley | c0e56ed | 2012-10-08 00:02:30 -0500 | [diff] [blame] | 48 | GLOBALS( |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 49 | char *file; |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 50 | char *setfmt; |
| 51 | char *showdate; |
Rob Landley | 6d87819 | 2012-07-21 18:37:26 -0500 | [diff] [blame] | 52 | ) |
| 53 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 54 | // Handle default posix date format: mmddhhmm[[cc]yy] |
| 55 | // returns 0 success, nonzero for error |
| 56 | int parse_posixdate(char *str, struct tm *tm) |
| 57 | { |
| 58 | int len; |
| 59 | |
| 60 | len = 0; |
| 61 | sscanf(str, "%2u%2u%2u%2u%n", &tm->tm_mon, &tm->tm_mday, &tm->tm_hour, |
| 62 | &tm->tm_min, &len); |
| 63 | if (len != 8) return 1; |
| 64 | str += len; |
| 65 | tm->tm_mon--; |
| 66 | |
| 67 | // If year specified, overwrite one we fetched earlier |
| 68 | if (*str && *str != '.') { |
| 69 | unsigned year, r1 = tm->tm_year % 100, r2 = (tm->tm_year + 50) % 100, |
| 70 | century = tm->tm_year - r1; |
| 71 | |
| 72 | len = 0; |
| 73 | sscanf(str, "%u%n", &year, &len); |
| 74 | if (len == 4) year -= 1900; |
| 75 | else if (len != 2) return 1; |
| 76 | str += len; |
| 77 | |
| 78 | // 2 digit years, next 50 years are "future", last 50 years are "past". |
| 79 | // A "future" date in past is a century ahead. |
| 80 | // A non-future date in the future is a century behind. |
| 81 | if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) { |
| 82 | if (year < r1) year += 100; |
| 83 | } else if (year > r1) year -= 100; |
| 84 | tm->tm_year = year + century; |
| 85 | } |
| 86 | if (*str == '.') { |
| 87 | len = 0; |
| 88 | sscanf(str, ".%u%n", &tm->tm_sec, &len); |
| 89 | str += len; |
| 90 | } |
| 91 | |
| 92 | return *str; |
| 93 | } |
| 94 | |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 95 | void date_main(void) |
| 96 | { |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 97 | char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y", |
Rob Landley | 7dfee8e | 2014-05-24 12:51:53 -0500 | [diff] [blame^] | 98 | *tz = 0; |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 99 | time_t now = time(NULL); |
| 100 | struct tm tm; |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 101 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 102 | // We can't just pass a timezone to mktime because posix. |
| 103 | if (toys.optflags & FLAG_u) { |
Rob Landley | 7dfee8e | 2014-05-24 12:51:53 -0500 | [diff] [blame^] | 104 | if (CFG_TOYBOX_FREE) tz = getenv("TZ"); |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 105 | setenv("TZ", "UTC", 1); |
| 106 | tzset(); |
| 107 | } |
| 108 | |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 109 | if (TT.file) { |
| 110 | struct stat st; |
Rob Landley | 6d87819 | 2012-07-21 18:37:26 -0500 | [diff] [blame] | 111 | |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 112 | xstat(TT.file, &st); |
| 113 | now = st.st_mtim.tv_sec; |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 114 | } else if (TT.showdate) { |
| 115 | setdate = TT.showdate; |
| 116 | if (TT.setfmt) { |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 117 | char *s = strptime(TT.showdate, TT.setfmt+(*TT.setfmt=='+'), &tm); |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 118 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 119 | if (!s || *s) goto bad_date; |
| 120 | } else if (parse_posixdate(TT.showdate, &tm)) goto bad_date; |
| 121 | } else localtime_r(&now, &tm); |
| 122 | |
| 123 | setdate = *toys.optargs; |
| 124 | // Fall through if no arguments |
| 125 | if (!setdate); |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 126 | // Display the date? |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 127 | else if (*setdate == '+') { |
| 128 | format_string = toys.optargs[0]+1; |
| 129 | setdate = toys.optargs[1]; |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 130 | |
| 131 | // Set the date |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 132 | } else if (setdate) { |
Rob Landley | 6c64f5f | 2014-04-16 08:54:19 -0500 | [diff] [blame] | 133 | struct timeval tv; |
Rob Landley | 6c64f5f | 2014-04-16 08:54:19 -0500 | [diff] [blame] | 134 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 135 | if (parse_posixdate(setdate, &tm)) goto bad_date; |
Rob Landley | 6d87819 | 2012-07-21 18:37:26 -0500 | [diff] [blame] | 136 | |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 137 | if (toys.optflags & FLAG_u) { |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 138 | char *tz = CFG_TOYBOX_FREE ? getenv("TZ") : 0; |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 139 | |
| 140 | // We can't just pass a timezone to mktime because posix. |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 141 | setenv("TZ", "UTC", 1); |
| 142 | tzset(); |
| 143 | tv.tv_sec = mktime(&tm); |
| 144 | if (CFG_TOYBOX_FREE) { |
| 145 | if (tz) setenv("TZ", tz, 1); |
| 146 | else unsetenv("TZ"); |
| 147 | tzset(); |
| 148 | } |
| 149 | } else tv.tv_sec = mktime(&tm); |
Rob Landley | 669f332 | 2014-04-10 19:40:14 -0500 | [diff] [blame] | 150 | if (tv.tv_sec == (time_t)-1) goto bad_date; |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 151 | |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 152 | tv.tv_usec = 0; |
Rob Landley | 7aa651a | 2012-11-13 17:14:08 -0600 | [diff] [blame] | 153 | if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date"); |
| 154 | } |
Rob Landley | 669f332 | 2014-04-10 19:40:14 -0500 | [diff] [blame] | 155 | |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 156 | if (toys.optflags & FLAG_u) { |
| 157 | if (tz) setenv("TZ", tz, 1); |
| 158 | else unsetenv("TZ"); |
| 159 | tzset(); |
| 160 | } |
| 161 | |
| 162 | if (!strftime(toybuf, sizeof(toybuf), format_string, &tm)) |
| 163 | perror_exit("bad format '%s'", format_string); |
| 164 | puts(toybuf); |
| 165 | |
Rob Landley | 669f332 | 2014-04-10 19:40:14 -0500 | [diff] [blame] | 166 | return; |
| 167 | |
| 168 | bad_date: |
Rob Landley | 0369ba5 | 2014-05-22 21:33:10 -0500 | [diff] [blame] | 169 | error_exit("bad date '%s'", setdate); |
Rob Landley | 26d35be | 2012-06-18 23:22:08 -0500 | [diff] [blame] | 170 | } |