]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/time-util.c
network: fix typo in log message
[thirdparty/systemd.git] / src / basic / time-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9a98c7a1
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
11c3a366
TA
21#include <errno.h>
22#include <limits.h>
23#include <stdlib.h>
9a98c7a1 24#include <string.h>
48d26c01 25#include <sys/mman.h>
11c3a366
TA
26#include <sys/stat.h>
27#include <sys/time.h>
77ff2de9 28#include <sys/timerfd.h>
07630cea 29#include <sys/timex.h>
11c3a366
TA
30#include <sys/types.h>
31#include <unistd.h>
9a98c7a1 32
b5efdb8a 33#include "alloc-util.h"
3ffd4af2 34#include "fd-util.h"
0d39fa9c 35#include "fileio.h"
f4f15635 36#include "fs-util.h"
11c3a366
TA
37#include "log.h"
38#include "macro.h"
93cc7779
TA
39#include "parse-util.h"
40#include "path-util.h"
dccca82b 41#include "process-util.h"
07630cea 42#include "string-util.h"
75683450 43#include "strv.h"
07630cea 44#include "time-util.h"
9a98c7a1 45
32c1f5a5
LP
46static clockid_t map_clock_id(clockid_t c) {
47
48 /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
49 * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
50 * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
bdf19f8f 51 * those archs. */
32c1f5a5
LP
52
53 switch (c) {
54
55 case CLOCK_BOOTTIME_ALARM:
bdf19f8f 56 return CLOCK_BOOTTIME;
32c1f5a5
LP
57
58 case CLOCK_REALTIME_ALARM:
59 return CLOCK_REALTIME;
60
61 default:
62 return c;
63 }
64}
65
9a98c7a1
LP
66usec_t now(clockid_t clock_id) {
67 struct timespec ts;
68
32c1f5a5 69 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
9a98c7a1
LP
70
71 return timespec_load(&ts);
72}
73
45d7a8bb
LP
74nsec_t now_nsec(clockid_t clock_id) {
75 struct timespec ts;
76
32c1f5a5 77 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
45d7a8bb
LP
78
79 return timespec_load_nsec(&ts);
80}
81
9a98c7a1
LP
82dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
83 assert(ts);
84
85 ts->realtime = now(CLOCK_REALTIME);
86 ts->monotonic = now(CLOCK_MONOTONIC);
87
88 return ts;
89}
90
fe624c4c
LP
91triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
92 assert(ts);
93
94 ts->realtime = now(CLOCK_REALTIME);
95 ts->monotonic = now(CLOCK_MONOTONIC);
96 ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
97
98 return ts;
99}
100
9a98c7a1
LP
101dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
102 int64_t delta;
103 assert(ts);
104
75a5f1d8
LP
105 if (u == USEC_INFINITY || u <= 0) {
106 ts->realtime = ts->monotonic = u;
cae0c5e0
LP
107 return ts;
108 }
109
9a98c7a1
LP
110 ts->realtime = u;
111
75a5f1d8 112 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
54d8ef14 113 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
9a98c7a1
LP
114
115 return ts;
116}
117
fe624c4c
LP
118triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
119 int64_t delta;
120
121 assert(ts);
122
123 if (u == USEC_INFINITY || u <= 0) {
124 ts->realtime = ts->monotonic = ts->boottime = u;
125 return ts;
126 }
127
128 ts->realtime = u;
129 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
54d8ef14
LP
130 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
131 ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
fe624c4c
LP
132
133 return ts;
134}
135
cae0c5e0
LP
136dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
137 int64_t delta;
138 assert(ts);
139
3a43da28
KS
140 if (u == USEC_INFINITY) {
141 ts->realtime = ts->monotonic = USEC_INFINITY;
cae0c5e0
LP
142 return ts;
143 }
144
145 ts->monotonic = u;
146 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
54d8ef14 147 ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta);
cae0c5e0
LP
148
149 return ts;
150}
151
fbe55073
LP
152dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
153 int64_t delta;
154
155 if (u == USEC_INFINITY) {
156 ts->realtime = ts->monotonic = USEC_INFINITY;
157 return ts;
158 }
fbe55073 159
0345d252 160 dual_timestamp_get(ts);
fbe55073 161 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
54d8ef14
LP
162 ts->realtime = usec_sub_signed(ts->realtime, delta);
163 ts->monotonic = usec_sub_signed(ts->monotonic, delta);
fbe55073
LP
164
165 return ts;
166}
167
fe624c4c
LP
168usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
169
170 switch (clock) {
171
172 case CLOCK_REALTIME:
173 case CLOCK_REALTIME_ALARM:
174 return ts->realtime;
175
176 case CLOCK_MONOTONIC:
177 return ts->monotonic;
178
179 case CLOCK_BOOTTIME:
180 case CLOCK_BOOTTIME_ALARM:
181 return ts->boottime;
182
183 default:
184 return USEC_INFINITY;
185 }
186}
187
9a98c7a1
LP
188usec_t timespec_load(const struct timespec *ts) {
189 assert(ts);
190
c477ff14 191 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
3a43da28 192 return USEC_INFINITY;
9a98c7a1
LP
193
194 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
3a43da28 195 return USEC_INFINITY;
9a98c7a1
LP
196
197 return
198 (usec_t) ts->tv_sec * USEC_PER_SEC +
199 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
200}
201
3a730176 202nsec_t timespec_load_nsec(const struct timespec *ts) {
45d7a8bb
LP
203 assert(ts);
204
c477ff14 205 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
45d7a8bb
LP
206 return NSEC_INFINITY;
207
a2daa2f0
ZJS
208 if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
209 return NSEC_INFINITY;
210
211 return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
45d7a8bb
LP
212}
213
9a98c7a1
LP
214struct timespec *timespec_store(struct timespec *ts, usec_t u) {
215 assert(ts);
216
f977849c 217 if (u == USEC_INFINITY ||
d201d908 218 u / USEC_PER_SEC >= TIME_T_MAX) {
9a98c7a1
LP
219 ts->tv_sec = (time_t) -1;
220 ts->tv_nsec = (long) -1;
221 return ts;
222 }
223
224 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
225 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
226
227 return ts;
228}
229
230usec_t timeval_load(const struct timeval *tv) {
231 assert(tv);
232
c477ff14 233 if (tv->tv_sec < 0 || tv->tv_usec < 0)
3a43da28 234 return USEC_INFINITY;
9a98c7a1
LP
235
236 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
3a43da28 237 return USEC_INFINITY;
9a98c7a1
LP
238
239 return
240 (usec_t) tv->tv_sec * USEC_PER_SEC +
241 (usec_t) tv->tv_usec;
242}
243
244struct timeval *timeval_store(struct timeval *tv, usec_t u) {
245 assert(tv);
246
68bdd2d2 247 if (u == USEC_INFINITY ||
f977849c 248 u / USEC_PER_SEC > TIME_T_MAX) {
9a98c7a1
LP
249 tv->tv_sec = (time_t) -1;
250 tv->tv_usec = (suseconds_t) -1;
4d89874a
ZJS
251 } else {
252 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
253 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
254 }
255
9a98c7a1
LP
256 return tv;
257}
258
21b3a0fc
LP
259static char *format_timestamp_internal(
260 char *buf,
261 size_t l,
262 usec_t t,
263 bool utc,
264 bool us) {
265
266 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
267 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
268 static const char * const weekdays[] = {
269 [0] = "Sun",
270 [1] = "Mon",
271 [2] = "Tue",
272 [3] = "Wed",
273 [4] = "Thu",
274 [5] = "Fri",
275 [6] = "Sat",
276 };
277
9a98c7a1
LP
278 struct tm tm;
279 time_t sec;
21b3a0fc 280 size_t n;
9a98c7a1
LP
281
282 assert(buf);
9a98c7a1 283
21b3a0fc
LP
284 if (l <
285 3 + /* week day */
286 1 + 10 + /* space and date */
287 1 + 8 + /* space and time */
288 (us ? 1 + 6 : 0) + /* "." and microsecond part */
289 1 + 1 + /* space and shortest possible zone */
290 1)
291 return NULL; /* Not enough space even for the shortest form. */
3a43da28 292 if (t <= 0 || t == USEC_INFINITY)
21b3a0fc
LP
293 return NULL; /* Timestamp is unset */
294
1bb4b028
LP
295 /* Let's not format times with years > 9999 */
296 if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
297 return NULL;
298
21b3a0fc 299 sec = (time_t) (t / USEC_PER_SEC); /* Round down */
21b3a0fc
LP
300
301 if (!localtime_or_gmtime_r(&sec, &tm, utc))
9a98c7a1
LP
302 return NULL;
303
21b3a0fc
LP
304 /* Start with the week day */
305 assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
306 memcpy(buf, weekdays[tm.tm_wday], 4);
9a98c7a1 307
21b3a0fc
LP
308 /* Add the main components */
309 if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
310 return NULL; /* Doesn't fit */
0056086a 311
21b3a0fc 312 /* Append the microseconds part, if that's requested */
0056086a 313 if (us) {
21b3a0fc
LP
314 n = strlen(buf);
315 if (n + 8 > l)
316 return NULL; /* Microseconds part doesn't fit. */
317
70887c5f 318 sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
21b3a0fc
LP
319 }
320
321 /* Append the timezone */
322 n = strlen(buf);
323 if (utc) {
324 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
325 * obsolete "GMT" instead. */
326 if (n + 5 > l)
327 return NULL; /* "UTC" doesn't fit. */
328
329 strcpy(buf + n, " UTC");
330
331 } else if (!isempty(tm.tm_zone)) {
332 size_t tn;
333
334 /* An explicit timezone is specified, let's use it, if it fits */
335 tn = strlen(tm.tm_zone);
336 if (n + 1 + tn + 1 > l) {
337 /* The full time zone does not fit in. Yuck. */
338
339 if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
340 return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
341
342 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
343 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
344 * an overly long, hard to read string on the user. This should be safe, because the user will
345 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
346 } else {
347 buf[n++] = ' ';
348 strcpy(buf + n, tm.tm_zone);
349 }
0056086a 350 }
9a98c7a1
LP
351
352 return buf;
353}
354
a62e83b4 355char *format_timestamp(char *buf, size_t l, usec_t t) {
0056086a 356 return format_timestamp_internal(buf, l, t, false, false);
a62e83b4
JS
357}
358
5ab99e07 359char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
0056086a 360 return format_timestamp_internal(buf, l, t, true, false);
f02d8367
ZJS
361}
362
5ab99e07 363char *format_timestamp_us(char *buf, size_t l, usec_t t) {
0056086a 364 return format_timestamp_internal(buf, l, t, false, true);
5ab99e07
LP
365}
366
367char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
0056086a 368 return format_timestamp_internal(buf, l, t, true, true);
5ab99e07
LP
369}
370
bbb8486e 371char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 372 const char *s;
9a98c7a1
LP
373 usec_t n, d;
374
65de0395 375 if (t <= 0 || t == USEC_INFINITY)
9a98c7a1
LP
376 return NULL;
377
65de0395 378 n = now(CLOCK_REALTIME);
1fcf71f5
LP
379 if (n > t) {
380 d = n - t;
381 s = "ago";
382 } else {
383 d = t - n;
384 s = "left";
385 }
9a98c7a1
LP
386
387 if (d >= USEC_PER_YEAR)
609e002e 388 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
de0671ee
ZJS
389 d / USEC_PER_YEAR,
390 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
9a98c7a1 391 else if (d >= USEC_PER_MONTH)
609e002e 392 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
de0671ee
ZJS
393 d / USEC_PER_MONTH,
394 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
9a98c7a1 395 else if (d >= USEC_PER_WEEK)
609e002e 396 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
de0671ee
ZJS
397 d / USEC_PER_WEEK,
398 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
9a98c7a1 399 else if (d >= 2*USEC_PER_DAY)
609e002e 400 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
9a98c7a1 401 else if (d >= 25*USEC_PER_HOUR)
609e002e 402 snprintf(buf, l, "1 day " USEC_FMT "h %s",
de0671ee 403 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
9a98c7a1 404 else if (d >= 6*USEC_PER_HOUR)
609e002e 405 snprintf(buf, l, USEC_FMT "h %s",
de0671ee 406 d / USEC_PER_HOUR, s);
9a98c7a1 407 else if (d >= USEC_PER_HOUR)
609e002e 408 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
de0671ee
ZJS
409 d / USEC_PER_HOUR,
410 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
9a98c7a1 411 else if (d >= 5*USEC_PER_MINUTE)
609e002e 412 snprintf(buf, l, USEC_FMT "min %s",
de0671ee 413 d / USEC_PER_MINUTE, s);
9a98c7a1 414 else if (d >= USEC_PER_MINUTE)
609e002e 415 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
de0671ee
ZJS
416 d / USEC_PER_MINUTE,
417 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
9a98c7a1 418 else if (d >= USEC_PER_SEC)
609e002e 419 snprintf(buf, l, USEC_FMT "s %s",
de0671ee 420 d / USEC_PER_SEC, s);
9a98c7a1 421 else if (d >= USEC_PER_MSEC)
609e002e 422 snprintf(buf, l, USEC_FMT "ms %s",
de0671ee 423 d / USEC_PER_MSEC, s);
9a98c7a1 424 else if (d > 0)
de0671ee
ZJS
425 snprintf(buf, l, USEC_FMT"us %s",
426 d, s);
9a98c7a1
LP
427 else
428 snprintf(buf, l, "now");
429
430 buf[l-1] = 0;
431 return buf;
432}
433
2fa4092c 434char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
435 static const struct {
436 const char *suffix;
437 usec_t usec;
438 } table[] = {
eb55ec9f
LP
439 { "y", USEC_PER_YEAR },
440 { "month", USEC_PER_MONTH },
441 { "w", USEC_PER_WEEK },
442 { "d", USEC_PER_DAY },
443 { "h", USEC_PER_HOUR },
444 { "min", USEC_PER_MINUTE },
445 { "s", USEC_PER_SEC },
446 { "ms", USEC_PER_MSEC },
447 { "us", 1 },
9a98c7a1
LP
448 };
449
450 unsigned i;
451 char *p = buf;
2fa4092c 452 bool something = false;
9a98c7a1
LP
453
454 assert(buf);
455 assert(l > 0);
456
bb1fada8
LP
457 if (t == USEC_INFINITY) {
458 strncpy(p, "infinity", l-1);
459 p[l-1] = 0;
460 return p;
461 }
462
463 if (t <= 0) {
464 strncpy(p, "0", l-1);
7c537b2e
LP
465 p[l-1] = 0;
466 return p;
467 }
468
7f602784 469 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
470
471 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 472 int k = 0;
9a98c7a1 473 size_t n;
2fa4092c
LP
474 bool done = false;
475 usec_t a, b;
476
7c537b2e
LP
477 if (t <= 0)
478 break;
2fa4092c 479
7c537b2e 480 if (t < accuracy && something)
2fa4092c 481 break;
9a98c7a1
LP
482
483 if (t < table[i].usec)
484 continue;
485
486 if (l <= 1)
487 break;
488
2fa4092c
LP
489 a = t / table[i].usec;
490 b = t % table[i].usec;
491
492 /* Let's see if we should shows this in dot notation */
493 if (t < USEC_PER_MINUTE && b > 0) {
494 usec_t cc;
495 int j;
496
497 j = 0;
498 for (cc = table[i].usec; cc > 1; cc /= 10)
499 j++;
500
501 for (cc = accuracy; cc > 1; cc /= 10) {
502 b /= 10;
503 j--;
504 }
505
506 if (j > 0) {
507 k = snprintf(p, l,
70887c5f 508 "%s"USEC_FMT".%0*"PRI_USEC"%s",
2fa4092c 509 p > buf ? " " : "",
de0671ee 510 a,
2fa4092c 511 j,
70887c5f 512 b,
2fa4092c
LP
513 table[i].suffix);
514
515 t = 0;
516 done = true;
517 }
518 }
519
520 /* No? Then let's show it normally */
521 if (!done) {
522 k = snprintf(p, l,
de0671ee 523 "%s"USEC_FMT"%s",
2fa4092c 524 p > buf ? " " : "",
de0671ee 525 a,
2fa4092c
LP
526 table[i].suffix);
527
528 t = b;
529 }
530
9a98c7a1
LP
531 n = MIN((size_t) k, l);
532
533 l -= n;
534 p += n;
535
2fa4092c 536 something = true;
9a98c7a1
LP
537 }
538
539 *p = 0;
540
541 return buf;
542}
543
544void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
545
546 assert(f);
547 assert(name);
548 assert(t);
549
550 if (!dual_timestamp_is_set(t))
551 return;
552
de0671ee 553 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
9a98c7a1 554 name,
de0671ee
ZJS
555 t->realtime,
556 t->monotonic);
9a98c7a1
LP
557}
558
e911de99 559int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
74c5b33b 560 uint64_t a, b;
9c0565b2 561 int r, pos;
9a98c7a1
LP
562
563 assert(value);
564 assert(t);
565
9c0565b2
ZJS
566 pos = strspn(value, WHITESPACE);
567 if (value[pos] == '-')
568 return -EINVAL;
569 pos += strspn(value + pos, DIGITS);
570 pos += strspn(value + pos, WHITESPACE);
571 if (value[pos] == '-')
572 return -EINVAL;
573
574 r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
575 if (r != 2) {
576 log_debug("Failed to parse dual timestamp value \"%s\".", value);
e911de99 577 return -EINVAL;
9a98c7a1 578 }
e911de99 579
9c0565b2
ZJS
580 if (value[pos] != '\0')
581 /* trailing garbage */
582 return -EINVAL;
583
e911de99
LP
584 t->realtime = a;
585 t->monotonic = b;
586
587 return 0;
9a98c7a1
LP
588}
589
b895a735 590int timestamp_deserialize(const char *value, usec_t *timestamp) {
ebf30a08
AK
591 int r;
592
593 assert(value);
594
595 r = safe_atou64(value, timestamp);
ebf30a08 596 if (r < 0)
b895a735 597 return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value);
ebf30a08
AK
598
599 return r;
600}
601
48d26c01 602static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
92134489
LP
603 static const struct {
604 const char *name;
605 const int nr;
606 } day_nr[] = {
607 { "Sunday", 0 },
608 { "Sun", 0 },
609 { "Monday", 1 },
610 { "Mon", 1 },
611 { "Tuesday", 2 },
612 { "Tue", 2 },
613 { "Wednesday", 3 },
614 { "Wed", 3 },
615 { "Thursday", 4 },
616 { "Thu", 4 },
617 { "Friday", 5 },
618 { "Fri", 5 },
619 { "Saturday", 6 },
620 { "Sat", 6 },
621 };
622
48d26c01 623 const char *k, *utc = NULL, *tzn = NULL;
9a98c7a1
LP
624 struct tm tm, copy;
625 time_t x;
e4eaf99a 626 usec_t x_usec, plus = 0, minus = 0, ret;
21b3a0fc 627 int r, weekday = -1, dst = -1;
92134489 628 unsigned i;
9a98c7a1
LP
629
630 /*
631 * Allowed syntaxes:
632 *
633 * 2012-09-22 16:34:22
634 * 2012-09-22 16:34 (seconds will be set to 0)
635 * 2012-09-22 (time will be set to 00:00:00)
636 * 16:34:22 (date will be set to today)
637 * 16:34 (date will be set to today, seconds to 0)
638 * now
639 * yesterday (time is set to 00:00:00)
640 * today (time is set to 00:00:00)
641 * tomorrow (time is set to 00:00:00)
642 * +5min
643 * -5days
5ba6e094 644 * @2147483647 (seconds since epoch)
9a98c7a1
LP
645 *
646 */
647
648 assert(t);
649 assert(usec);
650
48d26c01 651 if (t[0] == '@' && !with_tz)
e4eaf99a 652 return parse_sec(t + 1, usec);
9a98c7a1 653
e4eaf99a 654 ret = now(CLOCK_REALTIME);
9a98c7a1 655
48d26c01
IK
656 if (!with_tz) {
657 if (streq(t, "now"))
658 goto finish;
9a98c7a1 659
48d26c01
IK
660 else if (t[0] == '+') {
661 r = parse_sec(t+1, &plus);
662 if (r < 0)
663 return r;
9a98c7a1 664
48d26c01 665 goto finish;
9a98c7a1 666
48d26c01
IK
667 } else if (t[0] == '-') {
668 r = parse_sec(t+1, &minus);
669 if (r < 0)
670 return r;
9a98c7a1 671
48d26c01 672 goto finish;
decad910 673
48d26c01
IK
674 } else if ((k = endswith(t, " ago"))) {
675 t = strndupa(t, k - t);
decad910 676
48d26c01
IK
677 r = parse_sec(t, &minus);
678 if (r < 0)
679 return r;
decad910 680
48d26c01 681 goto finish;
1fcf71f5 682
48d26c01
IK
683 } else if ((k = endswith(t, " left"))) {
684 t = strndupa(t, k - t);
1fcf71f5 685
48d26c01
IK
686 r = parse_sec(t, &plus);
687 if (r < 0)
688 return r;
1fcf71f5 689
48d26c01
IK
690 goto finish;
691 }
9a98c7a1 692
48d26c01
IK
693 /* See if the timestamp is suffixed with UTC */
694 utc = endswith_no_case(t, " UTC");
695 if (utc)
696 t = strndupa(t, utc - t);
697 else {
698 const char *e = NULL;
699 int j;
21b3a0fc 700
48d26c01 701 tzset();
21b3a0fc 702
48d26c01
IK
703 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
704 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
705 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
706 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
707 * support arbitrary timezone specifications. */
e4eaf99a 708
48d26c01 709 for (j = 0; j <= 1; j++) {
21b3a0fc 710
48d26c01
IK
711 if (isempty(tzname[j]))
712 continue;
21b3a0fc 713
48d26c01
IK
714 e = endswith_no_case(t, tzname[j]);
715 if (!e)
716 continue;
717 if (e == t)
718 continue;
719 if (e[-1] != ' ')
720 continue;
21b3a0fc 721
48d26c01
IK
722 break;
723 }
21b3a0fc 724
48d26c01
IK
725 if (IN_SET(j, 0, 1)) {
726 /* Found one of the two timezones specified. */
727 t = strndupa(t, e - t - 1);
728 dst = j;
729 tzn = tzname[j];
730 }
21b3a0fc
LP
731 }
732 }
733
734 x = (time_t) (ret / USEC_PER_SEC);
e4eaf99a
HV
735 x_usec = 0;
736
21b3a0fc
LP
737 if (!localtime_or_gmtime_r(&x, &tm, utc))
738 return -EINVAL;
739
2e72b794
IK
740 tm.tm_isdst = dst;
741 if (!with_tz && tzn)
742 tm.tm_zone = tzn;
e4eaf99a
HV
743
744 if (streq(t, "today")) {
745 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
746 goto from_tm;
747
748 } else if (streq(t, "yesterday")) {
313cefa1 749 tm.tm_mday--;
e4eaf99a
HV
750 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
751 goto from_tm;
752
753 } else if (streq(t, "tomorrow")) {
313cefa1 754 tm.tm_mday++;
e4eaf99a
HV
755 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
756 goto from_tm;
757 }
758
92134489
LP
759 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
760 size_t skip;
761
762 if (!startswith_no_case(t, day_nr[i].name))
763 continue;
764
765 skip = strlen(day_nr[i].name);
766 if (t[skip] != ' ')
767 continue;
768
769 weekday = day_nr[i].nr;
770 t += skip + 1;
771 break;
772 }
773
9a98c7a1
LP
774 copy = tm;
775 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
776 if (k) {
777 if (*k == '.')
778 goto parse_usec;
779 else if (*k == 0)
780 goto from_tm;
781 }
9a98c7a1
LP
782
783 tm = copy;
784 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
785 if (k) {
786 if (*k == '.')
787 goto parse_usec;
788 else if (*k == 0)
789 goto from_tm;
790 }
9a98c7a1
LP
791
792 tm = copy;
793 k = strptime(t, "%y-%m-%d %H:%M", &tm);
794 if (k && *k == 0) {
795 tm.tm_sec = 0;
e4eaf99a 796 goto from_tm;
9a98c7a1
LP
797 }
798
799 tm = copy;
800 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
801 if (k && *k == 0) {
802 tm.tm_sec = 0;
e4eaf99a 803 goto from_tm;
9a98c7a1
LP
804 }
805
806 tm = copy;
807 k = strptime(t, "%y-%m-%d", &tm);
808 if (k && *k == 0) {
809 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 810 goto from_tm;
9a98c7a1
LP
811 }
812
813 tm = copy;
814 k = strptime(t, "%Y-%m-%d", &tm);
815 if (k && *k == 0) {
816 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 817 goto from_tm;
9a98c7a1
LP
818 }
819
820 tm = copy;
821 k = strptime(t, "%H:%M:%S", &tm);
e4eaf99a
HV
822 if (k) {
823 if (*k == '.')
824 goto parse_usec;
825 else if (*k == 0)
826 goto from_tm;
827 }
9a98c7a1
LP
828
829 tm = copy;
830 k = strptime(t, "%H:%M", &tm);
831 if (k && *k == 0) {
832 tm.tm_sec = 0;
e4eaf99a 833 goto from_tm;
9a98c7a1
LP
834 }
835
836 return -EINVAL;
837
e4eaf99a
HV
838parse_usec:
839 {
436dd70f 840 unsigned add;
e4eaf99a
HV
841
842 k++;
436dd70f
HV
843 r = parse_fractional_part_u(&k, 6, &add);
844 if (r < 0)
e4eaf99a
HV
845 return -EINVAL;
846
436dd70f 847 if (*k)
e4eaf99a
HV
848 return -EINVAL;
849
436dd70f 850 x_usec = add;
e4eaf99a
HV
851 }
852
853from_tm:
214cc95d 854 if (weekday >= 0 && tm.tm_wday != weekday)
68bdd2d2 855 return -EINVAL;
9a98c7a1 856
214cc95d
MH
857 x = mktime_or_timegm(&tm, utc);
858 if (x < 0)
92134489
LP
859 return -EINVAL;
860
68bdd2d2 861 ret = (usec_t) x * USEC_PER_SEC + x_usec;
1bb4b028
LP
862 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
863 return -EINVAL;
9a98c7a1 864
e4eaf99a 865finish:
315782db 866 if (ret + plus < ret) /* overflow? */
68bdd2d2 867 return -EINVAL;
9a98c7a1 868 ret += plus;
1bb4b028
LP
869 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
870 return -EINVAL;
871
68bdd2d2 872 if (ret >= minus)
9a98c7a1
LP
873 ret -= minus;
874 else
68bdd2d2 875 return -EINVAL;
9a98c7a1
LP
876
877 *usec = ret;
878
879 return 0;
880}
881
48d26c01
IK
882typedef struct ParseTimestampResult {
883 usec_t usec;
884 int return_value;
885} ParseTimestampResult;
886
887int parse_timestamp(const char *t, usec_t *usec) {
ff69484a 888 char *last_space, *tz = NULL;
48d26c01 889 ParseTimestampResult *shared, tmp;
4c253ed1 890 int r;
48d26c01
IK
891
892 last_space = strrchr(t, ' ');
a2932a0d 893 if (last_space != NULL && timezone_is_valid(last_space + 1))
ff69484a 894 tz = last_space + 1;
48d26c01 895
234519ae 896 if (!tz || endswith_no_case(t, " UTC"))
48d26c01
IK
897 return parse_timestamp_impl(t, usec, false);
898
48d26c01
IK
899 shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
900 if (shared == MAP_FAILED)
901 return negative_errno();
902
1f5d1e02 903 r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
4c253ed1 904 if (r < 0) {
48d26c01 905 (void) munmap(shared, sizeof *shared);
4c253ed1 906 return r;
48d26c01 907 }
4c253ed1 908 if (r == 0) {
3fd4929b
MH
909 bool with_tz = true;
910
ff69484a 911 if (setenv("TZ", tz, 1) != 0) {
48d26c01
IK
912 shared->return_value = negative_errno();
913 _exit(EXIT_FAILURE);
914 }
915
916 tzset();
917
3fd4929b
MH
918 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
919 * Otherwise just cut it off */
920 with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]);
921
922 /*cut off the timezone if we dont need it*/
923 if (with_tz)
924 t = strndupa(t, last_space - t);
925
926 shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
48d26c01
IK
927
928 _exit(EXIT_SUCCESS);
929 }
930
48d26c01
IK
931 tmp = *shared;
932 if (munmap(shared, sizeof *shared) != 0)
933 return negative_errno();
934
935 if (tmp.return_value == 0)
936 *usec = tmp.usec;
937
938 return tmp.return_value;
939}
940
240a7ba9 941static char* extract_multiplier(char *p, usec_t *multiplier) {
9a98c7a1
LP
942 static const struct {
943 const char *suffix;
944 usec_t usec;
945 } table[] = {
eb55ec9f
LP
946 { "seconds", USEC_PER_SEC },
947 { "second", USEC_PER_SEC },
948 { "sec", USEC_PER_SEC },
949 { "s", USEC_PER_SEC },
9a98c7a1 950 { "minutes", USEC_PER_MINUTE },
eb55ec9f
LP
951 { "minute", USEC_PER_MINUTE },
952 { "min", USEC_PER_MINUTE },
953 { "months", USEC_PER_MONTH },
954 { "month", USEC_PER_MONTH },
955 { "M", USEC_PER_MONTH },
956 { "msec", USEC_PER_MSEC },
957 { "ms", USEC_PER_MSEC },
958 { "m", USEC_PER_MINUTE },
959 { "hours", USEC_PER_HOUR },
960 { "hour", USEC_PER_HOUR },
961 { "hr", USEC_PER_HOUR },
962 { "h", USEC_PER_HOUR },
963 { "days", USEC_PER_DAY },
964 { "day", USEC_PER_DAY },
965 { "d", USEC_PER_DAY },
966 { "weeks", USEC_PER_WEEK },
967 { "week", USEC_PER_WEEK },
968 { "w", USEC_PER_WEEK },
969 { "years", USEC_PER_YEAR },
970 { "year", USEC_PER_YEAR },
971 { "y", USEC_PER_YEAR },
972 { "usec", 1ULL },
973 { "us", 1ULL },
5efdbf11 974 { "µs", 1ULL },
9a98c7a1 975 };
240a7ba9
ZJS
976 unsigned i;
977
978 for (i = 0; i < ELEMENTSOF(table); i++) {
979 char *e;
980
981 e = startswith(p, table[i].suffix);
982 if (e) {
983 *multiplier = table[i].usec;
984 return e;
985 }
986 }
9a98c7a1 987
240a7ba9
ZJS
988 return p;
989}
990
991int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
b1d6dcf5 992 const char *p, *s;
9a98c7a1 993 usec_t r = 0;
cb0dac05 994 bool something = false;
9a98c7a1
LP
995
996 assert(t);
997 assert(usec);
519cffec 998 assert(default_unit > 0);
9a98c7a1
LP
999
1000 p = t;
b1d6dcf5
ZJS
1001
1002 p += strspn(p, WHITESPACE);
1003 s = startswith(p, "infinity");
1004 if (s) {
1005 s += strspn(s, WHITESPACE);
1006 if (*s != 0)
1007 return -EINVAL;
1008
1009 *usec = USEC_INFINITY;
1010 return 0;
1011 }
1012
cb0dac05
LP
1013 for (;;) {
1014 long long l, z = 0;
9a98c7a1 1015 char *e;
240a7ba9
ZJS
1016 unsigned n = 0;
1017 usec_t multiplier = default_unit, k;
cb0dac05
LP
1018
1019 p += strspn(p, WHITESPACE);
1020
1021 if (*p == 0) {
1022 if (!something)
1023 return -EINVAL;
1024
1025 break;
1026 }
9a98c7a1
LP
1027
1028 errno = 0;
1029 l = strtoll(p, &e, 10);
8333c77e 1030 if (errno > 0)
9a98c7a1 1031 return -errno;
9a98c7a1
LP
1032 if (l < 0)
1033 return -ERANGE;
1034
cb0dac05
LP
1035 if (*e == '.') {
1036 char *b = e + 1;
1037
1038 errno = 0;
1039 z = strtoll(b, &e, 10);
1040 if (errno > 0)
1041 return -errno;
1042
1043 if (z < 0)
1044 return -ERANGE;
1045
1046 if (e == b)
1047 return -EINVAL;
1048
1049 n = e - b;
1050
1051 } else if (e == p)
9a98c7a1
LP
1052 return -EINVAL;
1053
1054 e += strspn(e, WHITESPACE);
240a7ba9 1055 p = extract_multiplier(e, &multiplier);
9a98c7a1 1056
519cffec
LP
1057 something = true;
1058
1059 k = (usec_t) z * multiplier;
1060
1061 for (; n > 0; n--)
1062 k /= 10;
1063
1064 r += (usec_t) l * multiplier + k;
cb0dac05 1065 }
9a98c7a1
LP
1066
1067 *usec = r;
1068
1069 return 0;
1070}
1071
519cffec
LP
1072int parse_sec(const char *t, usec_t *usec) {
1073 return parse_time(t, usec, USEC_PER_SEC);
1074}
1075
0004f698 1076int parse_sec_fix_0(const char *t, usec_t *usec) {
eae51da3
LP
1077 assert(t);
1078 assert(usec);
1079
0004f698 1080 t += strspn(t, WHITESPACE);
eae51da3 1081
0004f698
ZJS
1082 if (streq(t, "0")) {
1083 *usec = USEC_INFINITY;
1084 return 0;
1085 }
1086
1087 return parse_sec(t, usec);
1088}
1089
9a98c7a1
LP
1090int parse_nsec(const char *t, nsec_t *nsec) {
1091 static const struct {
1092 const char *suffix;
1093 nsec_t nsec;
1094 } table[] = {
1095 { "seconds", NSEC_PER_SEC },
1096 { "second", NSEC_PER_SEC },
1097 { "sec", NSEC_PER_SEC },
1098 { "s", NSEC_PER_SEC },
1099 { "minutes", NSEC_PER_MINUTE },
1100 { "minute", NSEC_PER_MINUTE },
1101 { "min", NSEC_PER_MINUTE },
1102 { "months", NSEC_PER_MONTH },
1103 { "month", NSEC_PER_MONTH },
1104 { "msec", NSEC_PER_MSEC },
1105 { "ms", NSEC_PER_MSEC },
1106 { "m", NSEC_PER_MINUTE },
1107 { "hours", NSEC_PER_HOUR },
1108 { "hour", NSEC_PER_HOUR },
1109 { "hr", NSEC_PER_HOUR },
1110 { "h", NSEC_PER_HOUR },
1111 { "days", NSEC_PER_DAY },
1112 { "day", NSEC_PER_DAY },
1113 { "d", NSEC_PER_DAY },
1114 { "weeks", NSEC_PER_WEEK },
1115 { "week", NSEC_PER_WEEK },
1116 { "w", NSEC_PER_WEEK },
1117 { "years", NSEC_PER_YEAR },
1118 { "year", NSEC_PER_YEAR },
1119 { "y", NSEC_PER_YEAR },
1120 { "usec", NSEC_PER_USEC },
1121 { "us", NSEC_PER_USEC },
5efdbf11 1122 { "µs", NSEC_PER_USEC },
9a98c7a1
LP
1123 { "nsec", 1ULL },
1124 { "ns", 1ULL },
1125 { "", 1ULL }, /* default is nsec */
1126 };
1127
e73c78c2 1128 const char *p, *s;
9a98c7a1 1129 nsec_t r = 0;
cb0dac05 1130 bool something = false;
9a98c7a1
LP
1131
1132 assert(t);
1133 assert(nsec);
1134
1135 p = t;
e73c78c2
LP
1136
1137 p += strspn(p, WHITESPACE);
1138 s = startswith(p, "infinity");
1139 if (s) {
1140 s += strspn(s, WHITESPACE);
8e8933ca 1141 if (*s != 0)
e73c78c2
LP
1142 return -EINVAL;
1143
1144 *nsec = NSEC_INFINITY;
1145 return 0;
1146 }
1147
cb0dac05
LP
1148 for (;;) {
1149 long long l, z = 0;
9a98c7a1 1150 char *e;
cb0dac05
LP
1151 unsigned i, n = 0;
1152
1153 p += strspn(p, WHITESPACE);
1154
1155 if (*p == 0) {
1156 if (!something)
1157 return -EINVAL;
1158
1159 break;
1160 }
9a98c7a1
LP
1161
1162 errno = 0;
1163 l = strtoll(p, &e, 10);
1164
8333c77e 1165 if (errno > 0)
9a98c7a1
LP
1166 return -errno;
1167
1168 if (l < 0)
1169 return -ERANGE;
1170
cb0dac05
LP
1171 if (*e == '.') {
1172 char *b = e + 1;
1173
1174 errno = 0;
1175 z = strtoll(b, &e, 10);
1176 if (errno > 0)
1177 return -errno;
1178
1179 if (z < 0)
1180 return -ERANGE;
1181
1182 if (e == b)
1183 return -EINVAL;
1184
1185 n = e - b;
1186
1187 } else if (e == p)
9a98c7a1
LP
1188 return -EINVAL;
1189
1190 e += strspn(e, WHITESPACE);
1191
1192 for (i = 0; i < ELEMENTSOF(table); i++)
1193 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
1194 nsec_t k = (nsec_t) z * table[i].nsec;
1195
1196 for (; n > 0; n--)
1197 k /= 10;
1198
1199 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 1200 p = e + strlen(table[i].suffix);
cb0dac05
LP
1201
1202 something = true;
9a98c7a1
LP
1203 break;
1204 }
1205
1206 if (i >= ELEMENTSOF(table))
1207 return -EINVAL;
1208
cb0dac05 1209 }
9a98c7a1
LP
1210
1211 *nsec = r;
1212
1213 return 0;
1214}
03cc26dd
LP
1215
1216bool ntp_synced(void) {
1217 struct timex txc = {};
1218
1219 if (adjtimex(&txc) < 0)
1220 return false;
1221
1222 if (txc.status & STA_UNSYNC)
1223 return false;
1224
1225 return true;
1226}
75683450
LP
1227
1228int get_timezones(char ***ret) {
1229 _cleanup_fclose_ FILE *f = NULL;
1230 _cleanup_strv_free_ char **zones = NULL;
1231 size_t n_zones = 0, n_allocated = 0;
1232
1233 assert(ret);
1234
1235 zones = strv_new("UTC", NULL);
1236 if (!zones)
1237 return -ENOMEM;
1238
1239 n_allocated = 2;
1240 n_zones = 1;
1241
1242 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
1243 if (f) {
1244 char l[LINE_MAX];
1245
1246 FOREACH_LINE(l, f, return -errno) {
1247 char *p, *w;
1248 size_t k;
1249
1250 p = strstrip(l);
1251
1252 if (isempty(p) || *p == '#')
1253 continue;
1254
1255 /* Skip over country code */
1256 p += strcspn(p, WHITESPACE);
1257 p += strspn(p, WHITESPACE);
1258
1259 /* Skip over coordinates */
1260 p += strcspn(p, WHITESPACE);
1261 p += strspn(p, WHITESPACE);
1262
1263 /* Found timezone name */
1264 k = strcspn(p, WHITESPACE);
1265 if (k <= 0)
1266 continue;
1267
1268 w = strndup(p, k);
1269 if (!w)
1270 return -ENOMEM;
1271
1272 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
1273 free(w);
1274 return -ENOMEM;
1275 }
1276
1277 zones[n_zones++] = w;
1278 zones[n_zones] = NULL;
1279 }
1280
1281 strv_sort(zones);
1282
1283 } else if (errno != ENOENT)
1284 return -errno;
1285
1286 *ret = zones;
1287 zones = NULL;
1288
1289 return 0;
1290}
1291
1292bool timezone_is_valid(const char *name) {
1293 bool slash = false;
1294 const char *p, *t;
1295 struct stat st;
1296
5c904ba5
LP
1297 if (isempty(name))
1298 return false;
1299
1300 if (name[0] == '/')
75683450
LP
1301 return false;
1302
1303 for (p = name; *p; p++) {
1304 if (!(*p >= '0' && *p <= '9') &&
1305 !(*p >= 'a' && *p <= 'z') &&
1306 !(*p >= 'A' && *p <= 'Z') &&
4c701096 1307 !IN_SET(*p, '-', '_', '+', '/'))
75683450
LP
1308 return false;
1309
1310 if (*p == '/') {
1311
1312 if (slash)
1313 return false;
1314
1315 slash = true;
1316 } else
1317 slash = false;
1318 }
1319
1320 if (slash)
1321 return false;
1322
63c372cb 1323 t = strjoina("/usr/share/zoneinfo/", name);
75683450
LP
1324 if (stat(t, &st) < 0)
1325 return false;
1326
1327 if (!S_ISREG(st.st_mode))
1328 return false;
1329
1330 return true;
1331}
77ff2de9 1332
3411372e
LP
1333bool clock_boottime_supported(void) {
1334 static int supported = -1;
1335
1336 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1337
1338 if (supported < 0) {
1339 int fd;
1340
1341 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1342 if (fd < 0)
1343 supported = false;
1344 else {
1345 safe_close(fd);
1346 supported = true;
1347 }
77ff2de9
TG
1348 }
1349
3411372e
LP
1350 return supported;
1351}
1352
1353clockid_t clock_boottime_or_monotonic(void) {
1354 if (clock_boottime_supported())
1355 return CLOCK_BOOTTIME;
1356 else
1357 return CLOCK_MONOTONIC;
77ff2de9 1358}
5c904ba5 1359
fe624c4c
LP
1360bool clock_supported(clockid_t clock) {
1361 struct timespec ts;
1362
1363 switch (clock) {
1364
1365 case CLOCK_MONOTONIC:
1366 case CLOCK_REALTIME:
1367 return true;
1368
1369 case CLOCK_BOOTTIME:
1370 return clock_boottime_supported();
1371
1372 case CLOCK_BOOTTIME_ALARM:
1373 if (!clock_boottime_supported())
1374 return false;
1375
4831981d 1376 _fallthrough_;
fe624c4c
LP
1377 default:
1378 /* For everything else, check properly */
1379 return clock_gettime(clock, &ts) >= 0;
1380 }
1381}
1382
64d6c229 1383int get_timezone(char **tz) {
5c904ba5
LP
1384 _cleanup_free_ char *t = NULL;
1385 const char *e;
1386 char *z;
1387 int r;
1388
1389 r = readlink_malloc("/etc/localtime", &t);
1390 if (r < 0)
1391 return r; /* returns EINVAL if not a symlink */
1392
1393 e = path_startswith(t, "/usr/share/zoneinfo/");
1394 if (!e)
1395 e = path_startswith(t, "../usr/share/zoneinfo/");
1396 if (!e)
1397 return -EINVAL;
1398
1399 if (!timezone_is_valid(e))
1400 return -EINVAL;
1401
1402 z = strdup(e);
1403 if (!z)
1404 return -ENOMEM;
1405
64d6c229 1406 *tz = z;
5c904ba5
LP
1407 return 0;
1408}
7c67c79c
HV
1409
1410time_t mktime_or_timegm(struct tm *tm, bool utc) {
1411 return utc ? timegm(tm) : mktime(tm);
1412}
1413
1414struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1415 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1416}
87b8ce69
SS
1417
1418unsigned long usec_to_jiffies(usec_t u) {
1419 static thread_local unsigned long hz = 0;
1420 long r;
1421
1422 if (hz == 0) {
1423 r = sysconf(_SC_CLK_TCK);
1424
1425 assert(r > 0);
70887c5f 1426 hz = r;
87b8ce69
SS
1427 }
1428
1429 return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
1430}
1007ec60
LP
1431
1432usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
1433 usec_t a, b;
1434
1435 if (x == USEC_INFINITY)
1436 return USEC_INFINITY;
1437 if (map_clock_id(from) == map_clock_id(to))
1438 return x;
1439
1440 a = now(from);
1441 b = now(to);
1442
1443 if (x > a)
1444 /* x lies in the future */
1445 return usec_add(b, usec_sub_unsigned(x, a));
1446 else
1447 /* x lies in the past */
1448 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
1449}
9a9a4f10
LP
1450
1451bool in_utc_timezone(void) {
1452 tzset();
1453
1454 return timezone == 0 && daylight == 0;
1455}