1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
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.
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.
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/>.
28 #include <sys/timerfd.h>
29 #include <sys/timex.h>
30 #include <sys/types.h>
33 #include "alloc-util.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 //#include "process-util.h"
42 #include "string-util.h"
44 #include "time-util.h"
46 static clockid_t map_clock_id(clockid_t c) {
48 /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
49 * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
50 * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
55 case CLOCK_BOOTTIME_ALARM:
56 return CLOCK_BOOTTIME;
58 case CLOCK_REALTIME_ALARM:
59 return CLOCK_REALTIME;
66 usec_t now(clockid_t clock_id) {
69 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
71 return timespec_load(&ts);
74 #if 0 /// UNNEEDED by elogind
75 nsec_t now_nsec(clockid_t clock_id) {
78 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
80 return timespec_load_nsec(&ts);
84 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
87 ts->realtime = now(CLOCK_REALTIME);
88 ts->monotonic = now(CLOCK_MONOTONIC);
93 triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
96 ts->realtime = now(CLOCK_REALTIME);
97 ts->monotonic = now(CLOCK_MONOTONIC);
98 ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
103 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
107 if (u == USEC_INFINITY || u <= 0) {
108 ts->realtime = ts->monotonic = u;
114 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
115 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
120 #if 0 /// UNNEEDED by elogind
121 triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
126 if (u == USEC_INFINITY || u <= 0) {
127 ts->realtime = ts->monotonic = ts->boottime = u;
132 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
133 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
134 ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
139 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
143 if (u == USEC_INFINITY) {
144 ts->realtime = ts->monotonic = USEC_INFINITY;
149 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
150 ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta);
155 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
158 if (u == USEC_INFINITY) {
159 ts->realtime = ts->monotonic = USEC_INFINITY;
163 dual_timestamp_get(ts);
164 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
165 ts->realtime = usec_sub_signed(ts->realtime, delta);
166 ts->monotonic = usec_sub_signed(ts->monotonic, delta);
172 usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
177 case CLOCK_REALTIME_ALARM:
180 case CLOCK_MONOTONIC:
181 return ts->monotonic;
184 case CLOCK_BOOTTIME_ALARM:
188 return USEC_INFINITY;
192 usec_t timespec_load(const struct timespec *ts) {
195 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
196 return USEC_INFINITY;
198 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
199 return USEC_INFINITY;
202 (usec_t) ts->tv_sec * USEC_PER_SEC +
203 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
206 #if 0 /// UNNEEDED by elogind
207 nsec_t timespec_load_nsec(const struct timespec *ts) {
210 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
211 return NSEC_INFINITY;
213 if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
214 return NSEC_INFINITY;
216 return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
220 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
223 if (u == USEC_INFINITY ||
224 u / USEC_PER_SEC >= TIME_T_MAX) {
225 ts->tv_sec = (time_t) -1;
226 ts->tv_nsec = (long) -1;
230 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
231 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
236 usec_t timeval_load(const struct timeval *tv) {
239 if (tv->tv_sec < 0 || tv->tv_usec < 0)
240 return USEC_INFINITY;
242 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
243 return USEC_INFINITY;
246 (usec_t) tv->tv_sec * USEC_PER_SEC +
247 (usec_t) tv->tv_usec;
250 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
253 if (u == USEC_INFINITY ||
254 u / USEC_PER_SEC > TIME_T_MAX) {
255 tv->tv_sec = (time_t) -1;
256 tv->tv_usec = (suseconds_t) -1;
258 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
259 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
265 static char *format_timestamp_internal(
272 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
273 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
274 static const char * const weekdays[] = {
292 1 + 10 + /* space and date */
293 1 + 8 + /* space and time */
294 (us ? 1 + 6 : 0) + /* "." and microsecond part */
295 1 + 1 + /* space and shortest possible zone */
297 return NULL; /* Not enough space even for the shortest form. */
298 if (t <= 0 || t == USEC_INFINITY)
299 return NULL; /* Timestamp is unset */
301 /* Let's not format times with years > 9999 */
302 if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
305 sec = (time_t) (t / USEC_PER_SEC); /* Round down */
307 if (!localtime_or_gmtime_r(&sec, &tm, utc))
310 /* Start with the week day */
311 assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
312 memcpy(buf, weekdays[tm.tm_wday], 4);
314 /* Add the main components */
315 if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
316 return NULL; /* Doesn't fit */
318 /* Append the microseconds part, if that's requested */
322 return NULL; /* Microseconds part doesn't fit. */
324 sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
327 /* Append the timezone */
330 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
331 * obsolete "GMT" instead. */
333 return NULL; /* "UTC" doesn't fit. */
335 strcpy(buf + n, " UTC");
337 } else if (!isempty(tm.tm_zone)) {
340 /* An explicit timezone is specified, let's use it, if it fits */
341 tn = strlen(tm.tm_zone);
342 if (n + 1 + tn + 1 > l) {
343 /* The full time zone does not fit in. Yuck. */
345 if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
346 return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
348 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
349 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
350 * an overly long, hard to read string on the user. This should be safe, because the user will
351 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
354 strcpy(buf + n, tm.tm_zone);
361 char *format_timestamp(char *buf, size_t l, usec_t t) {
362 return format_timestamp_internal(buf, l, t, false, false);
365 #if 0 /// UNNEEDED by elogind
366 char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
367 return format_timestamp_internal(buf, l, t, true, false);
371 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
372 return format_timestamp_internal(buf, l, t, false, true);
375 #if 0 /// UNNEEDED by elogind
376 char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
377 return format_timestamp_internal(buf, l, t, true, true);
381 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
385 if (t <= 0 || t == USEC_INFINITY)
388 n = now(CLOCK_REALTIME);
397 if (d >= USEC_PER_YEAR)
398 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
400 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
401 else if (d >= USEC_PER_MONTH)
402 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
404 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
405 else if (d >= USEC_PER_WEEK)
406 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
408 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
409 else if (d >= 2*USEC_PER_DAY)
410 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
411 else if (d >= 25*USEC_PER_HOUR)
412 snprintf(buf, l, "1 day " USEC_FMT "h %s",
413 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
414 else if (d >= 6*USEC_PER_HOUR)
415 snprintf(buf, l, USEC_FMT "h %s",
416 d / USEC_PER_HOUR, s);
417 else if (d >= USEC_PER_HOUR)
418 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
420 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
421 else if (d >= 5*USEC_PER_MINUTE)
422 snprintf(buf, l, USEC_FMT "min %s",
423 d / USEC_PER_MINUTE, s);
424 else if (d >= USEC_PER_MINUTE)
425 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
427 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
428 else if (d >= USEC_PER_SEC)
429 snprintf(buf, l, USEC_FMT "s %s",
430 d / USEC_PER_SEC, s);
431 else if (d >= USEC_PER_MSEC)
432 snprintf(buf, l, USEC_FMT "ms %s",
433 d / USEC_PER_MSEC, s);
435 snprintf(buf, l, USEC_FMT"us %s",
438 snprintf(buf, l, "now");
444 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
445 static const struct {
449 { "y", USEC_PER_YEAR },
450 { "month", USEC_PER_MONTH },
451 { "w", USEC_PER_WEEK },
452 { "d", USEC_PER_DAY },
453 { "h", USEC_PER_HOUR },
454 { "min", USEC_PER_MINUTE },
455 { "s", USEC_PER_SEC },
456 { "ms", USEC_PER_MSEC },
462 bool something = false;
467 if (t == USEC_INFINITY) {
468 strncpy(p, "infinity", l-1);
474 strncpy(p, "0", l-1);
479 /* The result of this function can be parsed with parse_sec */
481 for (i = 0; i < ELEMENTSOF(table); i++) {
490 if (t < accuracy && something)
493 if (t < table[i].usec)
499 a = t / table[i].usec;
500 b = t % table[i].usec;
502 /* Let's see if we should shows this in dot notation */
503 if (t < USEC_PER_MINUTE && b > 0) {
508 for (cc = table[i].usec; cc > 1; cc /= 10)
511 for (cc = accuracy; cc > 1; cc /= 10) {
518 "%s"USEC_FMT".%0*"PRI_USEC"%s",
530 /* No? Then let's show it normally */
541 n = MIN((size_t) k, l);
554 #if 0 /// UNNEEDED by elogind
555 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
561 if (!dual_timestamp_is_set(t))
564 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
570 int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
577 pos = strspn(value, WHITESPACE);
578 if (value[pos] == '-')
580 pos += strspn(value + pos, DIGITS);
581 pos += strspn(value + pos, WHITESPACE);
582 if (value[pos] == '-')
585 r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
587 log_debug("Failed to parse dual timestamp value \"%s\".", value);
591 if (value[pos] != '\0')
592 /* trailing garbage */
602 int timestamp_deserialize(const char *value, usec_t *timestamp) {
607 r = safe_atou64(value, timestamp);
609 return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value);
614 #if 0 /// UNNEEDED by elogind
615 static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
616 static const struct {
636 const char *k, *utc = NULL, *tzn = NULL;
639 usec_t x_usec, plus = 0, minus = 0, ret;
640 int r, weekday = -1, dst = -1;
646 * 2012-09-22 16:34:22
647 * 2012-09-22 16:34 (seconds will be set to 0)
648 * 2012-09-22 (time will be set to 00:00:00)
649 * 16:34:22 (date will be set to today)
650 * 16:34 (date will be set to today, seconds to 0)
652 * yesterday (time is set to 00:00:00)
653 * today (time is set to 00:00:00)
654 * tomorrow (time is set to 00:00:00)
657 * @2147483647 (seconds since epoch)
664 if (t[0] == '@' && !with_tz)
665 return parse_sec(t + 1, usec);
667 ret = now(CLOCK_REALTIME);
673 else if (t[0] == '+') {
674 r = parse_sec(t+1, &plus);
680 } else if (t[0] == '-') {
681 r = parse_sec(t+1, &minus);
687 } else if ((k = endswith(t, " ago"))) {
688 t = strndupa(t, k - t);
690 r = parse_sec(t, &minus);
696 } else if ((k = endswith(t, " left"))) {
697 t = strndupa(t, k - t);
699 r = parse_sec(t, &plus);
706 /* See if the timestamp is suffixed with UTC */
707 utc = endswith_no_case(t, " UTC");
709 t = strndupa(t, utc - t);
711 const char *e = NULL;
716 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
717 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
718 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
719 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
720 * support arbitrary timezone specifications. */
722 for (j = 0; j <= 1; j++) {
724 if (isempty(tzname[j]))
727 e = endswith_no_case(t, tzname[j]);
738 if (IN_SET(j, 0, 1)) {
739 /* Found one of the two timezones specified. */
740 t = strndupa(t, e - t - 1);
747 x = (time_t) (ret / USEC_PER_SEC);
750 if (!localtime_or_gmtime_r(&x, &tm, utc))
757 if (streq(t, "today")) {
758 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
761 } else if (streq(t, "yesterday")) {
763 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
766 } else if (streq(t, "tomorrow")) {
768 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
772 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
775 if (!startswith_no_case(t, day_nr[i].name))
778 skip = strlen(day_nr[i].name);
782 weekday = day_nr[i].nr;
788 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
797 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
806 k = strptime(t, "%y-%m-%d %H:%M", &tm);
813 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
820 k = strptime(t, "%y-%m-%d", &tm);
822 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
827 k = strptime(t, "%Y-%m-%d", &tm);
829 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
834 k = strptime(t, "%H:%M:%S", &tm);
843 k = strptime(t, "%H:%M", &tm);
856 r = parse_fractional_part_u(&k, 6, &add);
867 if (weekday >= 0 && tm.tm_wday != weekday)
870 x = mktime_or_timegm(&tm, utc);
874 ret = (usec_t) x * USEC_PER_SEC + x_usec;
875 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
879 if (ret + plus < ret) /* overflow? */
882 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
895 typedef struct ParseTimestampResult {
898 } ParseTimestampResult;
900 int parse_timestamp(const char *t, usec_t *usec) {
901 char *last_space, *tz = NULL;
902 ParseTimestampResult *shared, tmp;
905 last_space = strrchr(t, ' ');
906 if (last_space != NULL && timezone_is_valid(last_space + 1))
909 if (!tz || endswith_no_case(t, " UTC"))
910 return parse_timestamp_impl(t, usec, false);
912 shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
913 if (shared == MAP_FAILED)
914 return negative_errno();
916 r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
918 (void) munmap(shared, sizeof *shared);
924 if (setenv("TZ", tz, 1) != 0) {
925 shared->return_value = negative_errno();
931 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
932 * Otherwise just cut it off */
933 with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]);
935 /*cut off the timezone if we dont need it*/
937 t = strndupa(t, last_space - t);
939 shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
945 if (munmap(shared, sizeof *shared) != 0)
946 return negative_errno();
948 if (tmp.return_value == 0)
951 return tmp.return_value;
955 static char* extract_multiplier(char *p, usec_t *multiplier) {
956 static const struct {
960 { "seconds", USEC_PER_SEC },
961 { "second", USEC_PER_SEC },
962 { "sec", USEC_PER_SEC },
963 { "s", USEC_PER_SEC },
964 { "minutes", USEC_PER_MINUTE },
965 { "minute", USEC_PER_MINUTE },
966 { "min", USEC_PER_MINUTE },
967 { "months", USEC_PER_MONTH },
968 { "month", USEC_PER_MONTH },
969 { "M", USEC_PER_MONTH },
970 { "msec", USEC_PER_MSEC },
971 { "ms", USEC_PER_MSEC },
972 { "m", USEC_PER_MINUTE },
973 { "hours", USEC_PER_HOUR },
974 { "hour", USEC_PER_HOUR },
975 { "hr", USEC_PER_HOUR },
976 { "h", USEC_PER_HOUR },
977 { "days", USEC_PER_DAY },
978 { "day", USEC_PER_DAY },
979 { "d", USEC_PER_DAY },
980 { "weeks", USEC_PER_WEEK },
981 { "week", USEC_PER_WEEK },
982 { "w", USEC_PER_WEEK },
983 { "years", USEC_PER_YEAR },
984 { "year", USEC_PER_YEAR },
985 { "y", USEC_PER_YEAR },
992 for (i = 0; i < ELEMENTSOF(table); i++) {
995 e = startswith(p, table[i].suffix);
997 *multiplier = table[i].usec;
1005 int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
1008 bool something = false;
1012 assert(default_unit > 0);
1016 p += strspn(p, WHITESPACE);
1017 s = startswith(p, "infinity");
1019 s += strspn(s, WHITESPACE);
1023 *usec = USEC_INFINITY;
1031 usec_t multiplier = default_unit, k;
1033 p += strspn(p, WHITESPACE);
1043 l = strtoll(p, &e, 10);
1053 z = strtoll(b, &e, 10);
1068 e += strspn(e, WHITESPACE);
1069 p = extract_multiplier(e, &multiplier);
1073 k = (usec_t) z * multiplier;
1078 r += (usec_t) l * multiplier + k;
1086 int parse_sec(const char *t, usec_t *usec) {
1087 return parse_time(t, usec, USEC_PER_SEC);
1090 #if 0 /// UNNEEDED by elogind
1091 int parse_sec_fix_0(const char *t, usec_t *usec) {
1095 t += strspn(t, WHITESPACE);
1097 if (streq(t, "0")) {
1098 *usec = USEC_INFINITY;
1102 return parse_sec(t, usec);
1105 int parse_nsec(const char *t, nsec_t *nsec) {
1106 static const struct {
1110 { "seconds", NSEC_PER_SEC },
1111 { "second", NSEC_PER_SEC },
1112 { "sec", NSEC_PER_SEC },
1113 { "s", NSEC_PER_SEC },
1114 { "minutes", NSEC_PER_MINUTE },
1115 { "minute", NSEC_PER_MINUTE },
1116 { "min", NSEC_PER_MINUTE },
1117 { "months", NSEC_PER_MONTH },
1118 { "month", NSEC_PER_MONTH },
1119 { "msec", NSEC_PER_MSEC },
1120 { "ms", NSEC_PER_MSEC },
1121 { "m", NSEC_PER_MINUTE },
1122 { "hours", NSEC_PER_HOUR },
1123 { "hour", NSEC_PER_HOUR },
1124 { "hr", NSEC_PER_HOUR },
1125 { "h", NSEC_PER_HOUR },
1126 { "days", NSEC_PER_DAY },
1127 { "day", NSEC_PER_DAY },
1128 { "d", NSEC_PER_DAY },
1129 { "weeks", NSEC_PER_WEEK },
1130 { "week", NSEC_PER_WEEK },
1131 { "w", NSEC_PER_WEEK },
1132 { "years", NSEC_PER_YEAR },
1133 { "year", NSEC_PER_YEAR },
1134 { "y", NSEC_PER_YEAR },
1135 { "usec", NSEC_PER_USEC },
1136 { "us", NSEC_PER_USEC },
1137 { "µs", NSEC_PER_USEC },
1140 { "", 1ULL }, /* default is nsec */
1145 bool something = false;
1152 p += strspn(p, WHITESPACE);
1153 s = startswith(p, "infinity");
1155 s += strspn(s, WHITESPACE);
1159 *nsec = NSEC_INFINITY;
1168 p += strspn(p, WHITESPACE);
1178 l = strtoll(p, &e, 10);
1190 z = strtoll(b, &e, 10);
1205 e += strspn(e, WHITESPACE);
1207 for (i = 0; i < ELEMENTSOF(table); i++)
1208 if (startswith(e, table[i].suffix)) {
1209 nsec_t k = (nsec_t) z * table[i].nsec;
1214 r += (nsec_t) l * table[i].nsec + k;
1215 p = e + strlen(table[i].suffix);
1221 if (i >= ELEMENTSOF(table))
1231 bool ntp_synced(void) {
1232 struct timex txc = {};
1234 if (adjtimex(&txc) < 0)
1237 if (txc.status & STA_UNSYNC)
1243 int get_timezones(char ***ret) {
1244 _cleanup_fclose_ FILE *f = NULL;
1245 _cleanup_strv_free_ char **zones = NULL;
1246 size_t n_zones = 0, n_allocated = 0;
1250 zones = strv_new("UTC", NULL);
1257 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
1261 FOREACH_LINE(l, f, return -errno) {
1267 if (isempty(p) || *p == '#')
1270 /* Skip over country code */
1271 p += strcspn(p, WHITESPACE);
1272 p += strspn(p, WHITESPACE);
1274 /* Skip over coordinates */
1275 p += strcspn(p, WHITESPACE);
1276 p += strspn(p, WHITESPACE);
1278 /* Found timezone name */
1279 k = strcspn(p, WHITESPACE);
1287 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
1292 zones[n_zones++] = w;
1293 zones[n_zones] = NULL;
1298 } else if (errno != ENOENT)
1307 bool timezone_is_valid(const char *name) {
1318 for (p = name; *p; p++) {
1319 if (!(*p >= '0' && *p <= '9') &&
1320 !(*p >= 'a' && *p <= 'z') &&
1321 !(*p >= 'A' && *p <= 'Z') &&
1322 !IN_SET(*p, '-', '_', '+', '/'))
1338 t = strjoina("/usr/share/zoneinfo/", name);
1339 if (stat(t, &st) < 0)
1342 if (!S_ISREG(st.st_mode))
1349 bool clock_boottime_supported(void) {
1350 static int supported = -1;
1352 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1354 if (supported < 0) {
1357 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1369 #if 0 /// UNNEEDED by elogind
1370 clockid_t clock_boottime_or_monotonic(void) {
1371 if (clock_boottime_supported())
1372 return CLOCK_BOOTTIME;
1374 return CLOCK_MONOTONIC;
1378 #if 1 /// let's add a diagnostic push to silence -Wimplicit-fallthrough to elogind
1379 # if defined(__GNUC__) && (__GNUC__ > 6)
1380 # pragma GCC diagnostic push
1381 # pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
1384 bool clock_supported(clockid_t clock) {
1389 case CLOCK_MONOTONIC:
1390 case CLOCK_REALTIME:
1393 case CLOCK_BOOTTIME:
1394 return clock_boottime_supported();
1396 case CLOCK_BOOTTIME_ALARM:
1397 if (!clock_boottime_supported())
1402 /* For everything else, check properly */
1403 return clock_gettime(clock, &ts) >= 0;
1406 #if 1 /// end diagnostic push in elogind
1408 # pragma GCC diagnostic pop
1412 #if 0 /// UNNEEDED by elogind
1413 int get_timezone(char **tz) {
1414 _cleanup_free_ char *t = NULL;
1419 r = readlink_malloc("/etc/localtime", &t);
1421 return r; /* returns EINVAL if not a symlink */
1423 e = path_startswith(t, "/usr/share/zoneinfo/");
1425 e = path_startswith(t, "../usr/share/zoneinfo/");
1429 if (!timezone_is_valid(e))
1440 time_t mktime_or_timegm(struct tm *tm, bool utc) {
1441 return utc ? timegm(tm) : mktime(tm);
1445 struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1446 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1449 #if 0 /// UNNEEDED by elogind
1450 unsigned long usec_to_jiffies(usec_t u) {
1451 static thread_local unsigned long hz = 0;
1455 r = sysconf(_SC_CLK_TCK);
1461 return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
1464 usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
1467 if (x == USEC_INFINITY)
1468 return USEC_INFINITY;
1469 if (map_clock_id(from) == map_clock_id(to))
1476 /* x lies in the future */
1477 return usec_add(b, usec_sub_unsigned(x, a));
1479 /* x lies in the past */
1480 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
1484 bool in_utc_timezone(void) {
1487 return timezone == 0 && daylight == 0;