]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/time-util.c
hwdb: add Lenovo X1 Tablet pointing stick speed fix (#4128)
[thirdparty/systemd.git] / src / basic / time-util.c
CommitLineData
9a98c7a1
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
11c3a366
TA
20#include <errno.h>
21#include <limits.h>
22#include <stdlib.h>
9a98c7a1 23#include <string.h>
11c3a366
TA
24#include <sys/stat.h>
25#include <sys/time.h>
77ff2de9 26#include <sys/timerfd.h>
07630cea 27#include <sys/timex.h>
11c3a366
TA
28#include <sys/types.h>
29#include <unistd.h>
9a98c7a1 30
b5efdb8a 31#include "alloc-util.h"
3ffd4af2 32#include "fd-util.h"
0d39fa9c 33#include "fileio.h"
f4f15635 34#include "fs-util.h"
11c3a366
TA
35#include "log.h"
36#include "macro.h"
93cc7779
TA
37#include "parse-util.h"
38#include "path-util.h"
07630cea 39#include "string-util.h"
75683450 40#include "strv.h"
07630cea 41#include "time-util.h"
9a98c7a1 42
aaea9db8
AK
43static nsec_t timespec_load_nsec(const struct timespec *ts);
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;
5d634ca8 112 ts->monotonic = usec_sub(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;
129 ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta);
130 ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
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;
5d634ca8 146 ts->realtime = usec_sub(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;
5d634ca8
AK
161 ts->realtime = usec_sub(ts->realtime, delta);
162 ts->monotonic = usec_sub(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
a2daa2f0 190 if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
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
aaea9db8 201static nsec_t timespec_load_nsec(const struct timespec *ts) {
45d7a8bb
LP
202 assert(ts);
203
a2daa2f0 204 if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
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
3a43da28 216 if (u == USEC_INFINITY) {
9a98c7a1
LP
217 ts->tv_sec = (time_t) -1;
218 ts->tv_nsec = (long) -1;
219 return ts;
220 }
221
222 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
223 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
224
225 return ts;
226}
227
228usec_t timeval_load(const struct timeval *tv) {
229 assert(tv);
230
231 if (tv->tv_sec == (time_t) -1 &&
232 tv->tv_usec == (suseconds_t) -1)
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
3a43da28 246 if (u == USEC_INFINITY) {
9a98c7a1
LP
247 tv->tv_sec = (time_t) -1;
248 tv->tv_usec = (suseconds_t) -1;
4d89874a
ZJS
249 } else {
250 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
251 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
252 }
253
9a98c7a1
LP
254 return tv;
255}
256
21b3a0fc
LP
257static char *format_timestamp_internal(
258 char *buf,
259 size_t l,
260 usec_t t,
261 bool utc,
262 bool us) {
263
264 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
265 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
266 static const char * const weekdays[] = {
267 [0] = "Sun",
268 [1] = "Mon",
269 [2] = "Tue",
270 [3] = "Wed",
271 [4] = "Thu",
272 [5] = "Fri",
273 [6] = "Sat",
274 };
275
9a98c7a1
LP
276 struct tm tm;
277 time_t sec;
21b3a0fc 278 size_t n;
9a98c7a1
LP
279
280 assert(buf);
9a98c7a1 281
21b3a0fc
LP
282 if (l <
283 3 + /* week day */
284 1 + 10 + /* space and date */
285 1 + 8 + /* space and time */
286 (us ? 1 + 6 : 0) + /* "." and microsecond part */
287 1 + 1 + /* space and shortest possible zone */
288 1)
289 return NULL; /* Not enough space even for the shortest form. */
3a43da28 290 if (t <= 0 || t == USEC_INFINITY)
21b3a0fc
LP
291 return NULL; /* Timestamp is unset */
292
293 sec = (time_t) (t / USEC_PER_SEC); /* Round down */
294 if ((usec_t) sec != (t / USEC_PER_SEC))
295 return NULL; /* overflow? */
296
297 if (!localtime_or_gmtime_r(&sec, &tm, utc))
9a98c7a1
LP
298 return NULL;
299
21b3a0fc
LP
300 /* Start with the week day */
301 assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
302 memcpy(buf, weekdays[tm.tm_wday], 4);
9a98c7a1 303
21b3a0fc
LP
304 /* Add the main components */
305 if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
306 return NULL; /* Doesn't fit */
0056086a 307
21b3a0fc 308 /* Append the microseconds part, if that's requested */
0056086a 309 if (us) {
21b3a0fc
LP
310 n = strlen(buf);
311 if (n + 8 > l)
312 return NULL; /* Microseconds part doesn't fit. */
313
314 sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
315 }
316
317 /* Append the timezone */
318 n = strlen(buf);
319 if (utc) {
320 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
321 * obsolete "GMT" instead. */
322 if (n + 5 > l)
323 return NULL; /* "UTC" doesn't fit. */
324
325 strcpy(buf + n, " UTC");
326
327 } else if (!isempty(tm.tm_zone)) {
328 size_t tn;
329
330 /* An explicit timezone is specified, let's use it, if it fits */
331 tn = strlen(tm.tm_zone);
332 if (n + 1 + tn + 1 > l) {
333 /* The full time zone does not fit in. Yuck. */
334
335 if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
336 return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
337
338 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
339 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
340 * an overly long, hard to read string on the user. This should be safe, because the user will
341 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
342 } else {
343 buf[n++] = ' ';
344 strcpy(buf + n, tm.tm_zone);
345 }
0056086a 346 }
9a98c7a1
LP
347
348 return buf;
349}
350
a62e83b4 351char *format_timestamp(char *buf, size_t l, usec_t t) {
0056086a 352 return format_timestamp_internal(buf, l, t, false, false);
a62e83b4
JS
353}
354
5ab99e07 355char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
0056086a 356 return format_timestamp_internal(buf, l, t, true, false);
f02d8367
ZJS
357}
358
5ab99e07 359char *format_timestamp_us(char *buf, size_t l, usec_t t) {
0056086a 360 return format_timestamp_internal(buf, l, t, false, true);
5ab99e07
LP
361}
362
363char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
0056086a 364 return format_timestamp_internal(buf, l, t, true, true);
5ab99e07
LP
365}
366
bbb8486e 367char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 368 const char *s;
9a98c7a1
LP
369 usec_t n, d;
370
65de0395 371 if (t <= 0 || t == USEC_INFINITY)
9a98c7a1
LP
372 return NULL;
373
65de0395 374 n = now(CLOCK_REALTIME);
1fcf71f5
LP
375 if (n > t) {
376 d = n - t;
377 s = "ago";
378 } else {
379 d = t - n;
380 s = "left";
381 }
9a98c7a1
LP
382
383 if (d >= USEC_PER_YEAR)
609e002e 384 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
de0671ee
ZJS
385 d / USEC_PER_YEAR,
386 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
9a98c7a1 387 else if (d >= USEC_PER_MONTH)
609e002e 388 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
de0671ee
ZJS
389 d / USEC_PER_MONTH,
390 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
9a98c7a1 391 else if (d >= USEC_PER_WEEK)
609e002e 392 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
de0671ee
ZJS
393 d / USEC_PER_WEEK,
394 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
9a98c7a1 395 else if (d >= 2*USEC_PER_DAY)
609e002e 396 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
9a98c7a1 397 else if (d >= 25*USEC_PER_HOUR)
609e002e 398 snprintf(buf, l, "1 day " USEC_FMT "h %s",
de0671ee 399 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
9a98c7a1 400 else if (d >= 6*USEC_PER_HOUR)
609e002e 401 snprintf(buf, l, USEC_FMT "h %s",
de0671ee 402 d / USEC_PER_HOUR, s);
9a98c7a1 403 else if (d >= USEC_PER_HOUR)
609e002e 404 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
de0671ee
ZJS
405 d / USEC_PER_HOUR,
406 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
9a98c7a1 407 else if (d >= 5*USEC_PER_MINUTE)
609e002e 408 snprintf(buf, l, USEC_FMT "min %s",
de0671ee 409 d / USEC_PER_MINUTE, s);
9a98c7a1 410 else if (d >= USEC_PER_MINUTE)
609e002e 411 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
de0671ee
ZJS
412 d / USEC_PER_MINUTE,
413 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
9a98c7a1 414 else if (d >= USEC_PER_SEC)
609e002e 415 snprintf(buf, l, USEC_FMT "s %s",
de0671ee 416 d / USEC_PER_SEC, s);
9a98c7a1 417 else if (d >= USEC_PER_MSEC)
609e002e 418 snprintf(buf, l, USEC_FMT "ms %s",
de0671ee 419 d / USEC_PER_MSEC, s);
9a98c7a1 420 else if (d > 0)
de0671ee
ZJS
421 snprintf(buf, l, USEC_FMT"us %s",
422 d, s);
9a98c7a1
LP
423 else
424 snprintf(buf, l, "now");
425
426 buf[l-1] = 0;
427 return buf;
428}
429
2fa4092c 430char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
431 static const struct {
432 const char *suffix;
433 usec_t usec;
434 } table[] = {
eb55ec9f
LP
435 { "y", USEC_PER_YEAR },
436 { "month", USEC_PER_MONTH },
437 { "w", USEC_PER_WEEK },
438 { "d", USEC_PER_DAY },
439 { "h", USEC_PER_HOUR },
440 { "min", USEC_PER_MINUTE },
441 { "s", USEC_PER_SEC },
442 { "ms", USEC_PER_MSEC },
443 { "us", 1 },
9a98c7a1
LP
444 };
445
446 unsigned i;
447 char *p = buf;
2fa4092c 448 bool something = false;
9a98c7a1
LP
449
450 assert(buf);
451 assert(l > 0);
452
bb1fada8
LP
453 if (t == USEC_INFINITY) {
454 strncpy(p, "infinity", l-1);
455 p[l-1] = 0;
456 return p;
457 }
458
459 if (t <= 0) {
460 strncpy(p, "0", l-1);
7c537b2e
LP
461 p[l-1] = 0;
462 return p;
463 }
464
7f602784 465 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
466
467 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 468 int k = 0;
9a98c7a1 469 size_t n;
2fa4092c
LP
470 bool done = false;
471 usec_t a, b;
472
7c537b2e
LP
473 if (t <= 0)
474 break;
2fa4092c 475
7c537b2e 476 if (t < accuracy && something)
2fa4092c 477 break;
9a98c7a1
LP
478
479 if (t < table[i].usec)
480 continue;
481
482 if (l <= 1)
483 break;
484
2fa4092c
LP
485 a = t / table[i].usec;
486 b = t % table[i].usec;
487
488 /* Let's see if we should shows this in dot notation */
489 if (t < USEC_PER_MINUTE && b > 0) {
490 usec_t cc;
491 int j;
492
493 j = 0;
494 for (cc = table[i].usec; cc > 1; cc /= 10)
495 j++;
496
497 for (cc = accuracy; cc > 1; cc /= 10) {
498 b /= 10;
499 j--;
500 }
501
502 if (j > 0) {
503 k = snprintf(p, l,
de0671ee 504 "%s"USEC_FMT".%0*llu%s",
2fa4092c 505 p > buf ? " " : "",
de0671ee 506 a,
2fa4092c
LP
507 j,
508 (unsigned long long) b,
509 table[i].suffix);
510
511 t = 0;
512 done = true;
513 }
514 }
515
516 /* No? Then let's show it normally */
517 if (!done) {
518 k = snprintf(p, l,
de0671ee 519 "%s"USEC_FMT"%s",
2fa4092c 520 p > buf ? " " : "",
de0671ee 521 a,
2fa4092c
LP
522 table[i].suffix);
523
524 t = b;
525 }
526
9a98c7a1
LP
527 n = MIN((size_t) k, l);
528
529 l -= n;
530 p += n;
531
2fa4092c 532 something = true;
9a98c7a1
LP
533 }
534
535 *p = 0;
536
537 return buf;
538}
539
540void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
541
542 assert(f);
543 assert(name);
544 assert(t);
545
546 if (!dual_timestamp_is_set(t))
547 return;
548
de0671ee 549 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
9a98c7a1 550 name,
de0671ee
ZJS
551 t->realtime,
552 t->monotonic);
9a98c7a1
LP
553}
554
e911de99 555int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
9a98c7a1
LP
556 unsigned long long a, b;
557
558 assert(value);
559 assert(t);
560
e911de99 561 if (sscanf(value, "%llu %llu", &a, &b) != 2) {
b895a735 562 log_debug("Failed to parse dual timestamp value \"%s\": %m", value);
e911de99 563 return -EINVAL;
9a98c7a1 564 }
e911de99
LP
565
566 t->realtime = a;
567 t->monotonic = b;
568
569 return 0;
9a98c7a1
LP
570}
571
b895a735 572int timestamp_deserialize(const char *value, usec_t *timestamp) {
ebf30a08
AK
573 int r;
574
575 assert(value);
576
577 r = safe_atou64(value, timestamp);
ebf30a08 578 if (r < 0)
b895a735 579 return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value);
ebf30a08
AK
580
581 return r;
582}
583
9a98c7a1 584int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
585 static const struct {
586 const char *name;
587 const int nr;
588 } day_nr[] = {
589 { "Sunday", 0 },
590 { "Sun", 0 },
591 { "Monday", 1 },
592 { "Mon", 1 },
593 { "Tuesday", 2 },
594 { "Tue", 2 },
595 { "Wednesday", 3 },
596 { "Wed", 3 },
597 { "Thursday", 4 },
598 { "Thu", 4 },
599 { "Friday", 5 },
600 { "Fri", 5 },
601 { "Saturday", 6 },
602 { "Sat", 6 },
603 };
604
21b3a0fc 605 const char *k, *utc, *tzn = NULL;
9a98c7a1
LP
606 struct tm tm, copy;
607 time_t x;
e4eaf99a 608 usec_t x_usec, plus = 0, minus = 0, ret;
21b3a0fc 609 int r, weekday = -1, dst = -1;
92134489 610 unsigned i;
9a98c7a1
LP
611
612 /*
613 * Allowed syntaxes:
614 *
615 * 2012-09-22 16:34:22
616 * 2012-09-22 16:34 (seconds will be set to 0)
617 * 2012-09-22 (time will be set to 00:00:00)
618 * 16:34:22 (date will be set to today)
619 * 16:34 (date will be set to today, seconds to 0)
620 * now
621 * yesterday (time is set to 00:00:00)
622 * today (time is set to 00:00:00)
623 * tomorrow (time is set to 00:00:00)
624 * +5min
625 * -5days
5ba6e094 626 * @2147483647 (seconds since epoch)
9a98c7a1
LP
627 *
628 */
629
630 assert(t);
631 assert(usec);
632
e4eaf99a
HV
633 if (t[0] == '@')
634 return parse_sec(t + 1, usec);
9a98c7a1 635
e4eaf99a 636 ret = now(CLOCK_REALTIME);
9a98c7a1 637
e4eaf99a 638 if (streq(t, "now"))
9a98c7a1
LP
639 goto finish;
640
e4eaf99a 641 else if (t[0] == '+') {
7f602784 642 r = parse_sec(t+1, &plus);
9a98c7a1
LP
643 if (r < 0)
644 return r;
645
646 goto finish;
9a98c7a1 647
5ba6e094 648 } else if (t[0] == '-') {
7f602784 649 r = parse_sec(t+1, &minus);
9a98c7a1
LP
650 if (r < 0)
651 return r;
652
653 goto finish;
decad910 654
078efddd
HV
655 } else if ((k = endswith(t, " ago"))) {
656 t = strndupa(t, k - t);
decad910 657
e4eaf99a 658 r = parse_sec(t, &minus);
decad910
LP
659 if (r < 0)
660 return r;
661
1fcf71f5 662 goto finish;
1fcf71f5 663
078efddd
HV
664 } else if ((k = endswith(t, " left"))) {
665 t = strndupa(t, k - t);
1fcf71f5 666
e4eaf99a 667 r = parse_sec(t, &plus);
1fcf71f5
LP
668 if (r < 0)
669 return r;
670
decad910 671 goto finish;
9a98c7a1
LP
672 }
673
21b3a0fc 674 /* See if the timestamp is suffixed with UTC */
e4eaf99a
HV
675 utc = endswith_no_case(t, " UTC");
676 if (utc)
078efddd 677 t = strndupa(t, utc - t);
21b3a0fc
LP
678 else {
679 const char *e = NULL;
680 int j;
681
682 tzset();
683
684 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
685 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
686 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
687 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
688 * support arbitrary timezone specifications. */
e4eaf99a 689
21b3a0fc
LP
690 for (j = 0; j <= 1; j++) {
691
692 if (isempty(tzname[j]))
693 continue;
694
695 e = endswith_no_case(t, tzname[j]);
696 if (!e)
697 continue;
698 if (e == t)
699 continue;
700 if (e[-1] != ' ')
701 continue;
702
703 break;
704 }
705
706 if (IN_SET(j, 0, 1)) {
707 /* Found one of the two timezones specified. */
708 t = strndupa(t, e - t - 1);
709 dst = j;
710 tzn = tzname[j];
711 }
712 }
713
714 x = (time_t) (ret / USEC_PER_SEC);
e4eaf99a
HV
715 x_usec = 0;
716
21b3a0fc
LP
717 if (!localtime_or_gmtime_r(&x, &tm, utc))
718 return -EINVAL;
719
720 tm.tm_isdst = dst;
721 if (tzn)
722 tm.tm_zone = tzn;
e4eaf99a
HV
723
724 if (streq(t, "today")) {
725 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
726 goto from_tm;
727
728 } else if (streq(t, "yesterday")) {
313cefa1 729 tm.tm_mday--;
e4eaf99a
HV
730 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
731 goto from_tm;
732
733 } else if (streq(t, "tomorrow")) {
313cefa1 734 tm.tm_mday++;
e4eaf99a
HV
735 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
736 goto from_tm;
737 }
738
92134489
LP
739 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
740 size_t skip;
741
742 if (!startswith_no_case(t, day_nr[i].name))
743 continue;
744
745 skip = strlen(day_nr[i].name);
746 if (t[skip] != ' ')
747 continue;
748
749 weekday = day_nr[i].nr;
750 t += skip + 1;
751 break;
752 }
753
9a98c7a1
LP
754 copy = tm;
755 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
756 if (k) {
757 if (*k == '.')
758 goto parse_usec;
759 else if (*k == 0)
760 goto from_tm;
761 }
9a98c7a1
LP
762
763 tm = copy;
764 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
765 if (k) {
766 if (*k == '.')
767 goto parse_usec;
768 else if (*k == 0)
769 goto from_tm;
770 }
9a98c7a1
LP
771
772 tm = copy;
773 k = strptime(t, "%y-%m-%d %H:%M", &tm);
774 if (k && *k == 0) {
775 tm.tm_sec = 0;
e4eaf99a 776 goto from_tm;
9a98c7a1
LP
777 }
778
779 tm = copy;
780 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
781 if (k && *k == 0) {
782 tm.tm_sec = 0;
e4eaf99a 783 goto from_tm;
9a98c7a1
LP
784 }
785
786 tm = copy;
787 k = strptime(t, "%y-%m-%d", &tm);
788 if (k && *k == 0) {
789 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 790 goto from_tm;
9a98c7a1
LP
791 }
792
793 tm = copy;
794 k = strptime(t, "%Y-%m-%d", &tm);
795 if (k && *k == 0) {
796 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 797 goto from_tm;
9a98c7a1
LP
798 }
799
800 tm = copy;
801 k = strptime(t, "%H:%M:%S", &tm);
e4eaf99a
HV
802 if (k) {
803 if (*k == '.')
804 goto parse_usec;
805 else if (*k == 0)
806 goto from_tm;
807 }
9a98c7a1
LP
808
809 tm = copy;
810 k = strptime(t, "%H:%M", &tm);
811 if (k && *k == 0) {
812 tm.tm_sec = 0;
e4eaf99a 813 goto from_tm;
9a98c7a1
LP
814 }
815
816 return -EINVAL;
817
e4eaf99a
HV
818parse_usec:
819 {
436dd70f 820 unsigned add;
e4eaf99a
HV
821
822 k++;
436dd70f
HV
823 r = parse_fractional_part_u(&k, 6, &add);
824 if (r < 0)
e4eaf99a
HV
825 return -EINVAL;
826
436dd70f 827 if (*k)
e4eaf99a
HV
828 return -EINVAL;
829
436dd70f 830 x_usec = add;
e4eaf99a
HV
831 }
832
833from_tm:
834 x = mktime_or_timegm(&tm, utc);
9a98c7a1
LP
835 if (x == (time_t) -1)
836 return -EINVAL;
837
92134489
LP
838 if (weekday >= 0 && tm.tm_wday != weekday)
839 return -EINVAL;
840
e4eaf99a 841 ret = (usec_t) x * USEC_PER_SEC + x_usec;
9a98c7a1 842
e4eaf99a 843finish:
9a98c7a1
LP
844 ret += plus;
845 if (ret > minus)
846 ret -= minus;
847 else
848 ret = 0;
849
850 *usec = ret;
851
852 return 0;
853}
854
240a7ba9 855static char* extract_multiplier(char *p, usec_t *multiplier) {
9a98c7a1
LP
856 static const struct {
857 const char *suffix;
858 usec_t usec;
859 } table[] = {
eb55ec9f
LP
860 { "seconds", USEC_PER_SEC },
861 { "second", USEC_PER_SEC },
862 { "sec", USEC_PER_SEC },
863 { "s", USEC_PER_SEC },
9a98c7a1 864 { "minutes", USEC_PER_MINUTE },
eb55ec9f
LP
865 { "minute", USEC_PER_MINUTE },
866 { "min", USEC_PER_MINUTE },
867 { "months", USEC_PER_MONTH },
868 { "month", USEC_PER_MONTH },
869 { "M", USEC_PER_MONTH },
870 { "msec", USEC_PER_MSEC },
871 { "ms", USEC_PER_MSEC },
872 { "m", USEC_PER_MINUTE },
873 { "hours", USEC_PER_HOUR },
874 { "hour", USEC_PER_HOUR },
875 { "hr", USEC_PER_HOUR },
876 { "h", USEC_PER_HOUR },
877 { "days", USEC_PER_DAY },
878 { "day", USEC_PER_DAY },
879 { "d", USEC_PER_DAY },
880 { "weeks", USEC_PER_WEEK },
881 { "week", USEC_PER_WEEK },
882 { "w", USEC_PER_WEEK },
883 { "years", USEC_PER_YEAR },
884 { "year", USEC_PER_YEAR },
885 { "y", USEC_PER_YEAR },
886 { "usec", 1ULL },
887 { "us", 1ULL },
9a98c7a1 888 };
240a7ba9
ZJS
889 unsigned i;
890
891 for (i = 0; i < ELEMENTSOF(table); i++) {
892 char *e;
893
894 e = startswith(p, table[i].suffix);
895 if (e) {
896 *multiplier = table[i].usec;
897 return e;
898 }
899 }
9a98c7a1 900
240a7ba9
ZJS
901 return p;
902}
903
904int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
b1d6dcf5 905 const char *p, *s;
9a98c7a1 906 usec_t r = 0;
cb0dac05 907 bool something = false;
9a98c7a1
LP
908
909 assert(t);
910 assert(usec);
519cffec 911 assert(default_unit > 0);
9a98c7a1
LP
912
913 p = t;
b1d6dcf5
ZJS
914
915 p += strspn(p, WHITESPACE);
916 s = startswith(p, "infinity");
917 if (s) {
918 s += strspn(s, WHITESPACE);
919 if (*s != 0)
920 return -EINVAL;
921
922 *usec = USEC_INFINITY;
923 return 0;
924 }
925
cb0dac05
LP
926 for (;;) {
927 long long l, z = 0;
9a98c7a1 928 char *e;
240a7ba9
ZJS
929 unsigned n = 0;
930 usec_t multiplier = default_unit, k;
cb0dac05
LP
931
932 p += strspn(p, WHITESPACE);
933
934 if (*p == 0) {
935 if (!something)
936 return -EINVAL;
937
938 break;
939 }
9a98c7a1
LP
940
941 errno = 0;
942 l = strtoll(p, &e, 10);
8333c77e 943 if (errno > 0)
9a98c7a1 944 return -errno;
9a98c7a1
LP
945 if (l < 0)
946 return -ERANGE;
947
cb0dac05
LP
948 if (*e == '.') {
949 char *b = e + 1;
950
951 errno = 0;
952 z = strtoll(b, &e, 10);
953 if (errno > 0)
954 return -errno;
955
956 if (z < 0)
957 return -ERANGE;
958
959 if (e == b)
960 return -EINVAL;
961
962 n = e - b;
963
964 } else if (e == p)
9a98c7a1
LP
965 return -EINVAL;
966
967 e += strspn(e, WHITESPACE);
240a7ba9 968 p = extract_multiplier(e, &multiplier);
9a98c7a1 969
519cffec
LP
970 something = true;
971
972 k = (usec_t) z * multiplier;
973
974 for (; n > 0; n--)
975 k /= 10;
976
977 r += (usec_t) l * multiplier + k;
cb0dac05 978 }
9a98c7a1
LP
979
980 *usec = r;
981
982 return 0;
983}
984
519cffec
LP
985int parse_sec(const char *t, usec_t *usec) {
986 return parse_time(t, usec, USEC_PER_SEC);
987}
988
9a98c7a1
LP
989int parse_nsec(const char *t, nsec_t *nsec) {
990 static const struct {
991 const char *suffix;
992 nsec_t nsec;
993 } table[] = {
994 { "seconds", NSEC_PER_SEC },
995 { "second", NSEC_PER_SEC },
996 { "sec", NSEC_PER_SEC },
997 { "s", NSEC_PER_SEC },
998 { "minutes", NSEC_PER_MINUTE },
999 { "minute", NSEC_PER_MINUTE },
1000 { "min", NSEC_PER_MINUTE },
1001 { "months", NSEC_PER_MONTH },
1002 { "month", NSEC_PER_MONTH },
1003 { "msec", NSEC_PER_MSEC },
1004 { "ms", NSEC_PER_MSEC },
1005 { "m", NSEC_PER_MINUTE },
1006 { "hours", NSEC_PER_HOUR },
1007 { "hour", NSEC_PER_HOUR },
1008 { "hr", NSEC_PER_HOUR },
1009 { "h", NSEC_PER_HOUR },
1010 { "days", NSEC_PER_DAY },
1011 { "day", NSEC_PER_DAY },
1012 { "d", NSEC_PER_DAY },
1013 { "weeks", NSEC_PER_WEEK },
1014 { "week", NSEC_PER_WEEK },
1015 { "w", NSEC_PER_WEEK },
1016 { "years", NSEC_PER_YEAR },
1017 { "year", NSEC_PER_YEAR },
1018 { "y", NSEC_PER_YEAR },
1019 { "usec", NSEC_PER_USEC },
1020 { "us", NSEC_PER_USEC },
1021 { "nsec", 1ULL },
1022 { "ns", 1ULL },
1023 { "", 1ULL }, /* default is nsec */
1024 };
1025
e73c78c2 1026 const char *p, *s;
9a98c7a1 1027 nsec_t r = 0;
cb0dac05 1028 bool something = false;
9a98c7a1
LP
1029
1030 assert(t);
1031 assert(nsec);
1032
1033 p = t;
e73c78c2
LP
1034
1035 p += strspn(p, WHITESPACE);
1036 s = startswith(p, "infinity");
1037 if (s) {
1038 s += strspn(s, WHITESPACE);
8e8933ca 1039 if (*s != 0)
e73c78c2
LP
1040 return -EINVAL;
1041
1042 *nsec = NSEC_INFINITY;
1043 return 0;
1044 }
1045
cb0dac05
LP
1046 for (;;) {
1047 long long l, z = 0;
9a98c7a1 1048 char *e;
cb0dac05
LP
1049 unsigned i, n = 0;
1050
1051 p += strspn(p, WHITESPACE);
1052
1053 if (*p == 0) {
1054 if (!something)
1055 return -EINVAL;
1056
1057 break;
1058 }
9a98c7a1
LP
1059
1060 errno = 0;
1061 l = strtoll(p, &e, 10);
1062
8333c77e 1063 if (errno > 0)
9a98c7a1
LP
1064 return -errno;
1065
1066 if (l < 0)
1067 return -ERANGE;
1068
cb0dac05
LP
1069 if (*e == '.') {
1070 char *b = e + 1;
1071
1072 errno = 0;
1073 z = strtoll(b, &e, 10);
1074 if (errno > 0)
1075 return -errno;
1076
1077 if (z < 0)
1078 return -ERANGE;
1079
1080 if (e == b)
1081 return -EINVAL;
1082
1083 n = e - b;
1084
1085 } else if (e == p)
9a98c7a1
LP
1086 return -EINVAL;
1087
1088 e += strspn(e, WHITESPACE);
1089
1090 for (i = 0; i < ELEMENTSOF(table); i++)
1091 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
1092 nsec_t k = (nsec_t) z * table[i].nsec;
1093
1094 for (; n > 0; n--)
1095 k /= 10;
1096
1097 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 1098 p = e + strlen(table[i].suffix);
cb0dac05
LP
1099
1100 something = true;
9a98c7a1
LP
1101 break;
1102 }
1103
1104 if (i >= ELEMENTSOF(table))
1105 return -EINVAL;
1106
cb0dac05 1107 }
9a98c7a1
LP
1108
1109 *nsec = r;
1110
1111 return 0;
1112}
03cc26dd
LP
1113
1114bool ntp_synced(void) {
1115 struct timex txc = {};
1116
1117 if (adjtimex(&txc) < 0)
1118 return false;
1119
1120 if (txc.status & STA_UNSYNC)
1121 return false;
1122
1123 return true;
1124}
75683450
LP
1125
1126int get_timezones(char ***ret) {
1127 _cleanup_fclose_ FILE *f = NULL;
1128 _cleanup_strv_free_ char **zones = NULL;
1129 size_t n_zones = 0, n_allocated = 0;
1130
1131 assert(ret);
1132
1133 zones = strv_new("UTC", NULL);
1134 if (!zones)
1135 return -ENOMEM;
1136
1137 n_allocated = 2;
1138 n_zones = 1;
1139
1140 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
1141 if (f) {
1142 char l[LINE_MAX];
1143
1144 FOREACH_LINE(l, f, return -errno) {
1145 char *p, *w;
1146 size_t k;
1147
1148 p = strstrip(l);
1149
1150 if (isempty(p) || *p == '#')
1151 continue;
1152
1153 /* Skip over country code */
1154 p += strcspn(p, WHITESPACE);
1155 p += strspn(p, WHITESPACE);
1156
1157 /* Skip over coordinates */
1158 p += strcspn(p, WHITESPACE);
1159 p += strspn(p, WHITESPACE);
1160
1161 /* Found timezone name */
1162 k = strcspn(p, WHITESPACE);
1163 if (k <= 0)
1164 continue;
1165
1166 w = strndup(p, k);
1167 if (!w)
1168 return -ENOMEM;
1169
1170 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
1171 free(w);
1172 return -ENOMEM;
1173 }
1174
1175 zones[n_zones++] = w;
1176 zones[n_zones] = NULL;
1177 }
1178
1179 strv_sort(zones);
1180
1181 } else if (errno != ENOENT)
1182 return -errno;
1183
1184 *ret = zones;
1185 zones = NULL;
1186
1187 return 0;
1188}
1189
1190bool timezone_is_valid(const char *name) {
1191 bool slash = false;
1192 const char *p, *t;
1193 struct stat st;
1194
5c904ba5
LP
1195 if (isempty(name))
1196 return false;
1197
1198 if (name[0] == '/')
75683450
LP
1199 return false;
1200
1201 for (p = name; *p; p++) {
1202 if (!(*p >= '0' && *p <= '9') &&
1203 !(*p >= 'a' && *p <= 'z') &&
1204 !(*p >= 'A' && *p <= 'Z') &&
1205 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
1206 return false;
1207
1208 if (*p == '/') {
1209
1210 if (slash)
1211 return false;
1212
1213 slash = true;
1214 } else
1215 slash = false;
1216 }
1217
1218 if (slash)
1219 return false;
1220
63c372cb 1221 t = strjoina("/usr/share/zoneinfo/", name);
75683450
LP
1222 if (stat(t, &st) < 0)
1223 return false;
1224
1225 if (!S_ISREG(st.st_mode))
1226 return false;
1227
1228 return true;
1229}
77ff2de9 1230
3411372e
LP
1231bool clock_boottime_supported(void) {
1232 static int supported = -1;
1233
1234 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1235
1236 if (supported < 0) {
1237 int fd;
1238
1239 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1240 if (fd < 0)
1241 supported = false;
1242 else {
1243 safe_close(fd);
1244 supported = true;
1245 }
77ff2de9
TG
1246 }
1247
3411372e
LP
1248 return supported;
1249}
1250
1251clockid_t clock_boottime_or_monotonic(void) {
1252 if (clock_boottime_supported())
1253 return CLOCK_BOOTTIME;
1254 else
1255 return CLOCK_MONOTONIC;
77ff2de9 1256}
5c904ba5 1257
fe624c4c
LP
1258bool clock_supported(clockid_t clock) {
1259 struct timespec ts;
1260
1261 switch (clock) {
1262
1263 case CLOCK_MONOTONIC:
1264 case CLOCK_REALTIME:
1265 return true;
1266
1267 case CLOCK_BOOTTIME:
1268 return clock_boottime_supported();
1269
1270 case CLOCK_BOOTTIME_ALARM:
1271 if (!clock_boottime_supported())
1272 return false;
1273
1274 /* fall through, after checking the cached value for CLOCK_BOOTTIME. */
1275
1276 default:
1277 /* For everything else, check properly */
1278 return clock_gettime(clock, &ts) >= 0;
1279 }
1280}
1281
64d6c229 1282int get_timezone(char **tz) {
5c904ba5
LP
1283 _cleanup_free_ char *t = NULL;
1284 const char *e;
1285 char *z;
1286 int r;
1287
1288 r = readlink_malloc("/etc/localtime", &t);
1289 if (r < 0)
1290 return r; /* returns EINVAL if not a symlink */
1291
1292 e = path_startswith(t, "/usr/share/zoneinfo/");
1293 if (!e)
1294 e = path_startswith(t, "../usr/share/zoneinfo/");
1295 if (!e)
1296 return -EINVAL;
1297
1298 if (!timezone_is_valid(e))
1299 return -EINVAL;
1300
1301 z = strdup(e);
1302 if (!z)
1303 return -ENOMEM;
1304
64d6c229 1305 *tz = z;
5c904ba5
LP
1306 return 0;
1307}
7c67c79c
HV
1308
1309time_t mktime_or_timegm(struct tm *tm, bool utc) {
1310 return utc ? timegm(tm) : mktime(tm);
1311}
1312
1313struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1314 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1315}
87b8ce69
SS
1316
1317unsigned long usec_to_jiffies(usec_t u) {
1318 static thread_local unsigned long hz = 0;
1319 long r;
1320
1321 if (hz == 0) {
1322 r = sysconf(_SC_CLK_TCK);
1323
1324 assert(r > 0);
1325 hz = (unsigned long) r;
1326 }
1327
1328 return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
1329}