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