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