]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/time-util.c
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / src / basic / time-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
9a98c7a1 2
11c3a366 3#include <stdlib.h>
48d26c01 4#include <sys/mman.h>
77ff2de9 5#include <sys/timerfd.h>
e7537295 6#include <threads.h>
11c3a366 7#include <unistd.h>
9a98c7a1 8
b5efdb8a 9#include "alloc-util.h"
fda65211 10#include "errno-util.h"
6553db60 11#include "extract-word.h"
3ffd4af2 12#include "fd-util.h"
0d39fa9c 13#include "fileio.h"
f4f15635 14#include "fs-util.h"
a2932d51 15#include "io-util.h"
11c3a366 16#include "log.h"
93cc7779
TA
17#include "parse-util.h"
18#include "path-util.h"
dccca82b 19#include "process-util.h"
a2932d51 20#include "stat-util.h"
0c15577a 21#include "stdio-util.h"
7b3eb5c9 22#include "string-table.h"
07630cea 23#include "string-util.h"
75683450 24#include "strv.h"
07630cea 25#include "time-util.h"
9a98c7a1 26
32c1f5a5
LP
27static clockid_t map_clock_id(clockid_t c) {
28
8b105ec8
ZJS
29 /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus,
30 * clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM
31 * pendants (their only difference is when timers are set on them), let's just map them
32 * accordingly. This way, we can get the correct time even on those archs. */
32c1f5a5
LP
33
34 switch (c) {
35
36 case CLOCK_BOOTTIME_ALARM:
bdf19f8f 37 return CLOCK_BOOTTIME;
32c1f5a5
LP
38
39 case CLOCK_REALTIME_ALARM:
40 return CLOCK_REALTIME;
41
42 default:
43 return c;
44 }
45}
46
9a98c7a1
LP
47usec_t now(clockid_t clock_id) {
48 struct timespec ts;
49
32c1f5a5 50 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
9a98c7a1
LP
51
52 return timespec_load(&ts);
53}
54
45d7a8bb
LP
55nsec_t now_nsec(clockid_t clock_id) {
56 struct timespec ts;
57
32c1f5a5 58 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
45d7a8bb
LP
59
60 return timespec_load_nsec(&ts);
61}
62
fa5a0251 63dual_timestamp* dual_timestamp_now(dual_timestamp *ts) {
9a98c7a1
LP
64 assert(ts);
65
66 ts->realtime = now(CLOCK_REALTIME);
67 ts->monotonic = now(CLOCK_MONOTONIC);
68
69 return ts;
70}
71
fa5a0251 72triple_timestamp* triple_timestamp_now(triple_timestamp *ts) {
fe624c4c
LP
73 assert(ts);
74
75 ts->realtime = now(CLOCK_REALTIME);
76 ts->monotonic = now(CLOCK_MONOTONIC);
ba4e0427 77 ts->boottime = now(CLOCK_BOOTTIME);
fe624c4c
LP
78
79 return ts;
80}
81
3ecd6fa9 82usec_t map_clock_usec_raw(usec_t from, usec_t from_base, usec_t to_base) {
7c0eb30e
LP
83
84 /* Maps the time 'from' between two clocks, based on a common reference point where the first clock
85 * is at 'from_base' and the second clock at 'to_base'. Basically calculates:
86 *
87 * from - from_base + to_base
88 *
89 * But takes care of overflows/underflows and avoids signed operations. */
90
91 if (from >= from_base) { /* In the future */
92 usec_t delta = from - from_base;
93
94 if (to_base >= USEC_INFINITY - delta) /* overflow? */
95 return USEC_INFINITY;
96
97 return to_base + delta;
98
99 } else { /* In the past */
100 usec_t delta = from_base - from;
101
102 if (to_base <= delta) /* underflow? */
103 return 0;
104
105 return to_base - delta;
106 }
107}
108
109usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
110
111 /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
112 * onto itself */
113 if (map_clock_id(from_clock) == map_clock_id(to_clock))
114 return from;
115
116 /* Keep infinity as is */
117 if (from == USEC_INFINITY)
118 return from;
119
3ecd6fa9 120 return map_clock_usec_raw(from, now(from_clock), now(to_clock));
7c0eb30e
LP
121}
122
9a98c7a1 123dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
9a98c7a1
LP
124 assert(ts);
125
0da36375 126 if (!timestamp_is_set(u)) {
75a5f1d8 127 ts->realtime = ts->monotonic = u;
cae0c5e0
LP
128 return ts;
129 }
130
9a98c7a1 131 ts->realtime = u;
7c0eb30e 132 ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC);
9a98c7a1
LP
133 return ts;
134}
135
fe624c4c 136triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
7c0eb30e 137 usec_t nowr;
fe624c4c
LP
138
139 assert(ts);
140
0da36375 141 if (!timestamp_is_set(u)) {
fe624c4c
LP
142 ts->realtime = ts->monotonic = ts->boottime = u;
143 return ts;
144 }
145
7c0eb30e
LP
146 nowr = now(CLOCK_REALTIME);
147
fe624c4c 148 ts->realtime = u;
3ecd6fa9
YW
149 ts->monotonic = map_clock_usec_raw(u, nowr, now(CLOCK_MONOTONIC));
150 ts->boottime = map_clock_usec_raw(u, nowr, now(CLOCK_BOOTTIME));
fe624c4c
LP
151
152 return ts;
153}
154
7cd07551
YW
155triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u) {
156 usec_t nowb;
157
158 assert(ts);
159
160 if (u == USEC_INFINITY) {
161 ts->realtime = ts->monotonic = ts->boottime = u;
162 return ts;
163 }
164
165 nowb = now(CLOCK_BOOTTIME);
166
167 ts->boottime = u;
3ecd6fa9
YW
168 ts->monotonic = map_clock_usec_raw(u, nowb, now(CLOCK_MONOTONIC));
169 ts->realtime = map_clock_usec_raw(u, nowb, now(CLOCK_REALTIME));
7cd07551
YW
170
171 return ts;
172}
173
cae0c5e0 174dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
cae0c5e0
LP
175 assert(ts);
176
3a43da28
KS
177 if (u == USEC_INFINITY) {
178 ts->realtime = ts->monotonic = USEC_INFINITY;
cae0c5e0
LP
179 return ts;
180 }
181
182 ts->monotonic = u;
7c0eb30e 183 ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME);
cae0c5e0
LP
184 return ts;
185}
186
ba4e0427 187dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
7c0eb30e 188 usec_t nowm;
fbe55073 189
dff3bddc
YW
190 assert(ts);
191
fbe55073
LP
192 if (u == USEC_INFINITY) {
193 ts->realtime = ts->monotonic = USEC_INFINITY;
194 return ts;
195 }
fbe55073 196
ba4e0427 197 nowm = now(CLOCK_BOOTTIME);
3ecd6fa9
YW
198 ts->monotonic = map_clock_usec_raw(u, nowm, now(CLOCK_MONOTONIC));
199 ts->realtime = map_clock_usec_raw(u, nowm, now(CLOCK_REALTIME));
fbe55073
LP
200 return ts;
201}
202
fe624c4c 203usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
dff3bddc 204 assert(ts);
fe624c4c
LP
205
206 switch (clock) {
207
208 case CLOCK_REALTIME:
209 case CLOCK_REALTIME_ALARM:
210 return ts->realtime;
211
212 case CLOCK_MONOTONIC:
213 return ts->monotonic;
214
215 case CLOCK_BOOTTIME:
216 case CLOCK_BOOTTIME_ALARM:
217 return ts->boottime;
218
219 default:
220 return USEC_INFINITY;
221 }
222}
223
9a98c7a1
LP
224usec_t timespec_load(const struct timespec *ts) {
225 assert(ts);
226
c477ff14 227 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
3a43da28 228 return USEC_INFINITY;
9a98c7a1
LP
229
230 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
3a43da28 231 return USEC_INFINITY;
9a98c7a1
LP
232
233 return
234 (usec_t) ts->tv_sec * USEC_PER_SEC +
235 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
236}
237
3a730176 238nsec_t timespec_load_nsec(const struct timespec *ts) {
45d7a8bb
LP
239 assert(ts);
240
c477ff14 241 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
45d7a8bb
LP
242 return NSEC_INFINITY;
243
a2daa2f0
ZJS
244 if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
245 return NSEC_INFINITY;
246
247 return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
45d7a8bb
LP
248}
249
7464953f 250struct timespec *timespec_store(struct timespec *ts, usec_t u) {
9a98c7a1
LP
251 assert(ts);
252
f977849c 253 if (u == USEC_INFINITY ||
d201d908 254 u / USEC_PER_SEC >= TIME_T_MAX) {
9a98c7a1 255 ts->tv_sec = (time_t) -1;
cd1361e2 256 ts->tv_nsec = -1L;
9a98c7a1
LP
257 return ts;
258 }
259
260 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
cd1361e2
LP
261 ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
262
263 return ts;
264}
265
7464953f 266struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
cd1361e2
LP
267 assert(ts);
268
269 if (n == NSEC_INFINITY ||
270 n / NSEC_PER_SEC >= TIME_T_MAX) {
271 ts->tv_sec = (time_t) -1;
272 ts->tv_nsec = -1L;
273 return ts;
274 }
275
276 ts->tv_sec = (time_t) (n / NSEC_PER_SEC);
277 ts->tv_nsec = (long) (n % NSEC_PER_SEC);
9a98c7a1
LP
278
279 return ts;
280}
281
282usec_t timeval_load(const struct timeval *tv) {
283 assert(tv);
284
c477ff14 285 if (tv->tv_sec < 0 || tv->tv_usec < 0)
3a43da28 286 return USEC_INFINITY;
9a98c7a1
LP
287
288 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
3a43da28 289 return USEC_INFINITY;
9a98c7a1
LP
290
291 return
292 (usec_t) tv->tv_sec * USEC_PER_SEC +
293 (usec_t) tv->tv_usec;
294}
295
296struct timeval *timeval_store(struct timeval *tv, usec_t u) {
297 assert(tv);
298
68bdd2d2 299 if (u == USEC_INFINITY ||
f977849c 300 u / USEC_PER_SEC > TIME_T_MAX) {
9a98c7a1
LP
301 tv->tv_sec = (time_t) -1;
302 tv->tv_usec = (suseconds_t) -1;
4d89874a
ZJS
303 } else {
304 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
305 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
306 }
307
9a98c7a1
LP
308 return tv;
309}
310
ff3f2953 311char* format_timestamp_style(
21b3a0fc
LP
312 char *buf,
313 size_t l,
314 usec_t t,
7b3eb5c9 315 TimestampStyle style) {
21b3a0fc 316
8b105ec8
ZJS
317 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that
318 * our generated timestamps may be parsed with parse_timestamp(), and always read the same. */
21b3a0fc
LP
319 static const char * const weekdays[] = {
320 [0] = "Sun",
321 [1] = "Mon",
322 [2] = "Tue",
323 [3] = "Wed",
324 [4] = "Thu",
325 [5] = "Fri",
326 [6] = "Sat",
327 };
328
9a98c7a1 329 struct tm tm;
060c9c02 330 bool utc, us;
21b3a0fc 331 size_t n;
9a98c7a1
LP
332
333 assert(buf);
060c9c02
LP
334 assert(style >= 0);
335 assert(style < _TIMESTAMP_STYLE_MAX);
9a98c7a1 336
0da36375 337 if (!timestamp_is_set(t))
21b3a0fc
LP
338 return NULL; /* Timestamp is unset */
339
ed4a5b43 340 if (style == TIMESTAMP_UNIX) {
61d03c54
LP
341 if (l < (size_t) (1 + 1 + 1))
342 return NULL; /* not enough space for even the shortest of forms */
ed4a5b43 343
e503019b 344 return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down μs → s */
ed4a5b43
FS
345 }
346
64f3419e 347 utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
3703e5d0
LP
348 us = IN_SET(style, TIMESTAMP_US, TIMESTAMP_US_UTC);
349
64f3419e
LP
350 if (l < (size_t) (3 + /* week day */
351 1 + 10 + /* space and date */
352 style == TIMESTAMP_DATE ? 0 :
353 (1 + 8 + /* space and time */
354 (us ? 1 + 6 : 0) + /* "." and microsecond part */
355 1 + (utc ? 3 : 1)) + /* space and shortest possible zone */
3703e5d0
LP
356 1))
357 return NULL; /* Not enough space even for the shortest form. */
358
1bb4b028 359 /* Let's not format times with years > 9999 */
d3d28024 360 if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
4361678c
LP
361 static const char* const xxx[_TIMESTAMP_STYLE_MAX] = {
362 [TIMESTAMP_PRETTY] = "--- XXXX-XX-XX XX:XX:XX",
363 [TIMESTAMP_US] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX",
364 [TIMESTAMP_UTC] = "--- XXXX-XX-XX XX:XX:XX UTC",
365 [TIMESTAMP_US_UTC] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC",
64f3419e 366 [TIMESTAMP_DATE] = "--- XXXX-XX-XX",
4361678c
LP
367 };
368
369 assert(l >= strlen(xxx[style]) + 1);
370 return strcpy(buf, xxx[style]);
d3d28024 371 }
1bb4b028 372
6f5cf415 373 if (localtime_or_gmtime_usec(t, utc, &tm) < 0)
9a98c7a1
LP
374 return NULL;
375
21b3a0fc
LP
376 /* Start with the week day */
377 assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
378 memcpy(buf, weekdays[tm.tm_wday], 4);
9a98c7a1 379
64f3419e
LP
380 if (style == TIMESTAMP_DATE) {
381 /* Special format string if only date should be shown. */
382 if (strftime(buf + 3, l - 3, " %Y-%m-%d", &tm) <= 0)
383 return NULL; /* Doesn't fit */
384
385 return buf;
386 }
387
21b3a0fc
LP
388 /* Add the main components */
389 if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
390 return NULL; /* Doesn't fit */
0056086a 391
21b3a0fc 392 /* Append the microseconds part, if that's requested */
0056086a 393 if (us) {
21b3a0fc
LP
394 n = strlen(buf);
395 if (n + 8 > l)
396 return NULL; /* Microseconds part doesn't fit. */
397
70887c5f 398 sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
21b3a0fc
LP
399 }
400
401 /* Append the timezone */
402 n = strlen(buf);
403 if (utc) {
8b105ec8
ZJS
404 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r()
405 * normally uses the obsolete "GMT" instead. */
21b3a0fc
LP
406 if (n + 5 > l)
407 return NULL; /* "UTC" doesn't fit. */
408
409 strcpy(buf + n, " UTC");
410
411 } else if (!isempty(tm.tm_zone)) {
412 size_t tn;
413
414 /* An explicit timezone is specified, let's use it, if it fits */
415 tn = strlen(tm.tm_zone);
416 if (n + 1 + tn + 1 > l) {
417 /* The full time zone does not fit in. Yuck. */
418
419 if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
8b105ec8
ZJS
420 return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that
421 * case, complain that it doesn't fit. */
422
423 /* So the time zone doesn't fit in fully, but the caller passed enough space for the
424 * POSIX minimum time zone length. In this case suppress the timezone entirely, in
425 * order not to dump an overly long, hard to read string on the user. This should be
426 * safe, because the user will assume the local timezone anyway if none is shown. And
427 * so does parse_timestamp(). */
21b3a0fc
LP
428 } else {
429 buf[n++] = ' ';
430 strcpy(buf + n, tm.tm_zone);
431 }
0056086a 432 }
9a98c7a1
LP
433
434 return buf;
435}
436
d65c289f 437char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, clockid_t clock, bool implicit_left) {
1fcf71f5 438 const char *s;
9a98c7a1
LP
439 usec_t n, d;
440
dff3bddc
YW
441 assert(buf);
442
0da36375 443 if (!timestamp_is_set(t))
9a98c7a1
LP
444 return NULL;
445
d65c289f 446 n = now(clock);
1fcf71f5
LP
447 if (n > t) {
448 d = n - t;
d5e6f36c 449 s = " ago";
1fcf71f5
LP
450 } else {
451 d = t - n;
d5e6f36c 452 s = implicit_left ? "" : " left";
1fcf71f5 453 }
9a98c7a1 454
45eb4d22
AW
455 if (d >= USEC_PER_YEAR) {
456 usec_t years = d / USEC_PER_YEAR;
457 usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
f7581717 458
d5e6f36c 459 (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
121ed16c
LB
460 years,
461 years == 1 ? "year" : "years",
462 months,
463 months == 1 ? "month" : "months",
464 s);
f7581717 465 } else if (d >= USEC_PER_MONTH) {
45eb4d22
AW
466 usec_t months = d / USEC_PER_MONTH;
467 usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
f7581717 468
d5e6f36c 469 (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
121ed16c
LB
470 months,
471 months == 1 ? "month" : "months",
472 days,
473 days == 1 ? "day" : "days",
474 s);
f7581717 475 } else if (d >= USEC_PER_WEEK) {
45eb4d22
AW
476 usec_t weeks = d / USEC_PER_WEEK;
477 usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
f7581717 478
d5e6f36c 479 (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
121ed16c
LB
480 weeks,
481 weeks == 1 ? "week" : "weeks",
482 days,
483 days == 1 ? "day" : "days",
484 s);
f7581717 485 } else if (d >= 2*USEC_PER_DAY)
d5e6f36c 486 (void) snprintf(buf, l, USEC_FMT " days%s", d / USEC_PER_DAY,s);
9a98c7a1 487 else if (d >= 25*USEC_PER_HOUR)
d5e6f36c 488 (void) snprintf(buf, l, "1 day " USEC_FMT "h%s",
121ed16c 489 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
9a98c7a1 490 else if (d >= 6*USEC_PER_HOUR)
d5e6f36c 491 (void) snprintf(buf, l, USEC_FMT "h%s",
121ed16c 492 d / USEC_PER_HOUR, s);
9a98c7a1 493 else if (d >= USEC_PER_HOUR)
d5e6f36c 494 (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min%s",
121ed16c
LB
495 d / USEC_PER_HOUR,
496 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
9a98c7a1 497 else if (d >= 5*USEC_PER_MINUTE)
d5e6f36c 498 (void) snprintf(buf, l, USEC_FMT "min%s",
121ed16c 499 d / USEC_PER_MINUTE, s);
9a98c7a1 500 else if (d >= USEC_PER_MINUTE)
d5e6f36c 501 (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s%s",
121ed16c
LB
502 d / USEC_PER_MINUTE,
503 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
9a98c7a1 504 else if (d >= USEC_PER_SEC)
d5e6f36c 505 (void) snprintf(buf, l, USEC_FMT "s%s",
121ed16c 506 d / USEC_PER_SEC, s);
9a98c7a1 507 else if (d >= USEC_PER_MSEC)
d5e6f36c 508 (void) snprintf(buf, l, USEC_FMT "ms%s",
121ed16c 509 d / USEC_PER_MSEC, s);
9a98c7a1 510 else if (d > 0)
d5e6f36c 511 (void) snprintf(buf, l, USEC_FMT"us%s",
121ed16c 512 d, s);
9a98c7a1 513 else
121ed16c 514 (void) snprintf(buf, l, "now");
9a98c7a1
LP
515
516 buf[l-1] = 0;
517 return buf;
518}
519
d5e6f36c 520char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
521 static const struct {
522 const char *suffix;
523 usec_t usec;
524 } table[] = {
eb55ec9f
LP
525 { "y", USEC_PER_YEAR },
526 { "month", USEC_PER_MONTH },
527 { "w", USEC_PER_WEEK },
528 { "d", USEC_PER_DAY },
529 { "h", USEC_PER_HOUR },
530 { "min", USEC_PER_MINUTE },
531 { "s", USEC_PER_SEC },
532 { "ms", USEC_PER_MSEC },
533 { "us", 1 },
9a98c7a1
LP
534 };
535
99534007 536 char *p = ASSERT_PTR(buf);
2fa4092c 537 bool something = false;
9a98c7a1 538
9a98c7a1
LP
539 assert(l > 0);
540
bb1fada8
LP
541 if (t == USEC_INFINITY) {
542 strncpy(p, "infinity", l-1);
543 p[l-1] = 0;
544 return p;
545 }
546
547 if (t <= 0) {
548 strncpy(p, "0", l-1);
7c537b2e
LP
549 p[l-1] = 0;
550 return p;
551 }
552
7f602784 553 /* The result of this function can be parsed with parse_sec */
9a98c7a1 554
ddb8a639 555 FOREACH_ELEMENT(i, table) {
7759ecb2 556 int k = 0;
9a98c7a1 557 size_t n;
2fa4092c
LP
558 bool done = false;
559 usec_t a, b;
560
7c537b2e
LP
561 if (t <= 0)
562 break;
2fa4092c 563
7c537b2e 564 if (t < accuracy && something)
2fa4092c 565 break;
9a98c7a1 566
ddb8a639 567 if (t < i->usec)
9a98c7a1
LP
568 continue;
569
570 if (l <= 1)
571 break;
572
ddb8a639
I
573 a = t / i->usec;
574 b = t % i->usec;
2fa4092c
LP
575
576 /* Let's see if we should shows this in dot notation */
577 if (t < USEC_PER_MINUTE && b > 0) {
c543f4d7 578 signed char j = 0;
2fa4092c 579
ddb8a639 580 for (usec_t cc = i->usec; cc > 1; cc /= 10)
2fa4092c
LP
581 j++;
582
c543f4d7 583 for (usec_t cc = accuracy; cc > 1; cc /= 10) {
2fa4092c
LP
584 b /= 10;
585 j--;
586 }
587
588 if (j > 0) {
589 k = snprintf(p, l,
70887c5f 590 "%s"USEC_FMT".%0*"PRI_USEC"%s",
2fa4092c 591 p > buf ? " " : "",
de0671ee 592 a,
2fa4092c 593 j,
70887c5f 594 b,
ddb8a639 595 i->suffix);
2fa4092c
LP
596
597 t = 0;
598 done = true;
599 }
600 }
601
602 /* No? Then let's show it normally */
603 if (!done) {
604 k = snprintf(p, l,
de0671ee 605 "%s"USEC_FMT"%s",
2fa4092c 606 p > buf ? " " : "",
de0671ee 607 a,
ddb8a639 608 i->suffix);
2fa4092c
LP
609
610 t = b;
611 }
612
9102c625 613 n = MIN((size_t) k, l-1);
9a98c7a1
LP
614
615 l -= n;
616 p += n;
617
2fa4092c 618 something = true;
9a98c7a1
LP
619 }
620
621 *p = 0;
622
623 return buf;
624}
625
7a9afae6
YW
626static int parse_timestamp_impl(
627 const char *t,
ef658a63 628 size_t max_len,
7a9afae6
YW
629 bool utc,
630 int isdst,
631 long gmtoff,
632 usec_t *ret) {
633
92134489
LP
634 static const struct {
635 const char *name;
636 const int nr;
637 } day_nr[] = {
638 { "Sunday", 0 },
639 { "Sun", 0 },
640 { "Monday", 1 },
641 { "Mon", 1 },
642 { "Tuesday", 2 },
643 { "Tue", 2 },
644 { "Wednesday", 3 },
645 { "Wed", 3 },
646 { "Thursday", 4 },
647 { "Thu", 4 },
648 { "Friday", 5 },
649 { "Fri", 5 },
650 { "Saturday", 6 },
651 { "Sat", 6 },
652 };
653
8beb47c8 654 _cleanup_free_ char *t_alloc = NULL;
1d2c42c5 655 usec_t usec, plus = 0, minus = 0;
8beb47c8 656 bool with_tz = false;
7a9afae6 657 int r, weekday = -1;
1d2c42c5 658 unsigned fractional = 0;
7a9afae6
YW
659 const char *k;
660 struct tm tm, copy;
9a98c7a1 661
947f9f01 662 /* Allowed syntaxes:
9a98c7a1 663 *
ef658a63 664 * 2012-09-22 16:34:22.1[2[3[4[5[6]]]]]
665 * 2012-09-22 16:34:22 (µsec will be set to 0)
9a98c7a1 666 * 2012-09-22 16:34 (seconds will be set to 0)
ef658a63 667 * 2012-09-22T16:34:22.1[2[3[4[5[6]]]]]
668 * 2012-09-22T16:34:22 (µsec will be set to 0)
669 * 2012-09-22T16:34 (seconds will be set to 0)
9a98c7a1
LP
670 * 2012-09-22 (time will be set to 00:00:00)
671 * 16:34:22 (date will be set to today)
672 * 16:34 (date will be set to today, seconds to 0)
673 * now
674 * yesterday (time is set to 00:00:00)
675 * today (time is set to 00:00:00)
676 * tomorrow (time is set to 00:00:00)
677 * +5min
678 * -5days
5ba6e094 679 * @2147483647 (seconds since epoch)
37c6a3dc
YW
680 *
681 * Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted.
682 * E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT".
ef658a63 683 *
684 * A simplified strptime-spelled RFC3339 ABNF looks like
685 * "%Y-%m-%d" "T" "%H" ":" "%M" ":" "%S" [".%N"] ("Z" / (("+" / "-") "%H:%M"))
686 * We additionally allow no seconds and inherited timezone
687 * for symmetry with our other syntaxes and improved interactive usability:
688 * "%Y-%m-%d" "T" "%H" ":" "%M" ":" ["%S" [".%N"]] ["Z" / (("+" / "-") "%H:%M")]
689 * RFC3339 defines time-secfrac to as "." 1*DIGIT, but we limit to 6 digits,
690 * since we're limited to 1µs resolution.
691 * We also accept "Sat 2012-09-22T16:34:22", RFC3339 warns against it.
9a98c7a1
LP
692 */
693
694 assert(t);
9a98c7a1 695
ef658a63 696 if (max_len != SIZE_MAX) {
8beb47c8
YW
697 /* If the input string contains timezone, then cut it here. */
698
ef658a63 699 if (max_len == 0) /* Can't be the only field */
8beb47c8
YW
700 return -EINVAL;
701
ef658a63 702 t_alloc = strndup(t, max_len);
8beb47c8
YW
703 if (!t_alloc)
704 return -ENOMEM;
705
706 t = t_alloc;
707 with_tz = true;
708 }
709
9f819781
YW
710 if (utc) {
711 /* glibc accepts gmtoff more than 24 hours, but we refuse it. */
712 if ((usec_t) labs(gmtoff) * USEC_PER_SEC > USEC_PER_DAY)
713 return -EINVAL;
714 } else {
715 if (gmtoff != 0)
716 return -EINVAL;
717 }
718
48d26c01 719 if (t[0] == '@' && !with_tz)
cf98b66d 720 return parse_sec(t + 1, ret);
9a98c7a1 721
cf98b66d 722 usec = now(CLOCK_REALTIME);
9a98c7a1 723
48d26c01
IK
724 if (!with_tz) {
725 if (streq(t, "now"))
726 goto finish;
9a98c7a1 727
17d1ebfc 728 if (t[0] == '+') {
48d26c01
IK
729 r = parse_sec(t+1, &plus);
730 if (r < 0)
731 return r;
9a98c7a1 732
48d26c01 733 goto finish;
17d1ebfc 734 }
9a98c7a1 735
17d1ebfc 736 if (t[0] == '-') {
48d26c01
IK
737 r = parse_sec(t+1, &minus);
738 if (r < 0)
739 return r;
9a98c7a1 740
48d26c01 741 goto finish;
17d1ebfc 742 }
decad910 743
17d1ebfc 744 if ((k = endswith(t, " ago"))) {
804537bd 745 _cleanup_free_ char *buf = NULL;
decad910 746
804537bd
YW
747 buf = strndup(t, k - t);
748 if (!buf)
749 return -ENOMEM;
750
751 r = parse_sec(buf, &minus);
48d26c01
IK
752 if (r < 0)
753 return r;
decad910 754
48d26c01 755 goto finish;
17d1ebfc 756 }
1fcf71f5 757
17d1ebfc 758 if ((k = endswith(t, " left"))) {
804537bd
YW
759 _cleanup_free_ char *buf = NULL;
760
761 buf = strndup(t, k - t);
762 if (!buf)
763 return -ENOMEM;
1fcf71f5 764
804537bd 765 r = parse_sec(buf, &plus);
48d26c01
IK
766 if (r < 0)
767 return r;
1fcf71f5 768
48d26c01
IK
769 goto finish;
770 }
21b3a0fc
LP
771 }
772
6f5cf415
LP
773 r = localtime_or_gmtime_usec(usec, utc, &tm);
774 if (r < 0)
775 return r;
21b3a0fc 776
a83c1baa 777 tm.tm_isdst = isdst;
e4eaf99a
HV
778
779 if (streq(t, "today")) {
780 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
781 goto from_tm;
782
783 } else if (streq(t, "yesterday")) {
313cefa1 784 tm.tm_mday--;
e4eaf99a
HV
785 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
786 goto from_tm;
787
788 } else if (streq(t, "tomorrow")) {
313cefa1 789 tm.tm_mday++;
e4eaf99a
HV
790 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
791 goto from_tm;
792 }
793
ddb8a639
I
794 FOREACH_ELEMENT(day, day_nr) {
795 k = startswith_no_case(t, day->name);
f2ecfd8b 796 if (!k || *k != ' ')
92134489
LP
797 continue;
798
ddb8a639 799 weekday = day->nr;
f2ecfd8b 800 t = k + 1;
92134489
LP
801 break;
802 }
803
9a98c7a1
LP
804 copy = tm;
805 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
806 if (k) {
807 if (*k == '.')
808 goto parse_usec;
809 else if (*k == 0)
810 goto from_tm;
811 }
9a98c7a1 812
ef658a63 813 /* Our "canonical" RFC3339 syntax variant */
9a98c7a1
LP
814 tm = copy;
815 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
816 if (k) {
817 if (*k == '.')
818 goto parse_usec;
819 else if (*k == 0)
820 goto from_tm;
821 }
9a98c7a1 822
ef658a63 823 /* RFC3339 syntax */
824 tm = copy;
825 k = strptime(t, "%Y-%m-%dT%H:%M:%S", &tm);
826 if (k) {
827 if (*k == '.')
828 goto parse_usec;
829 else if (*k == 0)
830 goto from_tm;
831 }
832
34c4dff4
FS
833 /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
834 tm = copy;
835 k = strptime(t, "%b %d %H:%M:%S", &tm);
836 if (k) {
837 if (*k == '.')
838 goto parse_usec;
839 else if (*k == 0)
840 goto from_tm;
841 }
842
9a98c7a1
LP
843 tm = copy;
844 k = strptime(t, "%y-%m-%d %H:%M", &tm);
845 if (k && *k == 0) {
846 tm.tm_sec = 0;
e4eaf99a 847 goto from_tm;
9a98c7a1
LP
848 }
849
ef658a63 850 /* Our "canonical" RFC3339 syntax variant without seconds */
9a98c7a1
LP
851 tm = copy;
852 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
853 if (k && *k == 0) {
854 tm.tm_sec = 0;
e4eaf99a 855 goto from_tm;
9a98c7a1
LP
856 }
857
ef658a63 858 /* RFC3339 syntax without seconds */
859 tm = copy;
860 k = strptime(t, "%Y-%m-%dT%H:%M", &tm);
861 if (k && *k == 0) {
862 tm.tm_sec = 0;
863 goto from_tm;
864 }
865
9a98c7a1
LP
866 tm = copy;
867 k = strptime(t, "%y-%m-%d", &tm);
868 if (k && *k == 0) {
869 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 870 goto from_tm;
9a98c7a1
LP
871 }
872
873 tm = copy;
874 k = strptime(t, "%Y-%m-%d", &tm);
875 if (k && *k == 0) {
876 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 877 goto from_tm;
9a98c7a1
LP
878 }
879
880 tm = copy;
881 k = strptime(t, "%H:%M:%S", &tm);
e4eaf99a
HV
882 if (k) {
883 if (*k == '.')
884 goto parse_usec;
885 else if (*k == 0)
886 goto from_tm;
887 }
9a98c7a1
LP
888
889 tm = copy;
890 k = strptime(t, "%H:%M", &tm);
891 if (k && *k == 0) {
892 tm.tm_sec = 0;
e4eaf99a 893 goto from_tm;
9a98c7a1
LP
894 }
895
896 return -EINVAL;
897
e4eaf99a 898parse_usec:
1d2c42c5
YW
899 k++;
900 r = parse_fractional_part_u(&k, 6, &fractional);
901 if (r < 0)
902 return -EINVAL;
903 if (*k != '\0')
904 return -EINVAL;
e4eaf99a
HV
905
906from_tm:
7a9afae6
YW
907 assert(plus == 0);
908 assert(minus == 0);
909
214cc95d 910 if (weekday >= 0 && tm.tm_wday != weekday)
68bdd2d2 911 return -EINVAL;
9a98c7a1 912
7a9afae6
YW
913 if (gmtoff < 0) {
914 plus = -gmtoff * USEC_PER_SEC;
915
ca9c9d8d 916 /* If gmtoff is negative, the string may be too old to be parsed as UTC.
7a9afae6
YW
917 * E.g. 1969-12-31 23:00:00 -06 == 1970-01-01 05:00:00 UTC
918 * We assumed that gmtoff is in the range of -24:00…+24:00, hence the only date we need to
919 * handle here is 1969-12-31. So, let's shift the date with one day, then subtract the shift
920 * later. */
921 if (tm.tm_year == 69 && tm.tm_mon == 11 && tm.tm_mday == 31) {
922 /* Thu 1970-01-01-00:00:00 */
923 tm.tm_year = 70;
924 tm.tm_mon = 0;
925 tm.tm_mday = 1;
926 tm.tm_wday = 4;
927 tm.tm_yday = 0;
928 minus = USEC_PER_DAY;
929 }
930 } else
931 minus = gmtoff * USEC_PER_SEC;
932
6f5cf415
LP
933 r = mktime_or_timegm_usec(&tm, utc, &usec);
934 if (r < 0)
935 return r;
92134489 936
6f5cf415 937 usec = usec_add(usec, fractional);
9a98c7a1 938
e4eaf99a 939finish:
db43717e
YW
940 usec = usec_add(usec, plus);
941
942 if (usec < minus)
1bb4b028
LP
943 return -EINVAL;
944
db43717e
YW
945 usec = usec_sub_unsigned(usec, minus);
946
947 if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
68bdd2d2 948 return -EINVAL;
9a98c7a1 949
cf98b66d
YW
950 if (ret)
951 *ret = usec;
9a98c7a1
LP
952 return 0;
953}
954
8beb47c8 955static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool valid_tz, usec_t *ret) {
7a9afae6 956 assert(t);
7a9afae6
YW
957
958 tzset();
959
960 for (int j = 0; j <= 1; j++) {
961 if (isempty(tzname[j]))
962 continue;
963
8beb47c8 964 if (!streq(t + tz_offset, tzname[j]))
7a9afae6
YW
965 continue;
966
967 /* The specified timezone matches tzname[] of the local timezone. */
ef658a63 968 return parse_timestamp_impl(t, tz_offset - 1, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
7a9afae6
YW
969 }
970
87e0fd57
YW
971 /* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone
972 * and parse the remaining string as a local time. If we know that the last word is not a timezone,
973 * then assume that it is a part of the time and try to parse the whole string as a local time. */
ef658a63 974 return parse_timestamp_impl(t, valid_tz ? tz_offset - 1 : SIZE_MAX,
87e0fd57 975 /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
7a9afae6
YW
976}
977
48d26c01
IK
978typedef struct ParseTimestampResult {
979 usec_t usec;
980 int return_value;
981} ParseTimestampResult;
982
cf98b66d 983int parse_timestamp(const char *t, usec_t *ret) {
48d26c01 984 ParseTimestampResult *shared, tmp;
8beb47c8 985 const char *k, *tz, *current_tz;
ef658a63 986 size_t max_len, t_len;
7a9afae6 987 struct tm tm;
4c253ed1 988 int r;
48d26c01 989
dff3bddc
YW
990 assert(t);
991
ef658a63 992 t_len = strlen(t);
eb87d3e1
YW
993 if (t_len > 2 && t[t_len - 1] == 'Z') {
994 /* Try to parse as RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */
995 r = parse_timestamp_impl(t, t_len - 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
996 if (r >= 0)
997 return r;
998 }
ef658a63 999
1000 if (t_len > 7 && IN_SET(t[t_len - 6], '+', '-') && t[t_len - 7] != ' ') { /* RFC3339-style welded offset: "1990-12-31T15:59:60-08:00" */
1001 k = strptime(&t[t_len - 6], "%z", &tm);
1002 if (k && *k == '\0')
1003 return parse_timestamp_impl(t, t_len - 6, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
1004 }
1005
8beb47c8
YW
1006 tz = strrchr(t, ' ');
1007 if (!tz)
ef658a63 1008 return parse_timestamp_impl(t, /* max_len = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
48d26c01 1009
ef658a63 1010 max_len = tz - t;
8beb47c8 1011 tz++;
7a9afae6
YW
1012
1013 /* Shortcut, parse the string as UTC. */
8beb47c8 1014 if (streq(tz, "UTC"))
ef658a63 1015 return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
7a9afae6
YW
1016
1017 /* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
87e0fd57
YW
1018 * UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as
1019 * tzname[] may be in the same format. */
8beb47c8 1020 k = strptime(tz, "%z", &tm);
9f819781 1021 if (k && *k == '\0')
ef658a63 1022 return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
7a9afae6
YW
1023
1024 /* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
1025 * tzname[] of the local timezone, e.g. JST or CEST. */
8beb47c8 1026 if (!timezone_is_valid(tz, LOG_DEBUG))
ef658a63 1027 return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ false, ret);
7a9afae6
YW
1028
1029 /* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
1030 * the process. */
8beb47c8
YW
1031 current_tz = getenv("TZ");
1032 if (current_tz && *current_tz == ':' && streq(current_tz + 1, tz))
ef658a63 1033 return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, ret);
7a9afae6
YW
1034
1035 /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
1036 * the specified timezone in the child process. */
48d26c01 1037
48d26c01
IK
1038 shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
1039 if (shared == MAP_FAILED)
1040 return negative_errno();
1041
6d3012ba
YW
1042 /* The input string may be in argv. Let's copy it. */
1043 _cleanup_free_ char *t_copy = strdup(t);
1044 if (!t_copy)
1045 return -ENOMEM;
1046
1047 t = t_copy;
1048 assert_se(tz = endswith(t_copy, tz));
1049
e9ccae31 1050 r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL);
4c253ed1 1051 if (r < 0) {
48d26c01 1052 (void) munmap(shared, sizeof *shared);
4c253ed1 1053 return r;
48d26c01 1054 }
4c253ed1 1055 if (r == 0) {
7a9afae6 1056 const char *colon_tz;
3fd4929b 1057
437f48a4 1058 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
8beb47c8 1059 colon_tz = strjoina(":", tz);
437f48a4
LP
1060
1061 if (setenv("TZ", colon_tz, 1) != 0) {
48d26c01
IK
1062 shared->return_value = negative_errno();
1063 _exit(EXIT_FAILURE);
1064 }
1065
ef658a63 1066 shared->return_value = parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, &shared->usec);
48d26c01
IK
1067
1068 _exit(EXIT_SUCCESS);
1069 }
1070
48d26c01
IK
1071 tmp = *shared;
1072 if (munmap(shared, sizeof *shared) != 0)
1073 return negative_errno();
1074
cf98b66d
YW
1075 if (tmp.return_value == 0 && ret)
1076 *ret = tmp.usec;
48d26c01
IK
1077
1078 return tmp.return_value;
1079}
1080
cf98b66d 1081static const char* extract_multiplier(const char *p, usec_t *ret) {
9a98c7a1
LP
1082 static const struct {
1083 const char *suffix;
1084 usec_t usec;
1085 } table[] = {
eb55ec9f
LP
1086 { "seconds", USEC_PER_SEC },
1087 { "second", USEC_PER_SEC },
1088 { "sec", USEC_PER_SEC },
1089 { "s", USEC_PER_SEC },
9a98c7a1 1090 { "minutes", USEC_PER_MINUTE },
eb55ec9f
LP
1091 { "minute", USEC_PER_MINUTE },
1092 { "min", USEC_PER_MINUTE },
1093 { "months", USEC_PER_MONTH },
1094 { "month", USEC_PER_MONTH },
1095 { "M", USEC_PER_MONTH },
1096 { "msec", USEC_PER_MSEC },
1097 { "ms", USEC_PER_MSEC },
1098 { "m", USEC_PER_MINUTE },
1099 { "hours", USEC_PER_HOUR },
1100 { "hour", USEC_PER_HOUR },
1101 { "hr", USEC_PER_HOUR },
1102 { "h", USEC_PER_HOUR },
1103 { "days", USEC_PER_DAY },
1104 { "day", USEC_PER_DAY },
1105 { "d", USEC_PER_DAY },
1106 { "weeks", USEC_PER_WEEK },
1107 { "week", USEC_PER_WEEK },
1108 { "w", USEC_PER_WEEK },
1109 { "years", USEC_PER_YEAR },
1110 { "year", USEC_PER_YEAR },
1111 { "y", USEC_PER_YEAR },
1112 { "usec", 1ULL },
1113 { "us", 1ULL },
d0a6d7c4
LP
1114 { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */
1115 { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */
9a98c7a1 1116 };
240a7ba9 1117
dff3bddc
YW
1118 assert(p);
1119 assert(ret);
1120
ddb8a639 1121 FOREACH_ELEMENT(i, table) {
240a7ba9
ZJS
1122 char *e;
1123
ddb8a639 1124 e = startswith(p, i->suffix);
240a7ba9 1125 if (e) {
ddb8a639 1126 *ret = i->usec;
240a7ba9
ZJS
1127 return e;
1128 }
1129 }
9a98c7a1 1130
240a7ba9
ZJS
1131 return p;
1132}
1133
cf98b66d 1134int parse_time(const char *t, usec_t *ret, usec_t default_unit) {
b1d6dcf5 1135 const char *p, *s;
9a98c7a1
LP
1136
1137 assert(t);
519cffec 1138 assert(default_unit > 0);
9a98c7a1 1139
5dfccccc 1140 p = skip_leading_chars(t, /* bad = */ NULL);
b1d6dcf5
ZJS
1141 s = startswith(p, "infinity");
1142 if (s) {
5dfccccc 1143 if (!in_charset(s, WHITESPACE))
b1d6dcf5
ZJS
1144 return -EINVAL;
1145
cf98b66d
YW
1146 if (ret)
1147 *ret = USEC_INFINITY;
b1d6dcf5
ZJS
1148 return 0;
1149 }
1150
5dfccccc
MY
1151 usec_t usec = 0;
1152
1153 for (bool something = false;;) {
5a9fb358 1154 usec_t multiplier = default_unit, k;
ed2e7967 1155 long long l;
5a9fb358 1156 char *e;
cb0dac05 1157
5dfccccc 1158 p = skip_leading_chars(p, /* bad = */ NULL);
cb0dac05
LP
1159 if (*p == 0) {
1160 if (!something)
1161 return -EINVAL;
1162
1163 break;
1164 }
9a98c7a1 1165
5a9fb358
LP
1166 if (*p == '-') /* Don't allow "-0" */
1167 return -ERANGE;
1168
9a98c7a1
LP
1169 errno = 0;
1170 l = strtoll(p, &e, 10);
8333c77e 1171 if (errno > 0)
9a98c7a1 1172 return -errno;
9a98c7a1
LP
1173 if (l < 0)
1174 return -ERANGE;
1175
cb0dac05 1176 if (*e == '.') {
ed2e7967
YW
1177 p = e + 1;
1178 p += strspn(p, DIGITS);
cb0dac05 1179 } else if (e == p)
9a98c7a1 1180 return -EINVAL;
ed2e7967
YW
1181 else
1182 p = e;
9a98c7a1 1183
ed2e7967
YW
1184 s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
1185 if (s == p && *s != '\0')
7802194a 1186 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
ed2e7967 1187 return -EINVAL;
9a98c7a1 1188
ed2e7967 1189 p = s;
519cffec 1190
ed2e7967
YW
1191 if ((usec_t) l >= USEC_INFINITY / multiplier)
1192 return -ERANGE;
8079c903 1193
ed2e7967 1194 k = (usec_t) l * multiplier;
cf98b66d 1195 if (k >= USEC_INFINITY - usec)
8079c903
YW
1196 return -ERANGE;
1197
cf98b66d 1198 usec += k;
519cffec 1199
ed2e7967 1200 something = true;
519cffec 1201
ed2e7967
YW
1202 if (*e == '.') {
1203 usec_t m = multiplier / 10;
1204 const char *b;
8079c903 1205
ed2e7967
YW
1206 for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
1207 k = (usec_t) (*b - '0') * m;
cf98b66d 1208 if (k >= USEC_INFINITY - usec)
ed2e7967
YW
1209 return -ERANGE;
1210
cf98b66d 1211 usec += k;
ed2e7967
YW
1212 }
1213
7802194a 1214 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
ed2e7967
YW
1215 if (b == e + 1)
1216 return -EINVAL;
1217 }
cb0dac05 1218 }
9a98c7a1 1219
cf98b66d
YW
1220 if (ret)
1221 *ret = usec;
9a98c7a1
LP
1222 return 0;
1223}
1224
cf98b66d
YW
1225int parse_sec(const char *t, usec_t *ret) {
1226 return parse_time(t, ret, USEC_PER_SEC);
519cffec
LP
1227}
1228
def34f63
LP
1229int parse_sec_fix_0(const char *t, usec_t *ret) {
1230 usec_t k;
1231 int r;
eae51da3 1232
def34f63
LP
1233 assert(t);
1234 assert(ret);
eae51da3 1235
def34f63
LP
1236 r = parse_sec(t, &k);
1237 if (r < 0)
1238 return r;
0004f698 1239
def34f63
LP
1240 *ret = k == 0 ? USEC_INFINITY : k;
1241 return r;
0004f698
ZJS
1242}
1243
7b61ce3c 1244int parse_sec_def_infinity(const char *t, usec_t *ret) {
dff3bddc
YW
1245 assert(t);
1246 assert(ret);
1247
7b61ce3c
FB
1248 t += strspn(t, WHITESPACE);
1249 if (isempty(t)) {
1250 *ret = USEC_INFINITY;
1251 return 0;
1252 }
1253 return parse_sec(t, ret);
1254}
1255
cf98b66d 1256static const char* extract_nsec_multiplier(const char *p, nsec_t *ret) {
9a98c7a1
LP
1257 static const struct {
1258 const char *suffix;
1259 nsec_t nsec;
1260 } table[] = {
ed2e7967
YW
1261 { "seconds", NSEC_PER_SEC },
1262 { "second", NSEC_PER_SEC },
1263 { "sec", NSEC_PER_SEC },
1264 { "s", NSEC_PER_SEC },
9a98c7a1 1265 { "minutes", NSEC_PER_MINUTE },
ed2e7967
YW
1266 { "minute", NSEC_PER_MINUTE },
1267 { "min", NSEC_PER_MINUTE },
1268 { "months", NSEC_PER_MONTH },
1269 { "month", NSEC_PER_MONTH },
1270 { "M", NSEC_PER_MONTH },
1271 { "msec", NSEC_PER_MSEC },
1272 { "ms", NSEC_PER_MSEC },
1273 { "m", NSEC_PER_MINUTE },
1274 { "hours", NSEC_PER_HOUR },
1275 { "hour", NSEC_PER_HOUR },
1276 { "hr", NSEC_PER_HOUR },
1277 { "h", NSEC_PER_HOUR },
1278 { "days", NSEC_PER_DAY },
1279 { "day", NSEC_PER_DAY },
1280 { "d", NSEC_PER_DAY },
1281 { "weeks", NSEC_PER_WEEK },
1282 { "week", NSEC_PER_WEEK },
1283 { "w", NSEC_PER_WEEK },
1284 { "years", NSEC_PER_YEAR },
1285 { "year", NSEC_PER_YEAR },
1286 { "y", NSEC_PER_YEAR },
1287 { "usec", NSEC_PER_USEC },
1288 { "us", NSEC_PER_USEC },
d0a6d7c4
LP
1289 { "μs", NSEC_PER_USEC }, /* U+03bc (aka GREEK LETTER MU) */
1290 { "µs", NSEC_PER_USEC }, /* U+b5 (aka MICRO SIGN) */
ed2e7967
YW
1291 { "nsec", 1ULL },
1292 { "ns", 1ULL },
1293 { "", 1ULL }, /* default is nsec */
9a98c7a1 1294 };
ed2e7967 1295
dff3bddc
YW
1296 assert(p);
1297 assert(ret);
1298
ddb8a639 1299 FOREACH_ELEMENT(i, table) {
ed2e7967
YW
1300 char *e;
1301
ddb8a639 1302 e = startswith(p, i->suffix);
ed2e7967 1303 if (e) {
ddb8a639 1304 *ret = i->nsec;
ed2e7967
YW
1305 return e;
1306 }
1307 }
1308
1309 return p;
1310}
9a98c7a1 1311
cf98b66d 1312int parse_nsec(const char *t, nsec_t *ret) {
e73c78c2 1313 const char *p, *s;
cf98b66d 1314 nsec_t nsec = 0;
cb0dac05 1315 bool something = false;
9a98c7a1
LP
1316
1317 assert(t);
cf98b66d 1318 assert(ret);
9a98c7a1
LP
1319
1320 p = t;
e73c78c2
LP
1321
1322 p += strspn(p, WHITESPACE);
1323 s = startswith(p, "infinity");
1324 if (s) {
1325 s += strspn(s, WHITESPACE);
8e8933ca 1326 if (*s != 0)
e73c78c2
LP
1327 return -EINVAL;
1328
cf98b66d 1329 *ret = NSEC_INFINITY;
e73c78c2
LP
1330 return 0;
1331 }
1332
cb0dac05 1333 for (;;) {
ed2e7967
YW
1334 nsec_t multiplier = 1, k;
1335 long long l;
9a98c7a1 1336 char *e;
cb0dac05
LP
1337
1338 p += strspn(p, WHITESPACE);
1339
1340 if (*p == 0) {
1341 if (!something)
1342 return -EINVAL;
1343
1344 break;
1345 }
9a98c7a1 1346
ed2e7967 1347 if (*p == '-') /* Don't allow "-0" */
5a9fb358
LP
1348 return -ERANGE;
1349
9a98c7a1
LP
1350 errno = 0;
1351 l = strtoll(p, &e, 10);
8333c77e 1352 if (errno > 0)
9a98c7a1 1353 return -errno;
9a98c7a1
LP
1354 if (l < 0)
1355 return -ERANGE;
1356
cb0dac05 1357 if (*e == '.') {
ed2e7967
YW
1358 p = e + 1;
1359 p += strspn(p, DIGITS);
cb0dac05 1360 } else if (e == p)
9a98c7a1 1361 return -EINVAL;
ed2e7967
YW
1362 else
1363 p = e;
9a98c7a1 1364
ed2e7967
YW
1365 s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
1366 if (s == p && *s != '\0')
7802194a 1367 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
ed2e7967 1368 return -EINVAL;
9a98c7a1 1369
ed2e7967 1370 p = s;
f6a178e9 1371
ed2e7967
YW
1372 if ((nsec_t) l >= NSEC_INFINITY / multiplier)
1373 return -ERANGE;
1374
1375 k = (nsec_t) l * multiplier;
cf98b66d 1376 if (k >= NSEC_INFINITY - nsec)
ed2e7967 1377 return -ERANGE;
f6a178e9 1378
cf98b66d 1379 nsec += k;
ed2e7967
YW
1380
1381 something = true;
cb0dac05 1382
ed2e7967
YW
1383 if (*e == '.') {
1384 nsec_t m = multiplier / 10;
1385 const char *b;
cb0dac05 1386
ed2e7967
YW
1387 for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
1388 k = (nsec_t) (*b - '0') * m;
cf98b66d 1389 if (k >= NSEC_INFINITY - nsec)
f6a178e9
YW
1390 return -ERANGE;
1391
cf98b66d 1392 nsec += k;
9a98c7a1
LP
1393 }
1394
7802194a 1395 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
ed2e7967
YW
1396 if (b == e + 1)
1397 return -EINVAL;
1398 }
cb0dac05 1399 }
9a98c7a1 1400
cf98b66d 1401 *ret = nsec;
9a98c7a1
LP
1402
1403 return 0;
1404}
03cc26dd 1405
09a54a86 1406static int get_timezones_from_zone1970_tab(char ***ret) {
75683450
LP
1407 _cleanup_fclose_ FILE *f = NULL;
1408 _cleanup_strv_free_ char **zones = NULL;
8d2b9d14 1409 int r;
75683450
LP
1410
1411 assert(ret);
1412
ba32084f 1413 f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
09a54a86
DS
1414 if (!f)
1415 return -errno;
75683450 1416
09a54a86
DS
1417 for (;;) {
1418 _cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
8d2b9d14 1419
09a54a86
DS
1420 r = read_line(f, LONG_LINE_MAX, &line);
1421 if (r < 0)
1422 return r;
1423 if (r == 0)
1424 break;
75683450 1425
09a54a86 1426 const char *p = line;
75683450 1427
09a54a86
DS
1428 /* Line format is:
1429 * 'country codes' 'coordinates' 'timezone' 'comments' */
4f495126 1430 r = extract_many_words(&p, NULL, 0, &cc, &co, &tz);
09a54a86
DS
1431 if (r < 0)
1432 continue;
75683450 1433
09a54a86
DS
1434 /* Lines that start with # are comments. */
1435 if (*cc == '#')
1436 continue;
1437
1438 r = strv_extend(&zones, tz);
1439 if (r < 0)
1440 return r;
1441 }
1442
1443 *ret = TAKE_PTR(zones);
1444 return 0;
1445}
1446
147bc363
DS
1447static int get_timezones_from_tzdata_zi(char ***ret) {
1448 _cleanup_fclose_ FILE *f = NULL;
1449 _cleanup_strv_free_ char **zones = NULL;
1450 int r;
1451
dff3bddc
YW
1452 assert(ret);
1453
147bc363
DS
1454 f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
1455 if (!f)
1456 return -errno;
1457
1458 for (;;) {
1459 _cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
1460
1461 r = read_line(f, LONG_LINE_MAX, &line);
1462 if (r < 0)
1463 return r;
1464 if (r == 0)
1465 break;
1466
1467 const char *p = line;
1468
1469 /* The only lines we care about are Zone and Link lines.
1470 * Zone line format is:
1471 * 'Zone' 'timezone' ...
1472 * Link line format is:
1473 * 'Link' 'target' 'alias'
1474 * See 'man zic' for more detail. */
4f495126 1475 r = extract_many_words(&p, NULL, 0, &type, &f1, &f2);
147bc363
DS
1476 if (r < 0)
1477 continue;
1478
1479 char *tz;
2f092762 1480 if (IN_SET(*type, 'Z', 'z'))
147bc363
DS
1481 /* Zone lines have timezone in field 1. */
1482 tz = f1;
2f092762 1483 else if (IN_SET(*type, 'L', 'l'))
147bc363
DS
1484 /* Link lines have timezone in field 2. */
1485 tz = f2;
1486 else
1487 /* Not a line we care about. */
1488 continue;
1489
1490 r = strv_extend(&zones, tz);
1491 if (r < 0)
1492 return r;
1493 }
1494
1495 *ret = TAKE_PTR(zones);
1496 return 0;
1497}
1498
09a54a86
DS
1499int get_timezones(char ***ret) {
1500 _cleanup_strv_free_ char **zones = NULL;
1501 int r;
1502
1503 assert(ret);
1504
147bc363
DS
1505 r = get_timezones_from_tzdata_zi(&zones);
1506 if (r == -ENOENT) {
1507 log_debug_errno(r, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
1508 r = get_timezones_from_zone1970_tab(&zones);
1509 if (r == -ENOENT)
1510 log_debug_errno(r, "Could not get timezone data from zone1970.tab, using UTC: %m");
1511 }
09a54a86
DS
1512 if (r < 0 && r != -ENOENT)
1513 return r;
75683450 1514
31097e2b
DS
1515 /* Always include UTC */
1516 r = strv_extend(&zones, "UTC");
1517 if (r < 0)
010cd1dc 1518 return r;
75683450 1519
e367b426 1520 strv_sort_uniq(zones);
31097e2b
DS
1521
1522 *ret = TAKE_PTR(zones);
75683450
LP
1523 return 0;
1524}
1525
bdaeafea 1526int verify_timezone(const char *name, int log_level) {
75683450
LP
1527 bool slash = false;
1528 const char *p, *t;
254d1313 1529 _cleanup_close_ int fd = -EBADF;
a2932d51
MG
1530 char buf[4];
1531 int r;
75683450 1532
5c904ba5 1533 if (isempty(name))
bdaeafea 1534 return -EINVAL;
5c904ba5 1535
e8b9e9c4
LP
1536 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1537 if (streq(name, "UTC"))
bdaeafea 1538 return 0;
e8b9e9c4 1539
5c904ba5 1540 if (name[0] == '/')
bdaeafea 1541 return -EINVAL;
75683450
LP
1542
1543 for (p = name; *p; p++) {
ff25d338
LP
1544 if (!ascii_isdigit(*p) &&
1545 !ascii_isalpha(*p) &&
4c701096 1546 !IN_SET(*p, '-', '_', '+', '/'))
bdaeafea 1547 return -EINVAL;
75683450
LP
1548
1549 if (*p == '/') {
1550
1551 if (slash)
bdaeafea 1552 return -EINVAL;
75683450
LP
1553
1554 slash = true;
1555 } else
1556 slash = false;
1557 }
1558
1559 if (slash)
bdaeafea 1560 return -EINVAL;
75683450 1561
1c73b60b 1562 if (p - name >= PATH_MAX)
bdaeafea 1563 return -ENAMETOOLONG;
1c73b60b 1564
63c372cb 1565 t = strjoina("/usr/share/zoneinfo/", name);
a2932d51
MG
1566
1567 fd = open(t, O_RDONLY|O_CLOEXEC);
bdaeafea
ZJS
1568 if (fd < 0)
1569 return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
a2932d51
MG
1570
1571 r = fd_verify_regular(fd);
bdaeafea 1572 if (r < 0)
2560dcbf 1573 return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
a2932d51
MG
1574
1575 r = loop_read_exact(fd, buf, 4, false);
bdaeafea
ZJS
1576 if (r < 0)
1577 return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
75683450 1578
a2932d51 1579 /* Magic from tzfile(5) */
bdaeafea 1580 if (memcmp(buf, "TZif", 4) != 0)
8def4b34 1581 return log_full_errno(log_level, SYNTHETIC_ERRNO(EBADMSG),
bdaeafea 1582 "Timezone file '%s' has wrong magic bytes", t);
75683450 1583
bdaeafea 1584 return 0;
75683450 1585}
77ff2de9 1586
fe624c4c
LP
1587bool clock_supported(clockid_t clock) {
1588 struct timespec ts;
1589
1590 switch (clock) {
1591
1592 case CLOCK_MONOTONIC:
1593 case CLOCK_REALTIME:
fe624c4c 1594 case CLOCK_BOOTTIME:
ba4e0427
LP
1595 /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */
1596 return true;
fe624c4c 1597
fe624c4c
LP
1598 default:
1599 /* For everything else, check properly */
1600 return clock_gettime(clock, &ts) >= 0;
1601 }
1602}
1603
bc9ecd48 1604int get_timezone(char **ret) {
5c904ba5 1605 _cleanup_free_ char *t = NULL;
5c904ba5
LP
1606 int r;
1607
dff3bddc
YW
1608 assert(ret);
1609
0dc39dff 1610 r = readlink_malloc(etc_localtime(), &t);
4ab305fa 1611 if (r == -ENOENT)
7802194a 1612 /* If the symlink does not exist, assume "UTC", like glibc does */
4ab305fa 1613 return strdup_to(ret, "UTC");
5c904ba5 1614 if (r < 0)
4ab305fa 1615 return r; /* Return EINVAL if not a symlink */
5c904ba5 1616
4ab305fa 1617 const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
5c904ba5
LP
1618 if (!e)
1619 return -EINVAL;
089fb865 1620 if (!timezone_is_valid(e, LOG_DEBUG))
5c904ba5
LP
1621 return -EINVAL;
1622
4ab305fa 1623 return strdup_to(ret, e);
5c904ba5 1624}
7c67c79c 1625
0dc39dff
VD
1626const char* etc_localtime(void) {
1627 static const char *cached = NULL;
1628
1629 if (!cached)
1630 cached = secure_getenv("SYSTEMD_ETC_LOCALTIME") ?: "/etc/localtime";
1631
1632 return cached;
1633}
1634
6f5cf415
LP
1635int mktime_or_timegm_usec(
1636 struct tm *tm, /* input + normalized output */
1637 bool utc,
1638 usec_t *ret) {
1639
1640 time_t t;
1641
dff3bddc
YW
1642 assert(tm);
1643
d7306348 1644 if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!) */
6f5cf415
LP
1645 return -ERANGE;
1646 if ((usec_t) tm->tm_year > CONST_MIN(USEC_INFINITY / USEC_PER_YEAR, (usec_t) TIME_T_MAX / (365U * 24U * 60U * 60U)) - 1900) /* early check for possible overrun of usec_t or time_t */
1647 return -ERANGE;
1648
1649 /* timegm()/mktime() is a bit weird to use, since it returns -1 in two cases: on error as well as a
1650 * valid time indicating one second before the UNIX epoch. Let's treat both cases the same here, and
1651 * return -ERANGE for anything negative, since usec_t is unsigned, and we can thus not express
1652 * negative times anyway. */
1653
1654 t = utc ? timegm(tm) : mktime(tm);
1655 if (t < 0) /* Refuse negative times and errors */
1656 return -ERANGE;
1657 if ((usec_t) t >= USEC_INFINITY / USEC_PER_SEC) /* Never return USEC_INFINITY by accident (or overflow) */
1658 return -ERANGE;
1659
1660 if (ret)
1661 *ret = (usec_t) t * USEC_PER_SEC;
1662 return 0;
7c67c79c
HV
1663}
1664
6f5cf415
LP
1665int localtime_or_gmtime_usec(
1666 usec_t t,
1667 bool utc,
1668 struct tm *ret) {
dff3bddc 1669
6f5cf415
LP
1670 t /= USEC_PER_SEC; /* Round down */
1671 if (t > (usec_t) TIME_T_MAX)
1672 return -ERANGE;
1673 time_t sec = (time_t) t;
1674
1675 struct tm buf = {};
1676 if (!(utc ? gmtime_r(&sec, &buf) : localtime_r(&sec, &buf)))
1677 return -EINVAL;
1678
1679 if (ret)
1680 *ret = buf;
1681
1682 return 0;
7c67c79c 1683}
87b8ce69 1684
77372afb
YW
1685static uint32_t sysconf_clock_ticks_cached(void) {
1686 static thread_local uint32_t hz = 0;
87b8ce69
SS
1687 long r;
1688
1689 if (hz == 0) {
1690 r = sysconf(_SC_CLK_TCK);
1691
1692 assert(r > 0);
70887c5f 1693 hz = r;
87b8ce69
SS
1694 }
1695
77372afb
YW
1696 return hz;
1697}
1698
1699uint32_t usec_to_jiffies(usec_t u) {
1700 uint32_t hz = sysconf_clock_ticks_cached();
1701 return DIV_ROUND_UP(u, USEC_PER_SEC / hz);
1702}
1703
1704usec_t jiffies_to_usec(uint32_t j) {
1705 uint32_t hz = sysconf_clock_ticks_cached();
1706 return DIV_ROUND_UP(j * USEC_PER_SEC, hz);
87b8ce69 1707}
1007ec60
LP
1708
1709usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
1710 usec_t a, b;
1711
1712 if (x == USEC_INFINITY)
1713 return USEC_INFINITY;
1714 if (map_clock_id(from) == map_clock_id(to))
1715 return x;
1716
1717 a = now(from);
1718 b = now(to);
1719
1720 if (x > a)
1721 /* x lies in the future */
1722 return usec_add(b, usec_sub_unsigned(x, a));
1723 else
1724 /* x lies in the past */
1725 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
1726}
9a9a4f10
LP
1727
1728bool in_utc_timezone(void) {
1729 tzset();
1730
1731 return timezone == 0 && daylight == 0;
1732}
4f811d27
LP
1733
1734int time_change_fd(void) {
1735
1736 /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1737 static const struct itimerspec its = {
1738 .it_value.tv_sec = TIME_T_MAX,
1739 };
1740
254d1313 1741 _cleanup_close_ int fd = -EBADF;
4f811d27
LP
1742
1743 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
1744
1745 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1746 * CLOCK_MONOTONIC. */
1747
1748 fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1749 if (fd < 0)
1750 return -errno;
1751
601f91be
LP
1752 if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0)
1753 return TAKE_FD(fd);
1754
da890466
ZJS
1755 /* So apparently there are systems where time_t is 64-bit, but the kernel actually doesn't support
1756 * 64-bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
601f91be
LP
1757 * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
1758 * of a black magic thing though, but what can we do?
1759 *
da890466
ZJS
1760 * We don't want this code on x86-64, hence let's conditionalize this for systems with 64-bit time_t
1761 * but where "long" is shorter than 64-bit, i.e. 32-bit archs.
601f91be
LP
1762 *
1763 * See: https://github.com/systemd/systemd/issues/14362 */
1764
1765#if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
9e7c8f64 1766 if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EOVERFLOW) {
601f91be
LP
1767 static const struct itimerspec its32 = {
1768 .it_value.tv_sec = INT32_MAX,
1769 };
1770
1771 if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its32, NULL) >= 0)
1772 return TAKE_FD(fd);
1773 }
1774#endif
4f811d27 1775
601f91be 1776 return -errno;
4f811d27 1777}
7b3eb5c9
LB
1778
1779static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
1780 [TIMESTAMP_PRETTY] = "pretty",
e01a8fdd
YW
1781 [TIMESTAMP_US] = "us",
1782 [TIMESTAMP_UTC] = "utc",
7b3eb5c9 1783 [TIMESTAMP_US_UTC] = "us+utc",
e01a8fdd 1784 [TIMESTAMP_UNIX] = "unix",
7b3eb5c9
LB
1785};
1786
1787/* Use the macro for enum → string to allow for aliases */
d227a42a 1788DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle);
7b3eb5c9
LB
1789
1790/* For the string → enum mapping we use the generic implementation, but also support two aliases */
1791TimestampStyle timestamp_style_from_string(const char *s) {
1792 TimestampStyle t;
1793
c94f6ab1 1794 t = (TimestampStyle) string_table_lookup_from_string(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
7b3eb5c9
LB
1795 if (t >= 0)
1796 return t;
9a27ef09 1797 if (STRPTR_IN_SET(s, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
7b3eb5c9 1798 return TIMESTAMP_US;
d0a6d7c4 1799 if (STRPTR_IN_SET(s, "µs+utc", "μs+utc"))
7b3eb5c9
LB
1800 return TIMESTAMP_US_UTC;
1801 return t;
1802}