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