toybox/toys/posix/date.c
<<
>>
Prefs
   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