]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/timeutils.c
lib/colors: fix test compilation
[thirdparty/util-linux.git] / lib / timeutils.c
1 /***
2 First set of functions in this file are part of systemd, and were
3 copied to util-linux at August 2013.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with util-linux; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <assert.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <sys/time.h>
27
28 #include "c.h"
29 #include "nls.h"
30 #include "strutils.h"
31 #include "timeutils.h"
32
33 #define WHITESPACE " \t\n\r"
34
35 #define streq(a,b) (strcmp((a),(b)) == 0)
36
37 static int parse_sec(const char *t, usec_t *usec)
38 {
39 static const struct {
40 const char *suffix;
41 usec_t usec;
42 } table[] = {
43 { "seconds", USEC_PER_SEC },
44 { "second", USEC_PER_SEC },
45 { "sec", USEC_PER_SEC },
46 { "s", USEC_PER_SEC },
47 { "minutes", USEC_PER_MINUTE },
48 { "minute", USEC_PER_MINUTE },
49 { "min", USEC_PER_MINUTE },
50 { "months", USEC_PER_MONTH },
51 { "month", USEC_PER_MONTH },
52 { "msec", USEC_PER_MSEC },
53 { "ms", USEC_PER_MSEC },
54 { "m", USEC_PER_MINUTE },
55 { "hours", USEC_PER_HOUR },
56 { "hour", USEC_PER_HOUR },
57 { "hr", USEC_PER_HOUR },
58 { "h", USEC_PER_HOUR },
59 { "days", USEC_PER_DAY },
60 { "day", USEC_PER_DAY },
61 { "d", USEC_PER_DAY },
62 { "weeks", USEC_PER_WEEK },
63 { "week", USEC_PER_WEEK },
64 { "w", USEC_PER_WEEK },
65 { "years", USEC_PER_YEAR },
66 { "year", USEC_PER_YEAR },
67 { "y", USEC_PER_YEAR },
68 { "usec", 1ULL },
69 { "us", 1ULL },
70 { "", USEC_PER_SEC }, /* default is sec */
71 };
72
73 const char *p;
74 usec_t r = 0;
75 int something = FALSE;
76
77 assert(t);
78 assert(usec);
79
80 p = t;
81 for (;;) {
82 long long l, z = 0;
83 char *e;
84 unsigned i, n = 0;
85
86 p += strspn(p, WHITESPACE);
87
88 if (*p == 0) {
89 if (!something)
90 return -EINVAL;
91
92 break;
93 }
94
95 errno = 0;
96 l = strtoll(p, &e, 10);
97
98 if (errno > 0)
99 return -errno;
100
101 if (l < 0)
102 return -ERANGE;
103
104 if (*e == '.') {
105 char *b = e + 1;
106
107 errno = 0;
108 z = strtoll(b, &e, 10);
109 if (errno > 0)
110 return -errno;
111
112 if (z < 0)
113 return -ERANGE;
114
115 if (e == b)
116 return -EINVAL;
117
118 n = e - b;
119
120 } else if (e == p)
121 return -EINVAL;
122
123 e += strspn(e, WHITESPACE);
124
125 for (i = 0; i < ARRAY_SIZE(table); i++)
126 if (startswith(e, table[i].suffix)) {
127 usec_t k = (usec_t) z * table[i].usec;
128
129 for (; n > 0; n--)
130 k /= 10;
131
132 r += (usec_t) l *table[i].usec + k;
133 p = e + strlen(table[i].suffix);
134
135 something = TRUE;
136 break;
137 }
138
139 if (i >= ARRAY_SIZE(table))
140 return -EINVAL;
141
142 }
143
144 *usec = r;
145
146 return 0;
147 }
148
149 int parse_timestamp(const char *t, usec_t *usec)
150 {
151 static const struct {
152 const char *name;
153 const int nr;
154 } day_nr[] = {
155 { "Sunday", 0 },
156 { "Sun", 0 },
157 { "Monday", 1 },
158 { "Mon", 1 },
159 { "Tuesday", 2 },
160 { "Tue", 2 },
161 { "Wednesday", 3 },
162 { "Wed", 3 },
163 { "Thursday", 4 },
164 { "Thu", 4 },
165 { "Friday", 5 },
166 { "Fri", 5 },
167 { "Saturday", 6 },
168 { "Sat", 6 },
169 };
170
171 const char *k;
172 struct tm tm, copy;
173 time_t x;
174 usec_t plus = 0, minus = 0, ret;
175 int r, weekday = -1;
176 unsigned i;
177
178 /*
179 * Allowed syntaxes:
180 *
181 * 2012-09-22 16:34:22
182 * 2012-09-22 16:34 (seconds will be set to 0)
183 * 2012-09-22 (time will be set to 00:00:00)
184 * 16:34:22 (date will be set to today)
185 * 16:34 (date will be set to today, seconds to 0)
186 * now
187 * yesterday (time is set to 00:00:00)
188 * today (time is set to 00:00:00)
189 * tomorrow (time is set to 00:00:00)
190 * +5min
191 * -5days
192 *
193 */
194
195 assert(t);
196 assert(usec);
197
198 x = time(NULL);
199 localtime_r(&x, &tm);
200 tm.tm_isdst = -1;
201
202 if (streq(t, "now"))
203 goto finish;
204
205 else if (streq(t, "today")) {
206 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
207 goto finish;
208
209 } else if (streq(t, "yesterday")) {
210 tm.tm_mday--;
211 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
212 goto finish;
213
214 } else if (streq(t, "tomorrow")) {
215 tm.tm_mday++;
216 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
217 goto finish;
218
219 } else if (t[0] == '+') {
220
221 r = parse_sec(t + 1, &plus);
222 if (r < 0)
223 return r;
224
225 goto finish;
226 } else if (t[0] == '-') {
227
228 r = parse_sec(t + 1, &minus);
229 if (r < 0)
230 return r;
231
232 goto finish;
233
234 } else if (endswith(t, " ago")) {
235 char *z;
236
237 z = strndup(t, strlen(t) - 4);
238 if (!z)
239 return -ENOMEM;
240
241 r = parse_sec(z, &minus);
242 free(z);
243 if (r < 0)
244 return r;
245
246 goto finish;
247 }
248
249 for (i = 0; i < ARRAY_SIZE(day_nr); i++) {
250 size_t skip;
251
252 if (!startswith_no_case(t, day_nr[i].name))
253 continue;
254
255 skip = strlen(day_nr[i].name);
256 if (t[skip] != ' ')
257 continue;
258
259 weekday = day_nr[i].nr;
260 t += skip + 1;
261 break;
262 }
263
264 copy = tm;
265 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
266 if (k && *k == 0)
267 goto finish;
268
269 tm = copy;
270 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
271 if (k && *k == 0)
272 goto finish;
273
274 tm = copy;
275 k = strptime(t, "%y-%m-%d %H:%M", &tm);
276 if (k && *k == 0) {
277 tm.tm_sec = 0;
278 goto finish;
279 }
280
281 tm = copy;
282 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
283 if (k && *k == 0) {
284 tm.tm_sec = 0;
285 goto finish;
286 }
287
288 tm = copy;
289 k = strptime(t, "%y-%m-%d", &tm);
290 if (k && *k == 0) {
291 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
292 goto finish;
293 }
294
295 tm = copy;
296 k = strptime(t, "%Y-%m-%d", &tm);
297 if (k && *k == 0) {
298 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
299 goto finish;
300 }
301
302 tm = copy;
303 k = strptime(t, "%H:%M:%S", &tm);
304 if (k && *k == 0)
305 goto finish;
306
307 tm = copy;
308 k = strptime(t, "%H:%M", &tm);
309 if (k && *k == 0) {
310 tm.tm_sec = 0;
311 goto finish;
312 }
313
314 tm = copy;
315 k = strptime(t, "%Y%m%d%H%M%S", &tm);
316 if (k && *k == 0) {
317 tm.tm_sec = 0;
318 goto finish;
319 }
320
321 return -EINVAL;
322
323 finish:
324 x = mktime(&tm);
325 if (x == (time_t)-1)
326 return -EINVAL;
327
328 if (weekday >= 0 && tm.tm_wday != weekday)
329 return -EINVAL;
330
331 ret = (usec_t) x *USEC_PER_SEC;
332
333 ret += plus;
334 if (ret > minus)
335 ret -= minus;
336 else
337 ret = 0;
338
339 *usec = ret;
340
341 return 0;
342 }
343
344 static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz)
345 {
346 char *p = buf;
347 int len;
348
349 if (flags & ISO_8601_DATE) {
350 len = snprintf(p, bufsz, "%4d-%.2d-%.2d", tm->tm_year + 1900,
351 tm->tm_mon + 1, tm->tm_mday);
352 if (len < 0 || (size_t) len > bufsz)
353 return -1;
354 bufsz -= len;
355 p += len;
356 }
357
358 if ((flags & ISO_8601_DATE) && (flags & ISO_8601_TIME)) {
359 if (bufsz < 1)
360 return -1;
361 *p++ = (flags & ISO_8601_SPACE) ? ' ' : 'T';
362 bufsz--;
363 }
364
365 if (flags & ISO_8601_TIME) {
366 len = snprintf(p, bufsz, "%02d:%02d:%02d", tm->tm_hour,
367 tm->tm_min, tm->tm_sec);
368 if (len < 0 || (size_t) len > bufsz)
369 return -1;
370 bufsz -= len;
371 p += len;
372 }
373
374 if (flags & ISO_8601_DOTUSEC) {
375 len = snprintf(p, bufsz, ".%06ld", (long) usec);
376 if (len < 0 || (size_t) len > bufsz)
377 return -1;
378 bufsz -= len;
379 p += len;
380
381 } else if (flags & ISO_8601_COMMAUSEC) {
382 len = snprintf(p, bufsz, ",%06ld", (long) usec);
383 if (len < 0 || (size_t) len > bufsz)
384 return -1;
385 bufsz -= len;
386 p += len;
387 }
388
389 if (flags & ISO_8601_TIMEZONE && strftime(p, bufsz, "%z", tm) <= 0)
390 return -1;
391
392 return 0;
393 }
394
395 /* timeval to ISO 8601 */
396 int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz)
397 {
398 struct tm tm;
399
400 if (flags & ISO_8601_GMTIME)
401 tm = *gmtime(&tv->tv_sec);
402 else
403 tm = *localtime(&tv->tv_sec);
404 return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz);
405 }
406
407 /* struct tm to ISO 8601 */
408 int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz)
409 {
410 return format_iso_time(tm, 0, flags, buf, bufsz);
411 }
412
413 /* time_t to ISO 8601 */
414 int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz)
415 {
416 struct tm tm;
417
418 if (flags & ISO_8601_GMTIME)
419 tm = *gmtime(t);
420 else
421 tm = *localtime(t);
422 return format_iso_time(&tm, 0, flags, buf, bufsz);
423 }
424
425 /* relative time functions */
426 int time_is_today(const time_t *t, struct timeval *now)
427 {
428 if (now->tv_sec == 0)
429 gettimeofday(now, NULL);
430 return *t / (3600 * 24) == now->tv_sec / (3600 * 24);
431 }
432
433 int time_is_thisyear(const time_t *t, struct timeval *now)
434 {
435 if (now->tv_sec == 0)
436 gettimeofday(now, NULL);
437 return *t / (3600 * 24 * 365) == now->tv_sec / (3600 * 24 * 365);
438 }
439
440 int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, size_t bufsz)
441 {
442 struct tm tm;
443 int rc = 0;
444
445 localtime_r(t, &tm);
446
447 if (time_is_today(t, now)) {
448 rc = snprintf(buf, bufsz, "%02d:%02d", tm.tm_hour, tm.tm_min);
449 if (rc < 0 || (size_t) rc > bufsz)
450 return -1;
451 rc = 1;
452
453 } else if (time_is_thisyear(t, now)) {
454 if (flags & UL_SHORTTIME_THISYEAR_HHMM)
455 rc = strftime(buf, bufsz, "%b%d/%H:%M", &tm);
456 else
457 rc = strftime(buf, bufsz, "%b%d", &tm);
458 } else
459 rc = strftime(buf, bufsz, "%Y-%b%d", &tm);
460
461 return rc <= 0 ? -1 : 0;
462 }
463
464 #ifndef HAVE_TIMEGM
465 time_t timegm(struct tm *tm)
466 {
467 const char *zone = getenv("TZ");
468 time_t ret;
469
470 setenv("TZ", "", 1);
471 tzset();
472 ret = mktime(tm);
473 if (zone)
474 setenv("TZ", zone, 1);
475 else
476 unsetenv("TZ");
477 tzset();
478 return ret;
479 }
480 #endif /* HAVE_TIMEGM */
481
482 #ifdef TEST_PROGRAM_TIMEUTILS
483
484 int main(int argc, char *argv[])
485 {
486 struct timeval tv = { 0 };
487 char buf[ISO_8601_BUFSIZ];
488
489 if (argc < 2) {
490 fprintf(stderr, "usage: %s <time> [<usec>]\n", argv[0]);
491 exit(EXIT_FAILURE);
492 }
493
494 tv.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>");
495 if (argc == 3)
496 tv.tv_usec = strtos64_or_err(argv[2], "failed to parse <usec>");
497
498 strtimeval_iso(&tv, ISO_8601_DATE, buf, sizeof(buf));
499 printf("Date: '%s'\n", buf);
500
501 strtimeval_iso(&tv, ISO_8601_TIME, buf, sizeof(buf));
502 printf("Time: '%s'\n", buf);
503
504 strtimeval_iso(&tv, ISO_8601_DATE | ISO_8601_TIME | ISO_8601_COMMAUSEC,
505 buf, sizeof(buf));
506 printf("Full: '%s'\n", buf);
507
508 strtimeval_iso(&tv, ISO_8601_DATE | ISO_8601_TIME | ISO_8601_DOTUSEC |
509 ISO_8601_TIMEZONE | ISO_8601_SPACE,
510 buf, sizeof(buf));
511 printf("Zone: '%s'\n", buf);
512
513 return EXIT_SUCCESS;
514 }
515
516 #endif /* TEST_PROGRAM_TIMEUTILS */