]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/timeutils.c
fdisk: cleanup wipe warning
[thirdparty/util-linux.git] / lib / timeutils.c
CommitLineData
2659a49e
SK
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>
b72a75e9 23#include <stdlib.h>
2659a49e
SK
24#include <string.h>
25#include <time.h>
33c7ffa3 26#include <sys/time.h>
2659a49e
SK
27
28#include "c.h"
929f939e 29#include "nls.h"
60f25dea
KZ
30#include "strutils.h"
31#include "timeutils.h"
2659a49e
SK
32
33#define WHITESPACE " \t\n\r"
34
35#define streq(a,b) (strcmp((a),(b)) == 0)
36
2659a49e
SK
37static int parse_sec(const char *t, usec_t *usec)
38{
9d826076 39 static const struct {
2659a49e
SK
40 const char *suffix;
41 usec_t usec;
9d826076 42 } table[] = {
2659a49e
SK
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 */
9d826076 71 };
2659a49e
SK
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
149int parse_timestamp(const char *t, usec_t *usec)
150{
9d826076 151 static const struct {
2659a49e
SK
152 const char *name;
153 const int nr;
9d826076 154 } day_nr[] = {
2659a49e
SK
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 },
9d826076 169 };
2659a49e
SK
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);
06264d63 242 free(z);
2659a49e
SK
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
75ab9bf1
SK
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
2659a49e
SK
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}
3c201431 343
9fd0a7a9
WP
344/* Returns the difference in seconds between its argument and GMT. If if TP is
345 * invalid or no DST information is available default to UTC, that is, zero.
346 * tzset is called so, for example, 'TZ="UTC" hwclock' will work as expected.
347 * Derived from glibc/time/strftime_l.c
348 */
349int get_gmtoff(const struct tm *tp)
350{
351 if (tp->tm_isdst < 0)
352 return 0;
353
354#if HAVE_TM_GMTOFF
355 return tp->tm_gmtoff;
356#else
357 struct tm tm;
358 struct tm gtm;
359 struct tm ltm = *tp;
360 time_t lt;
361
362 tzset();
363 lt = mktime(&ltm);
364 /* Check if mktime returning -1 is an error or a valid time_t */
365 if (lt == (time_t) -1) {
366 if (! localtime_r(&lt, &tm)
367 || ((ltm.tm_sec ^ tm.tm_sec)
368 | (ltm.tm_min ^ tm.tm_min)
369 | (ltm.tm_hour ^ tm.tm_hour)
370 | (ltm.tm_mday ^ tm.tm_mday)
371 | (ltm.tm_mon ^ tm.tm_mon)
372 | (ltm.tm_year ^ tm.tm_year)))
373 return 0;
374 }
375
376 if (! gmtime_r(&lt, &gtm))
377 return 0;
378
379 /* Calculate the GMT offset, that is, the difference between the
380 * TP argument (ltm) and GMT (gtm).
381 *
382 * Compute intervening leap days correctly even if year is negative.
383 * Take care to avoid int overflow in leap day calculations, but it's OK
384 * to assume that A and B are close to each other.
385 */
386 int a4 = (ltm.tm_year >> 2) + (1900 >> 2) - ! (ltm.tm_year & 3);
387 int b4 = (gtm.tm_year >> 2) + (1900 >> 2) - ! (gtm.tm_year & 3);
388 int a100 = a4 / 25 - (a4 % 25 < 0);
389 int b100 = b4 / 25 - (b4 % 25 < 0);
390 int a400 = a100 >> 2;
391 int b400 = b100 >> 2;
392 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
393
394 int years = ltm.tm_year - gtm.tm_year;
395 int days = (365 * years + intervening_leap_days
396 + (ltm.tm_yday - gtm.tm_yday));
397
398 return (60 * (60 * (24 * days + (ltm.tm_hour - gtm.tm_hour))
399 + (ltm.tm_min - gtm.tm_min)) + (ltm.tm_sec - gtm.tm_sec));
400#endif
401}
402
33c7ffa3 403static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz)
3c201431 404{
33c7ffa3
KZ
405 char *p = buf;
406 int len;
3c201431 407
4111bb3a 408 if (flags & ISO_DATE) {
2d9a0b0a
WP
409 len = snprintf(p, bufsz, "%4ld-%.2d-%.2d",
410 tm->tm_year + (long) 1900,
411 tm->tm_mon + 1, tm->tm_mday);
33c7ffa3 412 if (len < 0 || (size_t) len > bufsz)
6cdc7b9c 413 goto err;
33c7ffa3
KZ
414 bufsz -= len;
415 p += len;
416 }
417
4111bb3a 418 if ((flags & ISO_DATE) && (flags & ISO_TIME)) {
33c7ffa3 419 if (bufsz < 1)
6cdc7b9c 420 goto err;
4111bb3a 421 *p++ = (flags & ISO_T) ? 'T' : ' ';
33c7ffa3
KZ
422 bufsz--;
423 }
3c201431 424
4111bb3a 425 if (flags & ISO_TIME) {
33c7ffa3 426 len = snprintf(p, bufsz, "%02d:%02d:%02d", tm->tm_hour,
2d9a0b0a 427 tm->tm_min, tm->tm_sec);
33c7ffa3 428 if (len < 0 || (size_t) len > bufsz)
6cdc7b9c 429 goto err;
33c7ffa3
KZ
430 bufsz -= len;
431 p += len;
432 }
3c201431 433
4111bb3a 434 if (flags & ISO_DOTUSEC) {
33c7ffa3
KZ
435 len = snprintf(p, bufsz, ".%06ld", (long) usec);
436 if (len < 0 || (size_t) len > bufsz)
6cdc7b9c 437 goto err;
33c7ffa3
KZ
438 bufsz -= len;
439 p += len;
440
4111bb3a 441 } else if (flags & ISO_COMMAUSEC) {
33c7ffa3
KZ
442 len = snprintf(p, bufsz, ",%06ld", (long) usec);
443 if (len < 0 || (size_t) len > bufsz)
6cdc7b9c 444 goto err;
33c7ffa3
KZ
445 bufsz -= len;
446 p += len;
447 }
3c201431 448
4111bb3a 449 if (flags & ISO_TIMEZONE) {
9fd0a7a9
WP
450 int tmin = get_gmtoff(tm) / 60;
451 int zhour = tmin / 60;
452 int zmin = abs(tmin % 60);
453 len = snprintf(p, bufsz, "%+03d:%02d", zhour,zmin);
454 if (len < 0 || (size_t) len > bufsz)
6cdc7b9c 455 goto err;
9fd0a7a9 456 }
33c7ffa3 457 return 0;
6cdc7b9c
WP
458 err:
459 warnx(_("format_iso_time: buffer overflow."));
460 return -1;
3c201431
KZ
461}
462
33c7ffa3
KZ
463/* timeval to ISO 8601 */
464int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz)
3c201431 465{
c1616946 466 struct tm tm;
ee475ab2 467 struct tm *rc;
c1616946 468
4111bb3a 469 if (flags & ISO_GMTIME)
ee475ab2 470 rc = gmtime_r(&tv->tv_sec, &tm);
c1616946 471 else
ee475ab2
WP
472 rc = localtime_r(&tv->tv_sec, &tm);
473
474 if (rc)
475 return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz);
476
477 warnx(_("time %ld is out of range."), tv->tv_sec);
478 return -1;
3c201431
KZ
479}
480
33c7ffa3
KZ
481/* struct tm to ISO 8601 */
482int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz)
3c201431 483{
33c7ffa3 484 return format_iso_time(tm, 0, flags, buf, bufsz);
3c201431
KZ
485}
486
33c7ffa3
KZ
487/* time_t to ISO 8601 */
488int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz)
3c201431 489{
c1616946 490 struct tm tm;
ee475ab2 491 struct tm *rc;
c1616946 492
4111bb3a 493 if (flags & ISO_GMTIME)
ee475ab2 494 rc = gmtime_r(t, &tm);
c1616946 495 else
ee475ab2
WP
496 rc = localtime_r(t, &tm);
497
498 if (rc)
499 return format_iso_time(&tm, 0, flags, buf, bufsz);
500
501 warnx(_("time %ld is out of range."), (long)t);
502 return -1;
3c201431
KZ
503}
504
eee665bb 505/* relative time functions */
d393c00c
SK
506static inline int time_is_thisyear(struct tm const *const tm,
507 struct tm const *const tmnow)
eee665bb 508{
d393c00c 509 return tm->tm_year == tmnow->tm_year;
eee665bb
KZ
510}
511
d393c00c
SK
512static inline int time_is_today(struct tm const *const tm,
513 struct tm const *const tmnow)
eee665bb 514{
d393c00c
SK
515 return (tm->tm_yday == tmnow->tm_yday &&
516 time_is_thisyear(tm, tmnow));
eee665bb
KZ
517}
518
519int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, size_t bufsz)
520{
d393c00c 521 struct tm tm, tmnow;
eee665bb
KZ
522 int rc = 0;
523
d393c00c
SK
524 if (now->tv_sec == 0)
525 gettimeofday(now, NULL);
526
527 localtime_r(t, &tm);
528 localtime_r(&now->tv_sec, &tmnow);
eee665bb 529
d393c00c 530 if (time_is_today(&tm, &tmnow)) {
eee665bb
KZ
531 rc = snprintf(buf, bufsz, "%02d:%02d", tm.tm_hour, tm.tm_min);
532 if (rc < 0 || (size_t) rc > bufsz)
533 return -1;
534 rc = 1;
535
d393c00c 536 } else if (time_is_thisyear(&tm, &tmnow)) {
eee665bb
KZ
537 if (flags & UL_SHORTTIME_THISYEAR_HHMM)
538 rc = strftime(buf, bufsz, "%b%d/%H:%M", &tm);
539 else
540 rc = strftime(buf, bufsz, "%b%d", &tm);
541 } else
542 rc = strftime(buf, bufsz, "%Y-%b%d", &tm);
543
544 return rc <= 0 ? -1 : 0;
545}
3c201431 546
b72a75e9
SK
547#ifndef HAVE_TIMEGM
548time_t timegm(struct tm *tm)
549{
550 const char *zone = getenv("TZ");
551 time_t ret;
552
553 setenv("TZ", "", 1);
554 tzset();
555 ret = mktime(tm);
556 if (zone)
557 setenv("TZ", zone, 1);
558 else
559 unsetenv("TZ");
560 tzset();
561 return ret;
562}
563#endif /* HAVE_TIMEGM */
564
3c201431
KZ
565#ifdef TEST_PROGRAM_TIMEUTILS
566
567int main(int argc, char *argv[])
568{
569 struct timeval tv = { 0 };
4111bb3a 570 char buf[ISO_BUFSIZ];
3c201431
KZ
571
572 if (argc < 2) {
573 fprintf(stderr, "usage: %s <time> [<usec>]\n", argv[0]);
574 exit(EXIT_FAILURE);
575 }
576
577 tv.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>");
578 if (argc == 3)
579 tv.tv_usec = strtos64_or_err(argv[2], "failed to parse <usec>");
580
4111bb3a 581 strtimeval_iso(&tv, ISO_DATE, buf, sizeof(buf));
33c7ffa3 582 printf("Date: '%s'\n", buf);
3c201431 583
4111bb3a 584 strtimeval_iso(&tv, ISO_TIME, buf, sizeof(buf));
33c7ffa3 585 printf("Time: '%s'\n", buf);
3c201431 586
4111bb3a
WP
587 strtimeval_iso(&tv, ISO_DATE | ISO_TIME | ISO_COMMAUSEC | ISO_T,
588 buf, sizeof(buf));
33c7ffa3 589 printf("Full: '%s'\n", buf);
3c201431 590
4111bb3a 591 strtimeval_iso(&tv, ISO_TIMESTAMP_DOT, buf, sizeof(buf));
33c7ffa3 592 printf("Zone: '%s'\n", buf);
3c201431
KZ
593
594 return EXIT_SUCCESS;
595}
596
e8f7acb0 597#endif /* TEST_PROGRAM_TIMEUTILS */