date.c 5.6 KB

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