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