]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
util: add a bit of syntactic sugar to run short code fragments with a different umask
[thirdparty/systemd.git] / src / shared / time-util.c
CommitLineData
9a98c7a1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <time.h>
23#include <string.h>
24
25#include "util.h"
26#include "time-util.h"
27
28usec_t now(clockid_t clock_id) {
29 struct timespec ts;
30
31 assert_se(clock_gettime(clock_id, &ts) == 0);
32
33 return timespec_load(&ts);
34}
35
36dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
37 assert(ts);
38
39 ts->realtime = now(CLOCK_REALTIME);
40 ts->monotonic = now(CLOCK_MONOTONIC);
41
42 return ts;
43}
44
45dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
46 int64_t delta;
47 assert(ts);
48
cae0c5e0
LP
49 if (u == (usec_t) -1) {
50 ts->realtime = ts->monotonic = (usec_t) -1;
51 return ts;
52 }
53
9a98c7a1
LP
54 ts->realtime = u;
55
56 if (u == 0)
57 ts->monotonic = 0;
58 else {
59 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
60
61 ts->monotonic = now(CLOCK_MONOTONIC);
62
63 if ((int64_t) ts->monotonic > delta)
64 ts->monotonic -= delta;
65 else
66 ts->monotonic = 0;
67 }
68
69 return ts;
70}
71
cae0c5e0
LP
72dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
73 int64_t delta;
74 assert(ts);
75
76 if (u == (usec_t) -1) {
77 ts->realtime = ts->monotonic = (usec_t) -1;
78 return ts;
79 }
80
81 ts->monotonic = u;
82 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
83
84 ts->realtime = now(CLOCK_REALTIME);
85 if ((int64_t) ts->realtime > delta)
86 ts->realtime -= delta;
87 else
88 ts->realtime = 0;
89
90 return ts;
91}
92
9a98c7a1
LP
93usec_t timespec_load(const struct timespec *ts) {
94 assert(ts);
95
96 if (ts->tv_sec == (time_t) -1 &&
97 ts->tv_nsec == (long) -1)
98 return (usec_t) -1;
99
100 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
101 return (usec_t) -1;
102
103 return
104 (usec_t) ts->tv_sec * USEC_PER_SEC +
105 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
106}
107
108struct timespec *timespec_store(struct timespec *ts, usec_t u) {
109 assert(ts);
110
111 if (u == (usec_t) -1) {
112 ts->tv_sec = (time_t) -1;
113 ts->tv_nsec = (long) -1;
114 return ts;
115 }
116
117 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
118 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
119
120 return ts;
121}
122
123usec_t timeval_load(const struct timeval *tv) {
124 assert(tv);
125
126 if (tv->tv_sec == (time_t) -1 &&
127 tv->tv_usec == (suseconds_t) -1)
128 return (usec_t) -1;
129
130 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
131 return (usec_t) -1;
132
133 return
134 (usec_t) tv->tv_sec * USEC_PER_SEC +
135 (usec_t) tv->tv_usec;
136}
137
138struct timeval *timeval_store(struct timeval *tv, usec_t u) {
139 assert(tv);
140
141 if (u == (usec_t) -1) {
142 tv->tv_sec = (time_t) -1;
143 tv->tv_usec = (suseconds_t) -1;
144 return tv;
145 }
146
147 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
148 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
149
150 return tv;
151}
152
153char *format_timestamp(char *buf, size_t l, usec_t t) {
154 struct tm tm;
155 time_t sec;
156
157 assert(buf);
158 assert(l > 0);
159
160 if (t <= 0)
161 return NULL;
162
163 sec = (time_t) (t / USEC_PER_SEC);
164
165 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
166 return NULL;
167
168 return buf;
169}
170
bbb8486e 171char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
9a98c7a1
LP
172 usec_t n, d;
173
174 n = now(CLOCK_REALTIME);
175
176 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
177 return NULL;
178
179 d = n - t;
180
181 if (d >= USEC_PER_YEAR)
182 snprintf(buf, l, "%llu years %llu months ago",
183 (unsigned long long) (d / USEC_PER_YEAR),
184 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
185 else if (d >= USEC_PER_MONTH)
186 snprintf(buf, l, "%llu months %llu days ago",
187 (unsigned long long) (d / USEC_PER_MONTH),
188 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
189 else if (d >= USEC_PER_WEEK)
190 snprintf(buf, l, "%llu weeks %llu days ago",
191 (unsigned long long) (d / USEC_PER_WEEK),
192 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
193 else if (d >= 2*USEC_PER_DAY)
194 snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
195 else if (d >= 25*USEC_PER_HOUR)
196 snprintf(buf, l, "1 day %lluh ago",
197 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
198 else if (d >= 6*USEC_PER_HOUR)
199 snprintf(buf, l, "%lluh ago",
200 (unsigned long long) (d / USEC_PER_HOUR));
201 else if (d >= USEC_PER_HOUR)
202 snprintf(buf, l, "%lluh %llumin ago",
203 (unsigned long long) (d / USEC_PER_HOUR),
204 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
205 else if (d >= 5*USEC_PER_MINUTE)
206 snprintf(buf, l, "%llumin ago",
207 (unsigned long long) (d / USEC_PER_MINUTE));
208 else if (d >= USEC_PER_MINUTE)
209 snprintf(buf, l, "%llumin %llus ago",
210 (unsigned long long) (d / USEC_PER_MINUTE),
211 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
212 else if (d >= USEC_PER_SEC)
213 snprintf(buf, l, "%llus ago",
214 (unsigned long long) (d / USEC_PER_SEC));
215 else if (d >= USEC_PER_MSEC)
216 snprintf(buf, l, "%llums ago",
217 (unsigned long long) (d / USEC_PER_MSEC));
218 else if (d > 0)
219 snprintf(buf, l, "%lluus ago",
220 (unsigned long long) d);
221 else
222 snprintf(buf, l, "now");
223
224 buf[l-1] = 0;
225 return buf;
226}
227
2fa4092c 228char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
229 static const struct {
230 const char *suffix;
231 usec_t usec;
232 } table[] = {
b719810d
LP
233 { "y", USEC_PER_YEAR },
234 { "month", USEC_PER_MONTH },
9a98c7a1
LP
235 { "w", USEC_PER_WEEK },
236 { "d", USEC_PER_DAY },
237 { "h", USEC_PER_HOUR },
238 { "min", USEC_PER_MINUTE },
239 { "s", USEC_PER_SEC },
240 { "ms", USEC_PER_MSEC },
241 { "us", 1 },
242 };
243
244 unsigned i;
245 char *p = buf;
2fa4092c 246 bool something = false;
9a98c7a1
LP
247
248 assert(buf);
249 assert(l > 0);
250
251 if (t == (usec_t) -1)
252 return NULL;
253
7f602784 254 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
255
256 for (i = 0; i < ELEMENTSOF(table); i++) {
257 int k;
258 size_t n;
2fa4092c
LP
259 bool done = false;
260 usec_t a, b;
261
262 if (t == 0 || t < accuracy) {
263 if (!something) {
264 snprintf(p, l, "0");
265 p[l-1] = 0;
266 return p;
267 }
268
269 break;
270 }
9a98c7a1
LP
271
272 if (t < table[i].usec)
273 continue;
274
275 if (l <= 1)
276 break;
277
2fa4092c
LP
278 a = t / table[i].usec;
279 b = t % table[i].usec;
280
281 /* Let's see if we should shows this in dot notation */
282 if (t < USEC_PER_MINUTE && b > 0) {
283 usec_t cc;
284 int j;
285
286 j = 0;
287 for (cc = table[i].usec; cc > 1; cc /= 10)
288 j++;
289
290 for (cc = accuracy; cc > 1; cc /= 10) {
291 b /= 10;
292 j--;
293 }
294
295 if (j > 0) {
296 k = snprintf(p, l,
297 "%s%llu.%0*llu%s",
298 p > buf ? " " : "",
299 (unsigned long long) a,
300 j,
301 (unsigned long long) b,
302 table[i].suffix);
303
304 t = 0;
305 done = true;
306 }
307 }
308
309 /* No? Then let's show it normally */
310 if (!done) {
311 k = snprintf(p, l,
312 "%s%llu%s",
313 p > buf ? " " : "",
314 (unsigned long long) a,
315 table[i].suffix);
316
317 t = b;
318 }
319
9a98c7a1
LP
320 n = MIN((size_t) k, l);
321
322 l -= n;
323 p += n;
324
2fa4092c
LP
325
326 something = true;
9a98c7a1
LP
327 }
328
329 *p = 0;
330
331 return buf;
332}
333
334void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
335
336 assert(f);
337 assert(name);
338 assert(t);
339
340 if (!dual_timestamp_is_set(t))
341 return;
342
343 fprintf(f, "%s=%llu %llu\n",
344 name,
345 (unsigned long long) t->realtime,
346 (unsigned long long) t->monotonic);
347}
348
349void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
350 unsigned long long a, b;
351
352 assert(value);
353 assert(t);
354
355 if (sscanf(value, "%lli %llu", &a, &b) != 2)
356 log_debug("Failed to parse finish timestamp value %s", value);
357 else {
358 t->realtime = a;
359 t->monotonic = b;
360 }
361}
362
363int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
364 static const struct {
365 const char *name;
366 const int nr;
367 } day_nr[] = {
368 { "Sunday", 0 },
369 { "Sun", 0 },
370 { "Monday", 1 },
371 { "Mon", 1 },
372 { "Tuesday", 2 },
373 { "Tue", 2 },
374 { "Wednesday", 3 },
375 { "Wed", 3 },
376 { "Thursday", 4 },
377 { "Thu", 4 },
378 { "Friday", 5 },
379 { "Fri", 5 },
380 { "Saturday", 6 },
381 { "Sat", 6 },
382 };
383
9a98c7a1
LP
384 const char *k;
385 struct tm tm, copy;
386 time_t x;
387 usec_t plus = 0, minus = 0, ret;
92134489
LP
388 int r, weekday = -1;
389 unsigned i;
9a98c7a1
LP
390
391 /*
392 * Allowed syntaxes:
393 *
394 * 2012-09-22 16:34:22
395 * 2012-09-22 16:34 (seconds will be set to 0)
396 * 2012-09-22 (time will be set to 00:00:00)
397 * 16:34:22 (date will be set to today)
398 * 16:34 (date will be set to today, seconds to 0)
399 * now
400 * yesterday (time is set to 00:00:00)
401 * today (time is set to 00:00:00)
402 * tomorrow (time is set to 00:00:00)
403 * +5min
404 * -5days
405 *
406 */
407
408 assert(t);
409 assert(usec);
410
411 x = time(NULL);
412 assert_se(localtime_r(&x, &tm));
6a741b4a 413 tm.tm_isdst = -1;
9a98c7a1
LP
414
415 if (streq(t, "now"))
416 goto finish;
417
418 else if (streq(t, "today")) {
419 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
420 goto finish;
421
422 } else if (streq(t, "yesterday")) {
423 tm.tm_mday --;
424 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
425 goto finish;
426
427 } else if (streq(t, "tomorrow")) {
428 tm.tm_mday ++;
429 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
430 goto finish;
431
432 } else if (t[0] == '+') {
433
7f602784 434 r = parse_sec(t+1, &plus);
9a98c7a1
LP
435 if (r < 0)
436 return r;
437
438 goto finish;
439 } else if (t[0] == '-') {
440
7f602784 441 r = parse_sec(t+1, &minus);
9a98c7a1
LP
442 if (r < 0)
443 return r;
444
445 goto finish;
decad910
LP
446
447 } else if (endswith(t, " ago")) {
448 _cleanup_free_ char *z;
449
450 z = strndup(t, strlen(t) - 4);
451 if (!z)
452 return -ENOMEM;
453
7f602784 454 r = parse_sec(z, &minus);
decad910
LP
455 if (r < 0)
456 return r;
457
458 goto finish;
9a98c7a1
LP
459 }
460
92134489
LP
461 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
462 size_t skip;
463
464 if (!startswith_no_case(t, day_nr[i].name))
465 continue;
466
467 skip = strlen(day_nr[i].name);
468 if (t[skip] != ' ')
469 continue;
470
471 weekday = day_nr[i].nr;
472 t += skip + 1;
473 break;
474 }
475
9a98c7a1
LP
476 copy = tm;
477 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
478 if (k && *k == 0)
479 goto finish;
480
481 tm = copy;
482 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
483 if (k && *k == 0)
484 goto finish;
485
486 tm = copy;
487 k = strptime(t, "%y-%m-%d %H:%M", &tm);
488 if (k && *k == 0) {
489 tm.tm_sec = 0;
490 goto finish;
491 }
492
493 tm = copy;
494 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
495 if (k && *k == 0) {
496 tm.tm_sec = 0;
497 goto finish;
498 }
499
500 tm = copy;
501 k = strptime(t, "%y-%m-%d", &tm);
502 if (k && *k == 0) {
503 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
504 goto finish;
505 }
506
507 tm = copy;
508 k = strptime(t, "%Y-%m-%d", &tm);
509 if (k && *k == 0) {
510 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
511 goto finish;
512 }
513
514 tm = copy;
515 k = strptime(t, "%H:%M:%S", &tm);
516 if (k && *k == 0)
517 goto finish;
518
519 tm = copy;
520 k = strptime(t, "%H:%M", &tm);
521 if (k && *k == 0) {
522 tm.tm_sec = 0;
523 goto finish;
524 }
525
526 return -EINVAL;
527
528finish:
529 x = mktime(&tm);
530 if (x == (time_t) -1)
531 return -EINVAL;
532
92134489
LP
533 if (weekday >= 0 && tm.tm_wday != weekday)
534 return -EINVAL;
535
9a98c7a1
LP
536 ret = (usec_t) x * USEC_PER_SEC;
537
538 ret += plus;
539 if (ret > minus)
540 ret -= minus;
541 else
542 ret = 0;
543
544 *usec = ret;
545
546 return 0;
547}
548
7f602784 549int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
550 static const struct {
551 const char *suffix;
552 usec_t usec;
553 } table[] = {
554 { "seconds", USEC_PER_SEC },
555 { "second", USEC_PER_SEC },
556 { "sec", USEC_PER_SEC },
557 { "s", USEC_PER_SEC },
558 { "minutes", USEC_PER_MINUTE },
559 { "minute", USEC_PER_MINUTE },
560 { "min", USEC_PER_MINUTE },
561 { "months", USEC_PER_MONTH },
562 { "month", USEC_PER_MONTH },
563 { "msec", USEC_PER_MSEC },
564 { "ms", USEC_PER_MSEC },
565 { "m", USEC_PER_MINUTE },
566 { "hours", USEC_PER_HOUR },
567 { "hour", USEC_PER_HOUR },
568 { "hr", USEC_PER_HOUR },
569 { "h", USEC_PER_HOUR },
570 { "days", USEC_PER_DAY },
571 { "day", USEC_PER_DAY },
572 { "d", USEC_PER_DAY },
573 { "weeks", USEC_PER_WEEK },
574 { "week", USEC_PER_WEEK },
575 { "w", USEC_PER_WEEK },
576 { "years", USEC_PER_YEAR },
577 { "year", USEC_PER_YEAR },
578 { "y", USEC_PER_YEAR },
579 { "usec", 1ULL },
580 { "us", 1ULL },
581 { "", USEC_PER_SEC }, /* default is sec */
582 };
583
584 const char *p;
585 usec_t r = 0;
cb0dac05 586 bool something = false;
9a98c7a1
LP
587
588 assert(t);
589 assert(usec);
590
591 p = t;
cb0dac05
LP
592 for (;;) {
593 long long l, z = 0;
9a98c7a1 594 char *e;
cb0dac05
LP
595 unsigned i, n = 0;
596
597 p += strspn(p, WHITESPACE);
598
599 if (*p == 0) {
600 if (!something)
601 return -EINVAL;
602
603 break;
604 }
9a98c7a1
LP
605
606 errno = 0;
607 l = strtoll(p, &e, 10);
608
8333c77e 609 if (errno > 0)
9a98c7a1
LP
610 return -errno;
611
612 if (l < 0)
613 return -ERANGE;
614
cb0dac05
LP
615 if (*e == '.') {
616 char *b = e + 1;
617
618 errno = 0;
619 z = strtoll(b, &e, 10);
620 if (errno > 0)
621 return -errno;
622
623 if (z < 0)
624 return -ERANGE;
625
626 if (e == b)
627 return -EINVAL;
628
629 n = e - b;
630
631 } else if (e == p)
9a98c7a1
LP
632 return -EINVAL;
633
634 e += strspn(e, WHITESPACE);
635
636 for (i = 0; i < ELEMENTSOF(table); i++)
637 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
638 usec_t k = (usec_t) z * table[i].usec;
639
640 for (; n > 0; n--)
641 k /= 10;
642
643 r += (usec_t) l * table[i].usec + k;
9a98c7a1 644 p = e + strlen(table[i].suffix);
cb0dac05
LP
645
646 something = true;
9a98c7a1
LP
647 break;
648 }
649
650 if (i >= ELEMENTSOF(table))
651 return -EINVAL;
652
cb0dac05 653 }
9a98c7a1
LP
654
655 *usec = r;
656
657 return 0;
658}
659
660int parse_nsec(const char *t, nsec_t *nsec) {
661 static const struct {
662 const char *suffix;
663 nsec_t nsec;
664 } table[] = {
665 { "seconds", NSEC_PER_SEC },
666 { "second", NSEC_PER_SEC },
667 { "sec", NSEC_PER_SEC },
668 { "s", NSEC_PER_SEC },
669 { "minutes", NSEC_PER_MINUTE },
670 { "minute", NSEC_PER_MINUTE },
671 { "min", NSEC_PER_MINUTE },
672 { "months", NSEC_PER_MONTH },
673 { "month", NSEC_PER_MONTH },
674 { "msec", NSEC_PER_MSEC },
675 { "ms", NSEC_PER_MSEC },
676 { "m", NSEC_PER_MINUTE },
677 { "hours", NSEC_PER_HOUR },
678 { "hour", NSEC_PER_HOUR },
679 { "hr", NSEC_PER_HOUR },
680 { "h", NSEC_PER_HOUR },
681 { "days", NSEC_PER_DAY },
682 { "day", NSEC_PER_DAY },
683 { "d", NSEC_PER_DAY },
684 { "weeks", NSEC_PER_WEEK },
685 { "week", NSEC_PER_WEEK },
686 { "w", NSEC_PER_WEEK },
687 { "years", NSEC_PER_YEAR },
688 { "year", NSEC_PER_YEAR },
689 { "y", NSEC_PER_YEAR },
690 { "usec", NSEC_PER_USEC },
691 { "us", NSEC_PER_USEC },
692 { "nsec", 1ULL },
693 { "ns", 1ULL },
694 { "", 1ULL }, /* default is nsec */
695 };
696
697 const char *p;
698 nsec_t r = 0;
cb0dac05 699 bool something = false;
9a98c7a1
LP
700
701 assert(t);
702 assert(nsec);
703
704 p = t;
cb0dac05
LP
705 for (;;) {
706 long long l, z = 0;
9a98c7a1 707 char *e;
cb0dac05
LP
708 unsigned i, n = 0;
709
710 p += strspn(p, WHITESPACE);
711
712 if (*p == 0) {
713 if (!something)
714 return -EINVAL;
715
716 break;
717 }
9a98c7a1
LP
718
719 errno = 0;
720 l = strtoll(p, &e, 10);
721
8333c77e 722 if (errno > 0)
9a98c7a1
LP
723 return -errno;
724
725 if (l < 0)
726 return -ERANGE;
727
cb0dac05
LP
728 if (*e == '.') {
729 char *b = e + 1;
730
731 errno = 0;
732 z = strtoll(b, &e, 10);
733 if (errno > 0)
734 return -errno;
735
736 if (z < 0)
737 return -ERANGE;
738
739 if (e == b)
740 return -EINVAL;
741
742 n = e - b;
743
744 } else if (e == p)
9a98c7a1
LP
745 return -EINVAL;
746
747 e += strspn(e, WHITESPACE);
748
749 for (i = 0; i < ELEMENTSOF(table); i++)
750 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
751 nsec_t k = (nsec_t) z * table[i].nsec;
752
753 for (; n > 0; n--)
754 k /= 10;
755
756 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 757 p = e + strlen(table[i].suffix);
cb0dac05
LP
758
759 something = true;
9a98c7a1
LP
760 break;
761 }
762
763 if (i >= ELEMENTSOF(table))
764 return -EINVAL;
765
cb0dac05 766 }
9a98c7a1
LP
767
768 *nsec = r;
769
770 return 0;
771}