]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
Rename F_TYPE_CMP() to F_TYPE_EQUAL()
[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
7c537b2e
LP
254 if (t <= 0) {
255 snprintf(p, l, "0");
256 p[l-1] = 0;
257 return p;
258 }
259
7f602784 260 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
261
262 for (i = 0; i < ELEMENTSOF(table); i++) {
263 int k;
264 size_t n;
2fa4092c
LP
265 bool done = false;
266 usec_t a, b;
267
7c537b2e
LP
268 if (t <= 0)
269 break;
2fa4092c 270
7c537b2e 271 if (t < accuracy && something)
2fa4092c 272 break;
9a98c7a1
LP
273
274 if (t < table[i].usec)
275 continue;
276
277 if (l <= 1)
278 break;
279
2fa4092c
LP
280 a = t / table[i].usec;
281 b = t % table[i].usec;
282
283 /* Let's see if we should shows this in dot notation */
284 if (t < USEC_PER_MINUTE && b > 0) {
285 usec_t cc;
286 int j;
287
288 j = 0;
289 for (cc = table[i].usec; cc > 1; cc /= 10)
290 j++;
291
292 for (cc = accuracy; cc > 1; cc /= 10) {
293 b /= 10;
294 j--;
295 }
296
297 if (j > 0) {
298 k = snprintf(p, l,
299 "%s%llu.%0*llu%s",
300 p > buf ? " " : "",
301 (unsigned long long) a,
302 j,
303 (unsigned long long) b,
304 table[i].suffix);
305
306 t = 0;
307 done = true;
308 }
309 }
310
311 /* No? Then let's show it normally */
312 if (!done) {
313 k = snprintf(p, l,
314 "%s%llu%s",
315 p > buf ? " " : "",
316 (unsigned long long) a,
317 table[i].suffix);
318
319 t = b;
320 }
321
9a98c7a1
LP
322 n = MIN((size_t) k, l);
323
324 l -= n;
325 p += n;
326
2fa4092c 327 something = true;
9a98c7a1
LP
328 }
329
330 *p = 0;
331
332 return buf;
333}
334
335void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
336
337 assert(f);
338 assert(name);
339 assert(t);
340
341 if (!dual_timestamp_is_set(t))
342 return;
343
344 fprintf(f, "%s=%llu %llu\n",
345 name,
346 (unsigned long long) t->realtime,
347 (unsigned long long) t->monotonic);
348}
349
350void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
351 unsigned long long a, b;
352
353 assert(value);
354 assert(t);
355
356 if (sscanf(value, "%lli %llu", &a, &b) != 2)
357 log_debug("Failed to parse finish timestamp value %s", value);
358 else {
359 t->realtime = a;
360 t->monotonic = b;
361 }
362}
363
364int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
365 static const struct {
366 const char *name;
367 const int nr;
368 } day_nr[] = {
369 { "Sunday", 0 },
370 { "Sun", 0 },
371 { "Monday", 1 },
372 { "Mon", 1 },
373 { "Tuesday", 2 },
374 { "Tue", 2 },
375 { "Wednesday", 3 },
376 { "Wed", 3 },
377 { "Thursday", 4 },
378 { "Thu", 4 },
379 { "Friday", 5 },
380 { "Fri", 5 },
381 { "Saturday", 6 },
382 { "Sat", 6 },
383 };
384
9a98c7a1
LP
385 const char *k;
386 struct tm tm, copy;
387 time_t x;
388 usec_t plus = 0, minus = 0, ret;
92134489
LP
389 int r, weekday = -1;
390 unsigned i;
9a98c7a1
LP
391
392 /*
393 * Allowed syntaxes:
394 *
395 * 2012-09-22 16:34:22
396 * 2012-09-22 16:34 (seconds will be set to 0)
397 * 2012-09-22 (time will be set to 00:00:00)
398 * 16:34:22 (date will be set to today)
399 * 16:34 (date will be set to today, seconds to 0)
400 * now
401 * yesterday (time is set to 00:00:00)
402 * today (time is set to 00:00:00)
403 * tomorrow (time is set to 00:00:00)
404 * +5min
405 * -5days
406 *
407 */
408
409 assert(t);
410 assert(usec);
411
412 x = time(NULL);
413 assert_se(localtime_r(&x, &tm));
6a741b4a 414 tm.tm_isdst = -1;
9a98c7a1
LP
415
416 if (streq(t, "now"))
417 goto finish;
418
419 else if (streq(t, "today")) {
420 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
421 goto finish;
422
423 } else if (streq(t, "yesterday")) {
424 tm.tm_mday --;
425 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
426 goto finish;
427
428 } else if (streq(t, "tomorrow")) {
429 tm.tm_mday ++;
430 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
431 goto finish;
432
433 } else if (t[0] == '+') {
434
7f602784 435 r = parse_sec(t+1, &plus);
9a98c7a1
LP
436 if (r < 0)
437 return r;
438
439 goto finish;
440 } else if (t[0] == '-') {
441
7f602784 442 r = parse_sec(t+1, &minus);
9a98c7a1
LP
443 if (r < 0)
444 return r;
445
446 goto finish;
decad910
LP
447
448 } else if (endswith(t, " ago")) {
449 _cleanup_free_ char *z;
450
451 z = strndup(t, strlen(t) - 4);
452 if (!z)
453 return -ENOMEM;
454
7f602784 455 r = parse_sec(z, &minus);
decad910
LP
456 if (r < 0)
457 return r;
458
459 goto finish;
9a98c7a1
LP
460 }
461
92134489
LP
462 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
463 size_t skip;
464
465 if (!startswith_no_case(t, day_nr[i].name))
466 continue;
467
468 skip = strlen(day_nr[i].name);
469 if (t[skip] != ' ')
470 continue;
471
472 weekday = day_nr[i].nr;
473 t += skip + 1;
474 break;
475 }
476
9a98c7a1
LP
477 copy = tm;
478 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
479 if (k && *k == 0)
480 goto finish;
481
482 tm = copy;
483 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
484 if (k && *k == 0)
485 goto finish;
486
487 tm = copy;
488 k = strptime(t, "%y-%m-%d %H:%M", &tm);
489 if (k && *k == 0) {
490 tm.tm_sec = 0;
491 goto finish;
492 }
493
494 tm = copy;
495 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
496 if (k && *k == 0) {
497 tm.tm_sec = 0;
498 goto finish;
499 }
500
501 tm = copy;
502 k = strptime(t, "%y-%m-%d", &tm);
503 if (k && *k == 0) {
504 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
505 goto finish;
506 }
507
508 tm = copy;
509 k = strptime(t, "%Y-%m-%d", &tm);
510 if (k && *k == 0) {
511 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
512 goto finish;
513 }
514
515 tm = copy;
516 k = strptime(t, "%H:%M:%S", &tm);
517 if (k && *k == 0)
518 goto finish;
519
520 tm = copy;
521 k = strptime(t, "%H:%M", &tm);
522 if (k && *k == 0) {
523 tm.tm_sec = 0;
524 goto finish;
525 }
526
527 return -EINVAL;
528
529finish:
530 x = mktime(&tm);
531 if (x == (time_t) -1)
532 return -EINVAL;
533
92134489
LP
534 if (weekday >= 0 && tm.tm_wday != weekday)
535 return -EINVAL;
536
9a98c7a1
LP
537 ret = (usec_t) x * USEC_PER_SEC;
538
539 ret += plus;
540 if (ret > minus)
541 ret -= minus;
542 else
543 ret = 0;
544
545 *usec = ret;
546
547 return 0;
548}
549
7f602784 550int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
551 static const struct {
552 const char *suffix;
553 usec_t usec;
554 } table[] = {
555 { "seconds", USEC_PER_SEC },
556 { "second", USEC_PER_SEC },
557 { "sec", USEC_PER_SEC },
558 { "s", USEC_PER_SEC },
559 { "minutes", USEC_PER_MINUTE },
560 { "minute", USEC_PER_MINUTE },
561 { "min", USEC_PER_MINUTE },
562 { "months", USEC_PER_MONTH },
563 { "month", USEC_PER_MONTH },
564 { "msec", USEC_PER_MSEC },
565 { "ms", USEC_PER_MSEC },
566 { "m", USEC_PER_MINUTE },
567 { "hours", USEC_PER_HOUR },
568 { "hour", USEC_PER_HOUR },
569 { "hr", USEC_PER_HOUR },
570 { "h", USEC_PER_HOUR },
571 { "days", USEC_PER_DAY },
572 { "day", USEC_PER_DAY },
573 { "d", USEC_PER_DAY },
574 { "weeks", USEC_PER_WEEK },
575 { "week", USEC_PER_WEEK },
576 { "w", USEC_PER_WEEK },
577 { "years", USEC_PER_YEAR },
578 { "year", USEC_PER_YEAR },
579 { "y", USEC_PER_YEAR },
580 { "usec", 1ULL },
581 { "us", 1ULL },
582 { "", USEC_PER_SEC }, /* default is sec */
583 };
584
585 const char *p;
586 usec_t r = 0;
cb0dac05 587 bool something = false;
9a98c7a1
LP
588
589 assert(t);
590 assert(usec);
591
592 p = t;
cb0dac05
LP
593 for (;;) {
594 long long l, z = 0;
9a98c7a1 595 char *e;
cb0dac05
LP
596 unsigned i, n = 0;
597
598 p += strspn(p, WHITESPACE);
599
600 if (*p == 0) {
601 if (!something)
602 return -EINVAL;
603
604 break;
605 }
9a98c7a1
LP
606
607 errno = 0;
608 l = strtoll(p, &e, 10);
609
8333c77e 610 if (errno > 0)
9a98c7a1
LP
611 return -errno;
612
613 if (l < 0)
614 return -ERANGE;
615
cb0dac05
LP
616 if (*e == '.') {
617 char *b = e + 1;
618
619 errno = 0;
620 z = strtoll(b, &e, 10);
621 if (errno > 0)
622 return -errno;
623
624 if (z < 0)
625 return -ERANGE;
626
627 if (e == b)
628 return -EINVAL;
629
630 n = e - b;
631
632 } else if (e == p)
9a98c7a1
LP
633 return -EINVAL;
634
635 e += strspn(e, WHITESPACE);
636
637 for (i = 0; i < ELEMENTSOF(table); i++)
638 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
639 usec_t k = (usec_t) z * table[i].usec;
640
641 for (; n > 0; n--)
642 k /= 10;
643
644 r += (usec_t) l * table[i].usec + k;
9a98c7a1 645 p = e + strlen(table[i].suffix);
cb0dac05
LP
646
647 something = true;
9a98c7a1
LP
648 break;
649 }
650
651 if (i >= ELEMENTSOF(table))
652 return -EINVAL;
653
cb0dac05 654 }
9a98c7a1
LP
655
656 *usec = r;
657
658 return 0;
659}
660
661int parse_nsec(const char *t, nsec_t *nsec) {
662 static const struct {
663 const char *suffix;
664 nsec_t nsec;
665 } table[] = {
666 { "seconds", NSEC_PER_SEC },
667 { "second", NSEC_PER_SEC },
668 { "sec", NSEC_PER_SEC },
669 { "s", NSEC_PER_SEC },
670 { "minutes", NSEC_PER_MINUTE },
671 { "minute", NSEC_PER_MINUTE },
672 { "min", NSEC_PER_MINUTE },
673 { "months", NSEC_PER_MONTH },
674 { "month", NSEC_PER_MONTH },
675 { "msec", NSEC_PER_MSEC },
676 { "ms", NSEC_PER_MSEC },
677 { "m", NSEC_PER_MINUTE },
678 { "hours", NSEC_PER_HOUR },
679 { "hour", NSEC_PER_HOUR },
680 { "hr", NSEC_PER_HOUR },
681 { "h", NSEC_PER_HOUR },
682 { "days", NSEC_PER_DAY },
683 { "day", NSEC_PER_DAY },
684 { "d", NSEC_PER_DAY },
685 { "weeks", NSEC_PER_WEEK },
686 { "week", NSEC_PER_WEEK },
687 { "w", NSEC_PER_WEEK },
688 { "years", NSEC_PER_YEAR },
689 { "year", NSEC_PER_YEAR },
690 { "y", NSEC_PER_YEAR },
691 { "usec", NSEC_PER_USEC },
692 { "us", NSEC_PER_USEC },
693 { "nsec", 1ULL },
694 { "ns", 1ULL },
695 { "", 1ULL }, /* default is nsec */
696 };
697
698 const char *p;
699 nsec_t r = 0;
cb0dac05 700 bool something = false;
9a98c7a1
LP
701
702 assert(t);
703 assert(nsec);
704
705 p = t;
cb0dac05
LP
706 for (;;) {
707 long long l, z = 0;
9a98c7a1 708 char *e;
cb0dac05
LP
709 unsigned i, n = 0;
710
711 p += strspn(p, WHITESPACE);
712
713 if (*p == 0) {
714 if (!something)
715 return -EINVAL;
716
717 break;
718 }
9a98c7a1
LP
719
720 errno = 0;
721 l = strtoll(p, &e, 10);
722
8333c77e 723 if (errno > 0)
9a98c7a1
LP
724 return -errno;
725
726 if (l < 0)
727 return -ERANGE;
728
cb0dac05
LP
729 if (*e == '.') {
730 char *b = e + 1;
731
732 errno = 0;
733 z = strtoll(b, &e, 10);
734 if (errno > 0)
735 return -errno;
736
737 if (z < 0)
738 return -ERANGE;
739
740 if (e == b)
741 return -EINVAL;
742
743 n = e - b;
744
745 } else if (e == p)
9a98c7a1
LP
746 return -EINVAL;
747
748 e += strspn(e, WHITESPACE);
749
750 for (i = 0; i < ELEMENTSOF(table); i++)
751 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
752 nsec_t k = (nsec_t) z * table[i].nsec;
753
754 for (; n > 0; n--)
755 k /= 10;
756
757 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 758 p = e + strlen(table[i].suffix);
cb0dac05
LP
759
760 something = true;
9a98c7a1
LP
761 break;
762 }
763
764 if (i >= ELEMENTSOF(table))
765 return -EINVAL;
766
cb0dac05 767 }
9a98c7a1
LP
768
769 *nsec = r;
770
771 return 0;
772}