]> 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>
03cc26dd 24#include <sys/timex.h>
9a98c7a1
LP
25
26#include "util.h"
27#include "time-util.h"
28
29usec_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
37dual_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
46dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
47 int64_t delta;
48 assert(ts);
49
cae0c5e0
LP
50 if (u == (usec_t) -1) {
51 ts->realtime = ts->monotonic = (usec_t) -1;
52 return ts;
53 }
54
9a98c7a1
LP
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
cae0c5e0
LP
73dual_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
9a98c7a1
LP
94usec_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
109struct 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
124usec_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
139struct 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;
4d89874a
ZJS
145 } else {
146 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
147 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
148 }
149
9a98c7a1
LP
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
966204e0 160 if (t <= 0 || t == (usec_t) -1)
9a98c7a1
LP
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
f02d8367
ZJS
171char *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
966204e0 178 if (t <= 0 || t == (usec_t) -1)
f02d8367
ZJS
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
bbb8486e 193char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 194 const char *s;
9a98c7a1
LP
195 usec_t n, d;
196
197 n = now(CLOCK_REALTIME);
198
1fcf71f5 199 if (t <= 0 || (t == (usec_t) -1))
9a98c7a1
LP
200 return NULL;
201
1fcf71f5
LP
202 if (n > t) {
203 d = n - t;
204 s = "ago";
205 } else {
206 d = t - n;
207 s = "left";
208 }
9a98c7a1
LP
209
210 if (d >= USEC_PER_YEAR)
1fcf71f5 211 snprintf(buf, l, "%llu years %llu months %s",
9a98c7a1 212 (unsigned long long) (d / USEC_PER_YEAR),
1fcf71f5 213 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH), s);
9a98c7a1 214 else if (d >= USEC_PER_MONTH)
1fcf71f5 215 snprintf(buf, l, "%llu months %llu days %s",
9a98c7a1 216 (unsigned long long) (d / USEC_PER_MONTH),
1fcf71f5 217 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY), s);
9a98c7a1 218 else if (d >= USEC_PER_WEEK)
1fcf71f5 219 snprintf(buf, l, "%llu weeks %llu days %s",
9a98c7a1 220 (unsigned long long) (d / USEC_PER_WEEK),
1fcf71f5 221 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY), s);
9a98c7a1 222 else if (d >= 2*USEC_PER_DAY)
1fcf71f5 223 snprintf(buf, l, "%llu days %s", (unsigned long long) (d / USEC_PER_DAY), s);
9a98c7a1 224 else if (d >= 25*USEC_PER_HOUR)
1fcf71f5
LP
225 snprintf(buf, l, "1 day %lluh %s",
226 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR), s);
9a98c7a1 227 else if (d >= 6*USEC_PER_HOUR)
1fcf71f5
LP
228 snprintf(buf, l, "%lluh %s",
229 (unsigned long long) (d / USEC_PER_HOUR), s);
9a98c7a1 230 else if (d >= USEC_PER_HOUR)
1fcf71f5 231 snprintf(buf, l, "%lluh %llumin %s",
9a98c7a1 232 (unsigned long long) (d / USEC_PER_HOUR),
1fcf71f5 233 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE), s);
9a98c7a1 234 else if (d >= 5*USEC_PER_MINUTE)
1fcf71f5
LP
235 snprintf(buf, l, "%llumin %s",
236 (unsigned long long) (d / USEC_PER_MINUTE), s);
9a98c7a1 237 else if (d >= USEC_PER_MINUTE)
1fcf71f5 238 snprintf(buf, l, "%llumin %llus %s",
9a98c7a1 239 (unsigned long long) (d / USEC_PER_MINUTE),
1fcf71f5 240 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC), s);
9a98c7a1 241 else if (d >= USEC_PER_SEC)
1fcf71f5
LP
242 snprintf(buf, l, "%llus %s",
243 (unsigned long long) (d / USEC_PER_SEC), s);
9a98c7a1 244 else if (d >= USEC_PER_MSEC)
1fcf71f5
LP
245 snprintf(buf, l, "%llums %s",
246 (unsigned long long) (d / USEC_PER_MSEC), s);
9a98c7a1 247 else if (d > 0)
1fcf71f5
LP
248 snprintf(buf, l, "%lluus %s",
249 (unsigned long long) d, s);
9a98c7a1
LP
250 else
251 snprintf(buf, l, "now");
252
253 buf[l-1] = 0;
254 return buf;
255}
256
2fa4092c 257char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
258 static const struct {
259 const char *suffix;
260 usec_t usec;
261 } table[] = {
b719810d
LP
262 { "y", USEC_PER_YEAR },
263 { "month", USEC_PER_MONTH },
9a98c7a1
LP
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;
2fa4092c 275 bool something = false;
9a98c7a1
LP
276
277 assert(buf);
278 assert(l > 0);
279
280 if (t == (usec_t) -1)
281 return NULL;
282
7c537b2e
LP
283 if (t <= 0) {
284 snprintf(p, l, "0");
285 p[l-1] = 0;
286 return p;
287 }
288
7f602784 289 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
290
291 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 292 int k = 0;
9a98c7a1 293 size_t n;
2fa4092c
LP
294 bool done = false;
295 usec_t a, b;
296
7c537b2e
LP
297 if (t <= 0)
298 break;
2fa4092c 299
7c537b2e 300 if (t < accuracy && something)
2fa4092c 301 break;
9a98c7a1
LP
302
303 if (t < table[i].usec)
304 continue;
305
306 if (l <= 1)
307 break;
308
2fa4092c
LP
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%llu.%0*llu%s",
329 p > buf ? " " : "",
330 (unsigned long long) 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%llu%s",
344 p > buf ? " " : "",
345 (unsigned long long) a,
346 table[i].suffix);
347
348 t = b;
349 }
350
9a98c7a1
LP
351 n = MIN((size_t) k, l);
352
353 l -= n;
354 p += n;
355
2fa4092c 356 something = true;
9a98c7a1
LP
357 }
358
359 *p = 0;
360
361 return buf;
362}
363
364void 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=%llu %llu\n",
374 name,
375 (unsigned long long) t->realtime,
376 (unsigned long long) t->monotonic);
377}
378
379void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
380 unsigned long long a, b;
381
382 assert(value);
383 assert(t);
384
b5dd8148 385 if (sscanf(value, "%llu %llu", &a, &b) != 2)
9a98c7a1
LP
386 log_debug("Failed to parse finish timestamp value %s", value);
387 else {
388 t->realtime = a;
389 t->monotonic = b;
390 }
391}
392
393int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
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
9a98c7a1
LP
414 const char *k;
415 struct tm tm, copy;
416 time_t x;
417 usec_t plus = 0, minus = 0, ret;
92134489
LP
418 int r, weekday = -1;
419 unsigned i;
9a98c7a1
LP
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 *
436 */
437
438 assert(t);
439 assert(usec);
440
441 x = time(NULL);
442 assert_se(localtime_r(&x, &tm));
6a741b4a 443 tm.tm_isdst = -1;
9a98c7a1
LP
444
445 if (streq(t, "now"))
446 goto finish;
447
448 else if (streq(t, "today")) {
449 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
450 goto finish;
451
452 } else if (streq(t, "yesterday")) {
453 tm.tm_mday --;
454 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
455 goto finish;
456
457 } else if (streq(t, "tomorrow")) {
458 tm.tm_mday ++;
459 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
460 goto finish;
461
462 } else if (t[0] == '+') {
463
7f602784 464 r = parse_sec(t+1, &plus);
9a98c7a1
LP
465 if (r < 0)
466 return r;
467
468 goto finish;
469 } else if (t[0] == '-') {
470
7f602784 471 r = parse_sec(t+1, &minus);
9a98c7a1
LP
472 if (r < 0)
473 return r;
474
475 goto finish;
decad910
LP
476
477 } else if (endswith(t, " ago")) {
478 _cleanup_free_ char *z;
479
480 z = strndup(t, strlen(t) - 4);
481 if (!z)
482 return -ENOMEM;
483
7f602784 484 r = parse_sec(z, &minus);
decad910
LP
485 if (r < 0)
486 return r;
487
1fcf71f5
LP
488 goto finish;
489 } else if (endswith(t, " left")) {
490 _cleanup_free_ char *z;
491
492 z = strndup(t, strlen(t) - 4);
493 if (!z)
494 return -ENOMEM;
495
496 r = parse_sec(z, &plus);
497 if (r < 0)
498 return r;
499
decad910 500 goto finish;
9a98c7a1
LP
501 }
502
92134489
LP
503 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
504 size_t skip;
505
506 if (!startswith_no_case(t, day_nr[i].name))
507 continue;
508
509 skip = strlen(day_nr[i].name);
510 if (t[skip] != ' ')
511 continue;
512
513 weekday = day_nr[i].nr;
514 t += skip + 1;
515 break;
516 }
517
9a98c7a1
LP
518 copy = tm;
519 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
520 if (k && *k == 0)
521 goto finish;
522
523 tm = copy;
524 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
525 if (k && *k == 0)
526 goto finish;
527
528 tm = copy;
529 k = strptime(t, "%y-%m-%d %H:%M", &tm);
530 if (k && *k == 0) {
531 tm.tm_sec = 0;
532 goto finish;
533 }
534
535 tm = copy;
536 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
537 if (k && *k == 0) {
538 tm.tm_sec = 0;
539 goto finish;
540 }
541
542 tm = copy;
543 k = strptime(t, "%y-%m-%d", &tm);
544 if (k && *k == 0) {
545 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
546 goto finish;
547 }
548
549 tm = copy;
550 k = strptime(t, "%Y-%m-%d", &tm);
551 if (k && *k == 0) {
552 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
553 goto finish;
554 }
555
556 tm = copy;
557 k = strptime(t, "%H:%M:%S", &tm);
558 if (k && *k == 0)
559 goto finish;
560
561 tm = copy;
562 k = strptime(t, "%H:%M", &tm);
563 if (k && *k == 0) {
564 tm.tm_sec = 0;
565 goto finish;
566 }
567
568 return -EINVAL;
569
570finish:
571 x = mktime(&tm);
572 if (x == (time_t) -1)
573 return -EINVAL;
574
92134489
LP
575 if (weekday >= 0 && tm.tm_wday != weekday)
576 return -EINVAL;
577
9a98c7a1
LP
578 ret = (usec_t) x * USEC_PER_SEC;
579
580 ret += plus;
581 if (ret > minus)
582 ret -= minus;
583 else
584 ret = 0;
585
586 *usec = ret;
587
588 return 0;
589}
590
7f602784 591int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
592 static const struct {
593 const char *suffix;
594 usec_t usec;
595 } table[] = {
596 { "seconds", USEC_PER_SEC },
597 { "second", USEC_PER_SEC },
598 { "sec", USEC_PER_SEC },
599 { "s", USEC_PER_SEC },
600 { "minutes", USEC_PER_MINUTE },
601 { "minute", USEC_PER_MINUTE },
602 { "min", USEC_PER_MINUTE },
603 { "months", USEC_PER_MONTH },
604 { "month", USEC_PER_MONTH },
605 { "msec", USEC_PER_MSEC },
606 { "ms", USEC_PER_MSEC },
607 { "m", USEC_PER_MINUTE },
608 { "hours", USEC_PER_HOUR },
609 { "hour", USEC_PER_HOUR },
610 { "hr", USEC_PER_HOUR },
611 { "h", USEC_PER_HOUR },
612 { "days", USEC_PER_DAY },
613 { "day", USEC_PER_DAY },
614 { "d", USEC_PER_DAY },
615 { "weeks", USEC_PER_WEEK },
616 { "week", USEC_PER_WEEK },
617 { "w", USEC_PER_WEEK },
618 { "years", USEC_PER_YEAR },
619 { "year", USEC_PER_YEAR },
620 { "y", USEC_PER_YEAR },
621 { "usec", 1ULL },
622 { "us", 1ULL },
623 { "", USEC_PER_SEC }, /* default is sec */
624 };
625
626 const char *p;
627 usec_t r = 0;
cb0dac05 628 bool something = false;
9a98c7a1
LP
629
630 assert(t);
631 assert(usec);
632
633 p = t;
cb0dac05
LP
634 for (;;) {
635 long long l, z = 0;
9a98c7a1 636 char *e;
cb0dac05
LP
637 unsigned i, n = 0;
638
639 p += strspn(p, WHITESPACE);
640
641 if (*p == 0) {
642 if (!something)
643 return -EINVAL;
644
645 break;
646 }
9a98c7a1
LP
647
648 errno = 0;
649 l = strtoll(p, &e, 10);
650
8333c77e 651 if (errno > 0)
9a98c7a1
LP
652 return -errno;
653
654 if (l < 0)
655 return -ERANGE;
656
cb0dac05
LP
657 if (*e == '.') {
658 char *b = e + 1;
659
660 errno = 0;
661 z = strtoll(b, &e, 10);
662 if (errno > 0)
663 return -errno;
664
665 if (z < 0)
666 return -ERANGE;
667
668 if (e == b)
669 return -EINVAL;
670
671 n = e - b;
672
673 } else if (e == p)
9a98c7a1
LP
674 return -EINVAL;
675
676 e += strspn(e, WHITESPACE);
677
678 for (i = 0; i < ELEMENTSOF(table); i++)
679 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
680 usec_t k = (usec_t) z * table[i].usec;
681
682 for (; n > 0; n--)
683 k /= 10;
684
685 r += (usec_t) l * table[i].usec + k;
9a98c7a1 686 p = e + strlen(table[i].suffix);
cb0dac05
LP
687
688 something = true;
9a98c7a1
LP
689 break;
690 }
691
692 if (i >= ELEMENTSOF(table))
693 return -EINVAL;
694
cb0dac05 695 }
9a98c7a1
LP
696
697 *usec = r;
698
699 return 0;
700}
701
702int parse_nsec(const char *t, nsec_t *nsec) {
703 static const struct {
704 const char *suffix;
705 nsec_t nsec;
706 } table[] = {
707 { "seconds", NSEC_PER_SEC },
708 { "second", NSEC_PER_SEC },
709 { "sec", NSEC_PER_SEC },
710 { "s", NSEC_PER_SEC },
711 { "minutes", NSEC_PER_MINUTE },
712 { "minute", NSEC_PER_MINUTE },
713 { "min", NSEC_PER_MINUTE },
714 { "months", NSEC_PER_MONTH },
715 { "month", NSEC_PER_MONTH },
716 { "msec", NSEC_PER_MSEC },
717 { "ms", NSEC_PER_MSEC },
718 { "m", NSEC_PER_MINUTE },
719 { "hours", NSEC_PER_HOUR },
720 { "hour", NSEC_PER_HOUR },
721 { "hr", NSEC_PER_HOUR },
722 { "h", NSEC_PER_HOUR },
723 { "days", NSEC_PER_DAY },
724 { "day", NSEC_PER_DAY },
725 { "d", NSEC_PER_DAY },
726 { "weeks", NSEC_PER_WEEK },
727 { "week", NSEC_PER_WEEK },
728 { "w", NSEC_PER_WEEK },
729 { "years", NSEC_PER_YEAR },
730 { "year", NSEC_PER_YEAR },
731 { "y", NSEC_PER_YEAR },
732 { "usec", NSEC_PER_USEC },
733 { "us", NSEC_PER_USEC },
734 { "nsec", 1ULL },
735 { "ns", 1ULL },
736 { "", 1ULL }, /* default is nsec */
737 };
738
739 const char *p;
740 nsec_t r = 0;
cb0dac05 741 bool something = false;
9a98c7a1
LP
742
743 assert(t);
744 assert(nsec);
745
746 p = t;
cb0dac05
LP
747 for (;;) {
748 long long l, z = 0;
9a98c7a1 749 char *e;
cb0dac05
LP
750 unsigned i, n = 0;
751
752 p += strspn(p, WHITESPACE);
753
754 if (*p == 0) {
755 if (!something)
756 return -EINVAL;
757
758 break;
759 }
9a98c7a1
LP
760
761 errno = 0;
762 l = strtoll(p, &e, 10);
763
8333c77e 764 if (errno > 0)
9a98c7a1
LP
765 return -errno;
766
767 if (l < 0)
768 return -ERANGE;
769
cb0dac05
LP
770 if (*e == '.') {
771 char *b = e + 1;
772
773 errno = 0;
774 z = strtoll(b, &e, 10);
775 if (errno > 0)
776 return -errno;
777
778 if (z < 0)
779 return -ERANGE;
780
781 if (e == b)
782 return -EINVAL;
783
784 n = e - b;
785
786 } else if (e == p)
9a98c7a1
LP
787 return -EINVAL;
788
789 e += strspn(e, WHITESPACE);
790
791 for (i = 0; i < ELEMENTSOF(table); i++)
792 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
793 nsec_t k = (nsec_t) z * table[i].nsec;
794
795 for (; n > 0; n--)
796 k /= 10;
797
798 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 799 p = e + strlen(table[i].suffix);
cb0dac05
LP
800
801 something = true;
9a98c7a1
LP
802 break;
803 }
804
805 if (i >= ELEMENTSOF(table))
806 return -EINVAL;
807
cb0dac05 808 }
9a98c7a1
LP
809
810 *nsec = r;
811
812 return 0;
813}
03cc26dd
LP
814
815bool ntp_synced(void) {
816 struct timex txc = {};
817
818 if (adjtimex(&txc) < 0)
819 return false;
820
821 if (txc.status & STA_UNSYNC)
822 return false;
823
824 return true;
825}