]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
time: earlier exit from format_timestamp_relative() on special times
[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>
77ff2de9 25#include <sys/timerfd.h>
9a98c7a1
LP
26
27#include "util.h"
28#include "time-util.h"
75683450 29#include "strv.h"
9a98c7a1
LP
30
31usec_t now(clockid_t clock_id) {
32 struct timespec ts;
33
34 assert_se(clock_gettime(clock_id, &ts) == 0);
35
36 return timespec_load(&ts);
37}
38
39dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
40 assert(ts);
41
42 ts->realtime = now(CLOCK_REALTIME);
43 ts->monotonic = now(CLOCK_MONOTONIC);
44
45 return ts;
46}
47
48dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
49 int64_t delta;
50 assert(ts);
51
3a43da28
KS
52 if (u == USEC_INFINITY) {
53 ts->realtime = ts->monotonic = USEC_INFINITY;
cae0c5e0
LP
54 return ts;
55 }
56
9a98c7a1
LP
57 ts->realtime = u;
58
59 if (u == 0)
60 ts->monotonic = 0;
61 else {
62 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
63
64 ts->monotonic = now(CLOCK_MONOTONIC);
65
66 if ((int64_t) ts->monotonic > delta)
67 ts->monotonic -= delta;
68 else
69 ts->monotonic = 0;
70 }
71
72 return ts;
73}
74
cae0c5e0
LP
75dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
76 int64_t delta;
77 assert(ts);
78
3a43da28
KS
79 if (u == USEC_INFINITY) {
80 ts->realtime = ts->monotonic = USEC_INFINITY;
cae0c5e0
LP
81 return ts;
82 }
83
84 ts->monotonic = u;
85 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
86
87 ts->realtime = now(CLOCK_REALTIME);
88 if ((int64_t) ts->realtime > delta)
89 ts->realtime -= delta;
90 else
91 ts->realtime = 0;
92
93 return ts;
94}
95
9a98c7a1
LP
96usec_t timespec_load(const struct timespec *ts) {
97 assert(ts);
98
99 if (ts->tv_sec == (time_t) -1 &&
100 ts->tv_nsec == (long) -1)
3a43da28 101 return USEC_INFINITY;
9a98c7a1
LP
102
103 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
3a43da28 104 return USEC_INFINITY;
9a98c7a1
LP
105
106 return
107 (usec_t) ts->tv_sec * USEC_PER_SEC +
108 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
109}
110
111struct timespec *timespec_store(struct timespec *ts, usec_t u) {
112 assert(ts);
113
3a43da28 114 if (u == USEC_INFINITY) {
9a98c7a1
LP
115 ts->tv_sec = (time_t) -1;
116 ts->tv_nsec = (long) -1;
117 return ts;
118 }
119
120 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
121 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
122
123 return ts;
124}
125
126usec_t timeval_load(const struct timeval *tv) {
127 assert(tv);
128
129 if (tv->tv_sec == (time_t) -1 &&
130 tv->tv_usec == (suseconds_t) -1)
3a43da28 131 return USEC_INFINITY;
9a98c7a1
LP
132
133 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
3a43da28 134 return USEC_INFINITY;
9a98c7a1
LP
135
136 return
137 (usec_t) tv->tv_sec * USEC_PER_SEC +
138 (usec_t) tv->tv_usec;
139}
140
141struct timeval *timeval_store(struct timeval *tv, usec_t u) {
142 assert(tv);
143
3a43da28 144 if (u == USEC_INFINITY) {
9a98c7a1
LP
145 tv->tv_sec = (time_t) -1;
146 tv->tv_usec = (suseconds_t) -1;
4d89874a
ZJS
147 } else {
148 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
149 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
150 }
151
9a98c7a1
LP
152 return tv;
153}
154
5ab99e07 155static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
9a98c7a1
LP
156 struct tm tm;
157 time_t sec;
158
159 assert(buf);
160 assert(l > 0);
161
3a43da28 162 if (t <= 0 || t == USEC_INFINITY)
9a98c7a1
LP
163 return NULL;
164
165 sec = (time_t) (t / USEC_PER_SEC);
166
a62e83b4
JS
167 if (utc)
168 gmtime_r(&sec, &tm);
169 else
170 localtime_r(&sec, &tm);
171 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
9a98c7a1
LP
172 return NULL;
173
174 return buf;
175}
176
a62e83b4
JS
177char *format_timestamp(char *buf, size_t l, usec_t t) {
178 return format_timestamp_internal(buf, l, t, false);
179}
180
5ab99e07
LP
181char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
182 return format_timestamp_internal(buf, l, t, true);
183}
184
185static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
f02d8367
ZJS
186 struct tm tm;
187 time_t sec;
188
189 assert(buf);
190 assert(l > 0);
191
3a43da28 192 if (t <= 0 || t == USEC_INFINITY)
f02d8367
ZJS
193 return NULL;
194
195 sec = (time_t) (t / USEC_PER_SEC);
a62e83b4
JS
196 if (utc)
197 gmtime_r(&sec, &tm);
198 else
199 localtime_r(&sec, &tm);
f02d8367
ZJS
200
201 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
202 return NULL;
609e002e 203 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
f02d8367
ZJS
204 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
205 return NULL;
206
207 return buf;
208}
209
5ab99e07
LP
210char *format_timestamp_us(char *buf, size_t l, usec_t t) {
211 return format_timestamp_internal_us(buf, l, t, false);
212}
213
214char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
215 return format_timestamp_internal_us(buf, l, t, true);
216}
217
bbb8486e 218char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 219 const char *s;
9a98c7a1
LP
220 usec_t n, d;
221
65de0395 222 if (t <= 0 || t == USEC_INFINITY)
9a98c7a1
LP
223 return NULL;
224
65de0395 225 n = now(CLOCK_REALTIME);
1fcf71f5
LP
226 if (n > t) {
227 d = n - t;
228 s = "ago";
229 } else {
230 d = t - n;
231 s = "left";
232 }
9a98c7a1
LP
233
234 if (d >= USEC_PER_YEAR)
609e002e 235 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
de0671ee
ZJS
236 d / USEC_PER_YEAR,
237 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
9a98c7a1 238 else if (d >= USEC_PER_MONTH)
609e002e 239 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
de0671ee
ZJS
240 d / USEC_PER_MONTH,
241 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
9a98c7a1 242 else if (d >= USEC_PER_WEEK)
609e002e 243 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
de0671ee
ZJS
244 d / USEC_PER_WEEK,
245 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
9a98c7a1 246 else if (d >= 2*USEC_PER_DAY)
609e002e 247 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
9a98c7a1 248 else if (d >= 25*USEC_PER_HOUR)
609e002e 249 snprintf(buf, l, "1 day " USEC_FMT "h %s",
de0671ee 250 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
9a98c7a1 251 else if (d >= 6*USEC_PER_HOUR)
609e002e 252 snprintf(buf, l, USEC_FMT "h %s",
de0671ee 253 d / USEC_PER_HOUR, s);
9a98c7a1 254 else if (d >= USEC_PER_HOUR)
609e002e 255 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
de0671ee
ZJS
256 d / USEC_PER_HOUR,
257 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
9a98c7a1 258 else if (d >= 5*USEC_PER_MINUTE)
609e002e 259 snprintf(buf, l, USEC_FMT "min %s",
de0671ee 260 d / USEC_PER_MINUTE, s);
9a98c7a1 261 else if (d >= USEC_PER_MINUTE)
609e002e 262 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
de0671ee
ZJS
263 d / USEC_PER_MINUTE,
264 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
9a98c7a1 265 else if (d >= USEC_PER_SEC)
609e002e 266 snprintf(buf, l, USEC_FMT "s %s",
de0671ee 267 d / USEC_PER_SEC, s);
9a98c7a1 268 else if (d >= USEC_PER_MSEC)
609e002e 269 snprintf(buf, l, USEC_FMT "ms %s",
de0671ee 270 d / USEC_PER_MSEC, s);
9a98c7a1 271 else if (d > 0)
de0671ee
ZJS
272 snprintf(buf, l, USEC_FMT"us %s",
273 d, s);
9a98c7a1
LP
274 else
275 snprintf(buf, l, "now");
276
277 buf[l-1] = 0;
278 return buf;
279}
280
2fa4092c 281char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
282 static const struct {
283 const char *suffix;
284 usec_t usec;
285 } table[] = {
b719810d
LP
286 { "y", USEC_PER_YEAR },
287 { "month", USEC_PER_MONTH },
9a98c7a1
LP
288 { "w", USEC_PER_WEEK },
289 { "d", USEC_PER_DAY },
290 { "h", USEC_PER_HOUR },
291 { "min", USEC_PER_MINUTE },
292 { "s", USEC_PER_SEC },
293 { "ms", USEC_PER_MSEC },
294 { "us", 1 },
295 };
296
297 unsigned i;
298 char *p = buf;
2fa4092c 299 bool something = false;
9a98c7a1
LP
300
301 assert(buf);
302 assert(l > 0);
303
b1d6dcf5
ZJS
304 if (t == USEC_INFINITY || t <= 0) {
305 strncpy(p, t == USEC_INFINITY ? "infinity" : "0", l);
7c537b2e
LP
306 p[l-1] = 0;
307 return p;
308 }
309
7f602784 310 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
311
312 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 313 int k = 0;
9a98c7a1 314 size_t n;
2fa4092c
LP
315 bool done = false;
316 usec_t a, b;
317
7c537b2e
LP
318 if (t <= 0)
319 break;
2fa4092c 320
7c537b2e 321 if (t < accuracy && something)
2fa4092c 322 break;
9a98c7a1
LP
323
324 if (t < table[i].usec)
325 continue;
326
327 if (l <= 1)
328 break;
329
2fa4092c
LP
330 a = t / table[i].usec;
331 b = t % table[i].usec;
332
333 /* Let's see if we should shows this in dot notation */
334 if (t < USEC_PER_MINUTE && b > 0) {
335 usec_t cc;
336 int j;
337
338 j = 0;
339 for (cc = table[i].usec; cc > 1; cc /= 10)
340 j++;
341
342 for (cc = accuracy; cc > 1; cc /= 10) {
343 b /= 10;
344 j--;
345 }
346
347 if (j > 0) {
348 k = snprintf(p, l,
de0671ee 349 "%s"USEC_FMT".%0*llu%s",
2fa4092c 350 p > buf ? " " : "",
de0671ee 351 a,
2fa4092c
LP
352 j,
353 (unsigned long long) b,
354 table[i].suffix);
355
356 t = 0;
357 done = true;
358 }
359 }
360
361 /* No? Then let's show it normally */
362 if (!done) {
363 k = snprintf(p, l,
de0671ee 364 "%s"USEC_FMT"%s",
2fa4092c 365 p > buf ? " " : "",
de0671ee 366 a,
2fa4092c
LP
367 table[i].suffix);
368
369 t = b;
370 }
371
9a98c7a1
LP
372 n = MIN((size_t) k, l);
373
374 l -= n;
375 p += n;
376
2fa4092c 377 something = true;
9a98c7a1
LP
378 }
379
380 *p = 0;
381
382 return buf;
383}
384
385void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
386
387 assert(f);
388 assert(name);
389 assert(t);
390
391 if (!dual_timestamp_is_set(t))
392 return;
393
de0671ee 394 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
9a98c7a1 395 name,
de0671ee
ZJS
396 t->realtime,
397 t->monotonic);
9a98c7a1
LP
398}
399
400void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
401 unsigned long long a, b;
402
403 assert(value);
404 assert(t);
405
b5dd8148 406 if (sscanf(value, "%llu %llu", &a, &b) != 2)
9a98c7a1
LP
407 log_debug("Failed to parse finish timestamp value %s", value);
408 else {
409 t->realtime = a;
410 t->monotonic = b;
411 }
412}
413
414int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
415 static const struct {
416 const char *name;
417 const int nr;
418 } day_nr[] = {
419 { "Sunday", 0 },
420 { "Sun", 0 },
421 { "Monday", 1 },
422 { "Mon", 1 },
423 { "Tuesday", 2 },
424 { "Tue", 2 },
425 { "Wednesday", 3 },
426 { "Wed", 3 },
427 { "Thursday", 4 },
428 { "Thu", 4 },
429 { "Friday", 5 },
430 { "Fri", 5 },
431 { "Saturday", 6 },
432 { "Sat", 6 },
433 };
434
9a98c7a1
LP
435 const char *k;
436 struct tm tm, copy;
437 time_t x;
438 usec_t plus = 0, minus = 0, ret;
92134489
LP
439 int r, weekday = -1;
440 unsigned i;
9a98c7a1
LP
441
442 /*
443 * Allowed syntaxes:
444 *
445 * 2012-09-22 16:34:22
446 * 2012-09-22 16:34 (seconds will be set to 0)
447 * 2012-09-22 (time will be set to 00:00:00)
448 * 16:34:22 (date will be set to today)
449 * 16:34 (date will be set to today, seconds to 0)
450 * now
451 * yesterday (time is set to 00:00:00)
452 * today (time is set to 00:00:00)
453 * tomorrow (time is set to 00:00:00)
454 * +5min
455 * -5days
5ba6e094 456 * @2147483647 (seconds since epoch)
9a98c7a1
LP
457 *
458 */
459
460 assert(t);
461 assert(usec);
462
463 x = time(NULL);
464 assert_se(localtime_r(&x, &tm));
6a741b4a 465 tm.tm_isdst = -1;
9a98c7a1
LP
466
467 if (streq(t, "now"))
468 goto finish;
469
470 else if (streq(t, "today")) {
471 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
472 goto finish;
473
474 } else if (streq(t, "yesterday")) {
475 tm.tm_mday --;
476 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
477 goto finish;
478
479 } else if (streq(t, "tomorrow")) {
480 tm.tm_mday ++;
481 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
482 goto finish;
483
484 } else if (t[0] == '+') {
7f602784 485 r = parse_sec(t+1, &plus);
9a98c7a1
LP
486 if (r < 0)
487 return r;
488
489 goto finish;
9a98c7a1 490
5ba6e094 491 } else if (t[0] == '-') {
7f602784 492 r = parse_sec(t+1, &minus);
9a98c7a1
LP
493 if (r < 0)
494 return r;
495
496 goto finish;
decad910 497
5ba6e094
LP
498 } else if (t[0] == '@')
499 return parse_sec(t + 1, usec);
500
501 else if (endswith(t, " ago")) {
decad910
LP
502 _cleanup_free_ char *z;
503
504 z = strndup(t, strlen(t) - 4);
505 if (!z)
506 return -ENOMEM;
507
7f602784 508 r = parse_sec(z, &minus);
decad910
LP
509 if (r < 0)
510 return r;
511
1fcf71f5
LP
512 goto finish;
513 } else if (endswith(t, " left")) {
514 _cleanup_free_ char *z;
515
516 z = strndup(t, strlen(t) - 4);
517 if (!z)
518 return -ENOMEM;
519
520 r = parse_sec(z, &plus);
521 if (r < 0)
522 return r;
523
decad910 524 goto finish;
9a98c7a1
LP
525 }
526
92134489
LP
527 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
528 size_t skip;
529
530 if (!startswith_no_case(t, day_nr[i].name))
531 continue;
532
533 skip = strlen(day_nr[i].name);
534 if (t[skip] != ' ')
535 continue;
536
537 weekday = day_nr[i].nr;
538 t += skip + 1;
539 break;
540 }
541
9a98c7a1
LP
542 copy = tm;
543 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
544 if (k && *k == 0)
545 goto finish;
546
547 tm = copy;
548 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
549 if (k && *k == 0)
550 goto finish;
551
552 tm = copy;
553 k = strptime(t, "%y-%m-%d %H:%M", &tm);
554 if (k && *k == 0) {
555 tm.tm_sec = 0;
556 goto finish;
557 }
558
559 tm = copy;
560 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
561 if (k && *k == 0) {
562 tm.tm_sec = 0;
563 goto finish;
564 }
565
566 tm = copy;
567 k = strptime(t, "%y-%m-%d", &tm);
568 if (k && *k == 0) {
569 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
570 goto finish;
571 }
572
573 tm = copy;
574 k = strptime(t, "%Y-%m-%d", &tm);
575 if (k && *k == 0) {
576 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
577 goto finish;
578 }
579
580 tm = copy;
581 k = strptime(t, "%H:%M:%S", &tm);
582 if (k && *k == 0)
583 goto finish;
584
585 tm = copy;
586 k = strptime(t, "%H:%M", &tm);
587 if (k && *k == 0) {
588 tm.tm_sec = 0;
589 goto finish;
590 }
591
592 return -EINVAL;
593
594finish:
595 x = mktime(&tm);
596 if (x == (time_t) -1)
597 return -EINVAL;
598
92134489
LP
599 if (weekday >= 0 && tm.tm_wday != weekday)
600 return -EINVAL;
601
9a98c7a1
LP
602 ret = (usec_t) x * USEC_PER_SEC;
603
604 ret += plus;
605 if (ret > minus)
606 ret -= minus;
607 else
608 ret = 0;
609
610 *usec = ret;
611
612 return 0;
613}
614
7f602784 615int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
616 static const struct {
617 const char *suffix;
618 usec_t usec;
619 } table[] = {
620 { "seconds", USEC_PER_SEC },
621 { "second", USEC_PER_SEC },
622 { "sec", USEC_PER_SEC },
623 { "s", USEC_PER_SEC },
624 { "minutes", USEC_PER_MINUTE },
625 { "minute", USEC_PER_MINUTE },
626 { "min", USEC_PER_MINUTE },
627 { "months", USEC_PER_MONTH },
628 { "month", USEC_PER_MONTH },
629 { "msec", USEC_PER_MSEC },
630 { "ms", USEC_PER_MSEC },
631 { "m", USEC_PER_MINUTE },
632 { "hours", USEC_PER_HOUR },
633 { "hour", USEC_PER_HOUR },
634 { "hr", USEC_PER_HOUR },
635 { "h", USEC_PER_HOUR },
636 { "days", USEC_PER_DAY },
637 { "day", USEC_PER_DAY },
638 { "d", USEC_PER_DAY },
639 { "weeks", USEC_PER_WEEK },
640 { "week", USEC_PER_WEEK },
641 { "w", USEC_PER_WEEK },
642 { "years", USEC_PER_YEAR },
643 { "year", USEC_PER_YEAR },
644 { "y", USEC_PER_YEAR },
645 { "usec", 1ULL },
646 { "us", 1ULL },
647 { "", USEC_PER_SEC }, /* default is sec */
648 };
649
b1d6dcf5 650 const char *p, *s;
9a98c7a1 651 usec_t r = 0;
cb0dac05 652 bool something = false;
9a98c7a1
LP
653
654 assert(t);
655 assert(usec);
656
657 p = t;
b1d6dcf5
ZJS
658
659 p += strspn(p, WHITESPACE);
660 s = startswith(p, "infinity");
661 if (s) {
662 s += strspn(s, WHITESPACE);
663 if (*s != 0)
664 return -EINVAL;
665
666 *usec = USEC_INFINITY;
667 return 0;
668 }
669
cb0dac05
LP
670 for (;;) {
671 long long l, z = 0;
9a98c7a1 672 char *e;
cb0dac05
LP
673 unsigned i, n = 0;
674
675 p += strspn(p, WHITESPACE);
676
677 if (*p == 0) {
678 if (!something)
679 return -EINVAL;
680
681 break;
682 }
9a98c7a1
LP
683
684 errno = 0;
685 l = strtoll(p, &e, 10);
686
8333c77e 687 if (errno > 0)
9a98c7a1
LP
688 return -errno;
689
690 if (l < 0)
691 return -ERANGE;
692
cb0dac05
LP
693 if (*e == '.') {
694 char *b = e + 1;
695
696 errno = 0;
697 z = strtoll(b, &e, 10);
698 if (errno > 0)
699 return -errno;
700
701 if (z < 0)
702 return -ERANGE;
703
704 if (e == b)
705 return -EINVAL;
706
707 n = e - b;
708
709 } else if (e == p)
9a98c7a1
LP
710 return -EINVAL;
711
712 e += strspn(e, WHITESPACE);
713
714 for (i = 0; i < ELEMENTSOF(table); i++)
715 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
716 usec_t k = (usec_t) z * table[i].usec;
717
718 for (; n > 0; n--)
719 k /= 10;
720
721 r += (usec_t) l * table[i].usec + k;
9a98c7a1 722 p = e + strlen(table[i].suffix);
cb0dac05
LP
723
724 something = true;
9a98c7a1
LP
725 break;
726 }
727
728 if (i >= ELEMENTSOF(table))
729 return -EINVAL;
730
cb0dac05 731 }
9a98c7a1
LP
732
733 *usec = r;
734
735 return 0;
736}
737
738int parse_nsec(const char *t, nsec_t *nsec) {
739 static const struct {
740 const char *suffix;
741 nsec_t nsec;
742 } table[] = {
743 { "seconds", NSEC_PER_SEC },
744 { "second", NSEC_PER_SEC },
745 { "sec", NSEC_PER_SEC },
746 { "s", NSEC_PER_SEC },
747 { "minutes", NSEC_PER_MINUTE },
748 { "minute", NSEC_PER_MINUTE },
749 { "min", NSEC_PER_MINUTE },
750 { "months", NSEC_PER_MONTH },
751 { "month", NSEC_PER_MONTH },
752 { "msec", NSEC_PER_MSEC },
753 { "ms", NSEC_PER_MSEC },
754 { "m", NSEC_PER_MINUTE },
755 { "hours", NSEC_PER_HOUR },
756 { "hour", NSEC_PER_HOUR },
757 { "hr", NSEC_PER_HOUR },
758 { "h", NSEC_PER_HOUR },
759 { "days", NSEC_PER_DAY },
760 { "day", NSEC_PER_DAY },
761 { "d", NSEC_PER_DAY },
762 { "weeks", NSEC_PER_WEEK },
763 { "week", NSEC_PER_WEEK },
764 { "w", NSEC_PER_WEEK },
765 { "years", NSEC_PER_YEAR },
766 { "year", NSEC_PER_YEAR },
767 { "y", NSEC_PER_YEAR },
768 { "usec", NSEC_PER_USEC },
769 { "us", NSEC_PER_USEC },
770 { "nsec", 1ULL },
771 { "ns", 1ULL },
772 { "", 1ULL }, /* default is nsec */
773 };
774
e73c78c2 775 const char *p, *s;
9a98c7a1 776 nsec_t r = 0;
cb0dac05 777 bool something = false;
9a98c7a1
LP
778
779 assert(t);
780 assert(nsec);
781
782 p = t;
e73c78c2
LP
783
784 p += strspn(p, WHITESPACE);
785 s = startswith(p, "infinity");
786 if (s) {
787 s += strspn(s, WHITESPACE);
788 if (!*s != 0)
789 return -EINVAL;
790
791 *nsec = NSEC_INFINITY;
792 return 0;
793 }
794
cb0dac05
LP
795 for (;;) {
796 long long l, z = 0;
9a98c7a1 797 char *e;
cb0dac05
LP
798 unsigned i, n = 0;
799
800 p += strspn(p, WHITESPACE);
801
802 if (*p == 0) {
803 if (!something)
804 return -EINVAL;
805
806 break;
807 }
9a98c7a1
LP
808
809 errno = 0;
810 l = strtoll(p, &e, 10);
811
8333c77e 812 if (errno > 0)
9a98c7a1
LP
813 return -errno;
814
815 if (l < 0)
816 return -ERANGE;
817
cb0dac05
LP
818 if (*e == '.') {
819 char *b = e + 1;
820
821 errno = 0;
822 z = strtoll(b, &e, 10);
823 if (errno > 0)
824 return -errno;
825
826 if (z < 0)
827 return -ERANGE;
828
829 if (e == b)
830 return -EINVAL;
831
832 n = e - b;
833
834 } else if (e == p)
9a98c7a1
LP
835 return -EINVAL;
836
837 e += strspn(e, WHITESPACE);
838
839 for (i = 0; i < ELEMENTSOF(table); i++)
840 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
841 nsec_t k = (nsec_t) z * table[i].nsec;
842
843 for (; n > 0; n--)
844 k /= 10;
845
846 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 847 p = e + strlen(table[i].suffix);
cb0dac05
LP
848
849 something = true;
9a98c7a1
LP
850 break;
851 }
852
853 if (i >= ELEMENTSOF(table))
854 return -EINVAL;
855
cb0dac05 856 }
9a98c7a1
LP
857
858 *nsec = r;
859
860 return 0;
861}
03cc26dd
LP
862
863bool ntp_synced(void) {
864 struct timex txc = {};
865
866 if (adjtimex(&txc) < 0)
867 return false;
868
869 if (txc.status & STA_UNSYNC)
870 return false;
871
872 return true;
873}
75683450
LP
874
875int get_timezones(char ***ret) {
876 _cleanup_fclose_ FILE *f = NULL;
877 _cleanup_strv_free_ char **zones = NULL;
878 size_t n_zones = 0, n_allocated = 0;
879
880 assert(ret);
881
882 zones = strv_new("UTC", NULL);
883 if (!zones)
884 return -ENOMEM;
885
886 n_allocated = 2;
887 n_zones = 1;
888
889 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
890 if (f) {
891 char l[LINE_MAX];
892
893 FOREACH_LINE(l, f, return -errno) {
894 char *p, *w;
895 size_t k;
896
897 p = strstrip(l);
898
899 if (isempty(p) || *p == '#')
900 continue;
901
902 /* Skip over country code */
903 p += strcspn(p, WHITESPACE);
904 p += strspn(p, WHITESPACE);
905
906 /* Skip over coordinates */
907 p += strcspn(p, WHITESPACE);
908 p += strspn(p, WHITESPACE);
909
910 /* Found timezone name */
911 k = strcspn(p, WHITESPACE);
912 if (k <= 0)
913 continue;
914
915 w = strndup(p, k);
916 if (!w)
917 return -ENOMEM;
918
919 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
920 free(w);
921 return -ENOMEM;
922 }
923
924 zones[n_zones++] = w;
925 zones[n_zones] = NULL;
926 }
927
928 strv_sort(zones);
929
930 } else if (errno != ENOENT)
931 return -errno;
932
933 *ret = zones;
934 zones = NULL;
935
936 return 0;
937}
938
939bool timezone_is_valid(const char *name) {
940 bool slash = false;
941 const char *p, *t;
942 struct stat st;
943
944 if (!name || *name == 0 || *name == '/')
945 return false;
946
947 for (p = name; *p; p++) {
948 if (!(*p >= '0' && *p <= '9') &&
949 !(*p >= 'a' && *p <= 'z') &&
950 !(*p >= 'A' && *p <= 'Z') &&
951 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
952 return false;
953
954 if (*p == '/') {
955
956 if (slash)
957 return false;
958
959 slash = true;
960 } else
961 slash = false;
962 }
963
964 if (slash)
965 return false;
966
967 t = strappenda("/usr/share/zoneinfo/", name);
968 if (stat(t, &st) < 0)
969 return false;
970
971 if (!S_ISREG(st.st_mode))
972 return false;
973
974 return true;
975}
77ff2de9
TG
976
977clockid_t clock_boottime_or_monotonic(void) {
978 static clockid_t clock = -1;
979 int fd;
980
981 if (clock != -1)
982 return clock;
983
984 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
985 if (fd < 0)
986 clock = CLOCK_MONOTONIC;
987 else {
988 safe_close(fd);
989 clock = CLOCK_BOOTTIME;
990 }
991
992 return clock;
993}