3
0

time.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Utility routines.
  4. *
  5. * Copyright (C) 2007 Denys Vlasenko
  6. *
  7. * Licensed under GPLv2, see file LICENSE in this source tree.
  8. */
  9. #include "libbb.h"
  10. /* Returns 0 if the time structure contains an absolute UTC time which
  11. * should not be subject to DST adjustment by the caller. */
  12. int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
  13. {
  14. char end = '\0';
  15. time_t t;
  16. #if ENABLE_DESKTOP
  17. /*
  18. * strptime is BIG: ~1k in uclibc, ~10k in glibc
  19. * We need it for 'month_name d HH:MM:SS YYYY', supported by GNU date,
  20. * but if we've linked it we might as well use it for everything.
  21. */
  22. static const char fmt_str[] ALIGN1 =
  23. "%R" "\0" /* HH:MM */
  24. "%T" "\0" /* HH:MM:SS */
  25. "%m.%d-%R" "\0" /* mm.dd-HH:MM */
  26. "%m.%d-%T" "\0" /* mm.dd-HH:MM:SS */
  27. "%Y.%m.%d-%R" "\0" /* yyyy.mm.dd-HH:MM */
  28. "%Y.%m.%d-%T" "\0" /* yyyy.mm.dd-HH:MM:SS */
  29. "%b %d %T %Y" "\0" /* month_name d HH:MM:SS YYYY */
  30. "%Y-%m-%d %R" "\0" /* yyyy-mm-dd HH:MM */
  31. "%Y-%m-%d %T" "\0" /* yyyy-mm-dd HH:MM:SS */
  32. # if ENABLE_FEATURE_TIMEZONE
  33. "%Y-%m-%d %R %z" "\0" /* yyyy-mm-dd HH:MM TZ */
  34. "%Y-%m-%d %T %z" "\0" /* yyyy-mm-dd HH:MM:SS TZ */
  35. # endif
  36. "%Y-%m-%d %H" "\0" /* yyyy-mm-dd HH */
  37. "%Y-%m-%d" "\0" /* yyyy-mm-dd */
  38. /* extra NUL */;
  39. struct tm save;
  40. const char *fmt;
  41. char *endp;
  42. save = *ptm;
  43. fmt = fmt_str;
  44. while (*fmt) {
  45. endp = strptime(date_str, fmt, ptm);
  46. if (endp && *endp == '\0') {
  47. # if ENABLE_FEATURE_TIMEZONE
  48. if (strchr(fmt, 'z')) {
  49. /* we have timezone offset: obtain Unix time_t */
  50. ptm->tm_sec -= ptm->tm_gmtoff;
  51. ptm->tm_isdst = 0;
  52. t = timegm(ptm);
  53. if (t == (time_t)-1)
  54. break;
  55. /* convert Unix time_t to struct tm in user's locale */
  56. goto localise;
  57. }
  58. # endif
  59. return 1;
  60. }
  61. *ptm = save;
  62. while (*++fmt)
  63. continue;
  64. ++fmt;
  65. }
  66. #else
  67. const char *last_colon = strrchr(date_str, ':');
  68. if (last_colon != NULL) {
  69. /* Parse input and assign appropriately to ptm */
  70. /* HH:MM */
  71. if (sscanf(date_str, "%u:%u%c",
  72. &ptm->tm_hour,
  73. &ptm->tm_min,
  74. &end) >= 2
  75. ) {
  76. /* no adjustments needed */
  77. } else
  78. /* mm.dd-HH:MM */
  79. if (sscanf(date_str, "%u.%u-%u:%u%c",
  80. &ptm->tm_mon, &ptm->tm_mday,
  81. &ptm->tm_hour, &ptm->tm_min,
  82. &end) >= 4
  83. ) {
  84. /* Adjust month from 1-12 to 0-11 */
  85. ptm->tm_mon -= 1;
  86. } else
  87. /* yyyy.mm.dd-HH:MM */
  88. if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &ptm->tm_year,
  89. &ptm->tm_mon, &ptm->tm_mday,
  90. &ptm->tm_hour, &ptm->tm_min,
  91. &end) >= 5
  92. /* yyyy-mm-dd HH:MM */
  93. || sscanf(date_str, "%u-%u-%u %u:%u%c", &ptm->tm_year,
  94. &ptm->tm_mon, &ptm->tm_mday,
  95. &ptm->tm_hour, &ptm->tm_min,
  96. &end) >= 5
  97. ) {
  98. ptm->tm_year -= 1900; /* Adjust years */
  99. ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
  100. } else
  101. {
  102. bb_error_msg_and_die(bb_msg_invalid_date, date_str);
  103. }
  104. if (end == ':') {
  105. /* xxx:SS */
  106. if (sscanf(last_colon + 1, "%u%c", &ptm->tm_sec, &end) == 1)
  107. end = '\0';
  108. /* else end != NUL and we error out */
  109. }
  110. } else
  111. if (strchr(date_str, '-')
  112. /* Why strchr('-') check?
  113. * sscanf below will trash ptm->tm_year, this breaks
  114. * if parse_str is "10101010" (iow, "MMddhhmm" form)
  115. * because we destroy year. Do these sscanf
  116. * only if we saw a dash in parse_str.
  117. */
  118. /* yyyy-mm-dd HH */
  119. && (sscanf(date_str, "%u-%u-%u %u%c", &ptm->tm_year,
  120. &ptm->tm_mon, &ptm->tm_mday,
  121. &ptm->tm_hour,
  122. &end) >= 4
  123. /* yyyy-mm-dd */
  124. || sscanf(date_str, "%u-%u-%u%c", &ptm->tm_year,
  125. &ptm->tm_mon, &ptm->tm_mday,
  126. &end) >= 3
  127. )
  128. ) {
  129. ptm->tm_year -= 1900; /* Adjust years */
  130. ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
  131. } else
  132. #endif /* ENABLE_DESKTOP */
  133. if (date_str[0] == '@') {
  134. if (sizeof(t) <= sizeof(long))
  135. t = bb_strtol(date_str + 1, NULL, 10);
  136. else /* time_t is 64 bits but longs are smaller */
  137. t = bb_strtoll(date_str + 1, NULL, 10);
  138. if (!errno) {
  139. struct tm *lt;
  140. IF_FEATURE_TIMEZONE(localise:)
  141. lt = localtime(&t);
  142. if (lt) {
  143. *ptm = *lt;
  144. return 0;
  145. }
  146. }
  147. end = '1';
  148. } else {
  149. /* Googled the following on an old date manpage:
  150. *
  151. * The canonical representation for setting the date/time is:
  152. * cc Century (either 19 or 20)
  153. * yy Year in abbreviated form (e.g. 89, 06)
  154. * mm Numeric month, a number from 1 to 12
  155. * dd Day, a number from 1 to 31
  156. * HH Hour, a number from 0 to 23
  157. * MM Minutes, a number from 0 to 59
  158. * .SS Seconds, a number from 0 to 61 (with leap seconds)
  159. * Everything but the minutes is optional
  160. *
  161. * "touch -t DATETIME" format: [[[[[YY]YY]MM]DD]hh]mm[.ss]
  162. * Some, but not all, Unix "date DATETIME" commands
  163. * move [[YY]YY] past minutes mm field (!).
  164. * Coreutils date does it, and SUS mandates it.
  165. * (date -s DATETIME does not support this format. lovely!)
  166. * In bbox, this format is special-cased in date applet
  167. * (IOW: this function assumes "touch -t" format).
  168. */
  169. unsigned cur_year = ptm->tm_year;
  170. int len = strchrnul(date_str, '.') - date_str;
  171. /* MM[.SS] */
  172. if (len == 2 && sscanf(date_str, "%2u%2u%2u%2u""%2u%c" + 12,
  173. &ptm->tm_min,
  174. &end) >= 1) {
  175. } else
  176. /* HHMM[.SS] */
  177. if (len == 4 && sscanf(date_str, "%2u%2u%2u""%2u%2u%c" + 9,
  178. &ptm->tm_hour,
  179. &ptm->tm_min,
  180. &end) >= 2) {
  181. } else
  182. /* ddHHMM[.SS] */
  183. if (len == 6 && sscanf(date_str, "%2u%2u""%2u%2u%2u%c" + 6,
  184. &ptm->tm_mday,
  185. &ptm->tm_hour,
  186. &ptm->tm_min,
  187. &end) >= 3) {
  188. } else
  189. /* mmddHHMM[.SS] */
  190. if (len == 8 && sscanf(date_str, "%2u""%2u%2u%2u%2u%c" + 3,
  191. &ptm->tm_mon,
  192. &ptm->tm_mday,
  193. &ptm->tm_hour,
  194. &ptm->tm_min,
  195. &end) >= 4) {
  196. /* Adjust month from 1-12 to 0-11 */
  197. ptm->tm_mon -= 1;
  198. } else
  199. /* yymmddHHMM[.SS] */
  200. if (len == 10 && sscanf(date_str, "%2u%2u%2u%2u%2u%c",
  201. &ptm->tm_year,
  202. &ptm->tm_mon,
  203. &ptm->tm_mday,
  204. &ptm->tm_hour,
  205. &ptm->tm_min,
  206. &end) >= 5) {
  207. /* Adjust month from 1-12 to 0-11 */
  208. ptm->tm_mon -= 1;
  209. if ((int)cur_year >= 50) { /* >= 1950 */
  210. /* Adjust year: */
  211. /* 1. Put it in the current century */
  212. ptm->tm_year += (cur_year / 100) * 100;
  213. /* 2. If too far in the past, +100 years */
  214. if (ptm->tm_year < cur_year - 50)
  215. ptm->tm_year += 100;
  216. /* 3. If too far in the future, -100 years */
  217. if (ptm->tm_year > cur_year + 50)
  218. ptm->tm_year -= 100;
  219. }
  220. } else
  221. /* ccyymmddHHMM[.SS] */
  222. if (len == 12 && sscanf(date_str, "%4u%2u%2u%2u%2u%c",
  223. &ptm->tm_year,
  224. &ptm->tm_mon,
  225. &ptm->tm_mday,
  226. &ptm->tm_hour,
  227. &ptm->tm_min,
  228. &end) >= 5) {
  229. ptm->tm_year -= 1900; /* Adjust years */
  230. ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
  231. } else {
  232. err:
  233. bb_error_msg_and_die(bb_msg_invalid_date, date_str);
  234. }
  235. ptm->tm_sec = 0; /* assume zero if [.SS] is not given */
  236. if (end == '.') {
  237. /* xxx.SS */
  238. if (sscanf(strchr(date_str, '.') + 1, "%u%c",
  239. &ptm->tm_sec, &end) == 1)
  240. end = '\0';
  241. /* else end != NUL and we error out */
  242. }
  243. /* Users were confused by "date -s 20180923"
  244. * working (not in the way they were expecting).
  245. * It was interpreted as MMDDhhmm, and not bothered by
  246. * "month #20" in the least. Prevent such cases:
  247. */
  248. if (ptm->tm_sec > 60 /* allow "23:60" leap second */
  249. || ptm->tm_min > 59
  250. || ptm->tm_hour > 23
  251. || ptm->tm_mday > 31
  252. || ptm->tm_mon > 11 /* month# is 0..11, not 1..12 */
  253. ) {
  254. goto err;
  255. }
  256. }
  257. if (end != '\0') {
  258. bb_error_msg_and_die(bb_msg_invalid_date, date_str);
  259. }
  260. return 1;
  261. }
  262. time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm)
  263. {
  264. time_t t = mktime(ptm);
  265. if (t == (time_t) -1L) {
  266. bb_error_msg_and_die(bb_msg_invalid_date, date_str);
  267. }
  268. return t;
  269. }
  270. static char* strftime_fmt(char *buf, unsigned len, time_t *tp, const char *fmt)
  271. {
  272. time_t t;
  273. if (!tp) {
  274. tp = &t;
  275. time(tp);
  276. }
  277. /* Returns pointer to NUL */
  278. return buf + strftime(buf, len, fmt, localtime(tp));
  279. }
  280. char* FAST_FUNC strftime_HHMMSS(char *buf, unsigned len, time_t *tp)
  281. {
  282. return strftime_fmt(buf, len, tp, "%H:%M:%S");
  283. }
  284. char* FAST_FUNC strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp)
  285. {
  286. return strftime_fmt(buf, len, tp, "%Y-%m-%d %H:%M:%S");
  287. }
  288. #if ENABLE_MONOTONIC_SYSCALL
  289. /* Old glibc (< 2.3.4) does not provide this constant. We use syscall
  290. * directly so this definition is safe. */
  291. #ifndef CLOCK_MONOTONIC
  292. #define CLOCK_MONOTONIC 1
  293. #endif
  294. static void get_mono(struct timespec *ts)
  295. {
  296. if (clock_gettime(CLOCK_MONOTONIC, ts))
  297. bb_simple_error_msg_and_die("clock_gettime(MONOTONIC) failed");
  298. }
  299. unsigned long long FAST_FUNC monotonic_ns(void)
  300. {
  301. struct timespec ts;
  302. get_mono(&ts);
  303. return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
  304. }
  305. unsigned long long FAST_FUNC monotonic_us(void)
  306. {
  307. struct timespec ts;
  308. get_mono(&ts);
  309. return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000;
  310. }
  311. unsigned long long FAST_FUNC monotonic_ms(void)
  312. {
  313. struct timespec ts;
  314. get_mono(&ts);
  315. return ts.tv_sec * 1000ULL + ts.tv_nsec/1000000;
  316. }
  317. unsigned FAST_FUNC monotonic_sec(void)
  318. {
  319. struct timespec ts;
  320. get_mono(&ts);
  321. return ts.tv_sec;
  322. }
  323. #else
  324. unsigned long long FAST_FUNC monotonic_ns(void)
  325. {
  326. struct timeval tv;
  327. xgettimeofday(&tv);
  328. return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
  329. }
  330. unsigned long long FAST_FUNC monotonic_us(void)
  331. {
  332. struct timeval tv;
  333. xgettimeofday(&tv);
  334. return tv.tv_sec * 1000000ULL + tv.tv_usec;
  335. }
  336. unsigned long long FAST_FUNC monotonic_ms(void)
  337. {
  338. struct timeval tv;
  339. xgettimeofday(&tv);
  340. return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
  341. }
  342. unsigned FAST_FUNC monotonic_sec(void)
  343. {
  344. return time(NULL);
  345. }
  346. #endif