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 "string-util.h"
43 #include "time-util.h"
45 static clockid_t map_clock_id(clockid_t c) {
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
54 case CLOCK_BOOTTIME_ALARM:
55 return CLOCK_BOOTTIME;
57 case CLOCK_REALTIME_ALARM:
58 return CLOCK_REALTIME;
65 usec_t now(clockid_t clock_id) {
68 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
70 return timespec_load(&ts);
73 #if 0 /// UNNEEDED by elogind
74 nsec_t now_nsec(clockid_t clock_id) {
77 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
79 return timespec_load_nsec(&ts);
83 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
86 ts->realtime = now(CLOCK_REALTIME);
87 ts->monotonic = now(CLOCK_MONOTONIC);
92 triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
95 ts->realtime = now(CLOCK_REALTIME);
96 ts->monotonic = now(CLOCK_MONOTONIC);
97 ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
102 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
106 if (u == USEC_INFINITY || u <= 0) {
107 ts->realtime = ts->monotonic = u;
113 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
114 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
119 #if 0 /// UNNEEDED by elogind
120 triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
125 if (u == USEC_INFINITY || u <= 0) {
126 ts->realtime = ts->monotonic = ts->boottime = u;
131 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
132 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
133 ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
138 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
142 if (u == USEC_INFINITY) {
143 ts->realtime = ts->monotonic = USEC_INFINITY;
148 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
149 ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta);
154 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
157 if (u == USEC_INFINITY) {
158 ts->realtime = ts->monotonic = USEC_INFINITY;
162 dual_timestamp_get(ts);
163 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
164 ts->realtime = usec_sub_signed(ts->realtime, delta);
165 ts->monotonic = usec_sub_signed(ts->monotonic, delta);
171 usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
176 case CLOCK_REALTIME_ALARM:
179 case CLOCK_MONOTONIC:
180 return ts->monotonic;
183 case CLOCK_BOOTTIME_ALARM:
187 return USEC_INFINITY;
191 usec_t timespec_load(const struct timespec *ts) {
194 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
195 return USEC_INFINITY;
197 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
198 return USEC_INFINITY;
201 (usec_t) ts->tv_sec * USEC_PER_SEC +
202 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
205 #if 0 /// UNNEEDED by elogind
206 nsec_t timespec_load_nsec(const struct timespec *ts) {
209 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
210 return NSEC_INFINITY;
212 if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
213 return NSEC_INFINITY;
215 return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
219 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
222 if (u == USEC_INFINITY ||
223 u / USEC_PER_SEC >= TIME_T_MAX) {
224 ts->tv_sec = (time_t) -1;
225 ts->tv_nsec = (long) -1;
229 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
230 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
235 usec_t timeval_load(const struct timeval *tv) {
238 if (tv->tv_sec < 0 || tv->tv_usec < 0)
239 return USEC_INFINITY;
241 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
242 return USEC_INFINITY;
245 (usec_t) tv->tv_sec * USEC_PER_SEC +
246 (usec_t) tv->tv_usec;
249 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
252 if (u == USEC_INFINITY ||
253 u / USEC_PER_SEC > TIME_T_MAX) {
254 tv->tv_sec = (time_t) -1;
255 tv->tv_usec = (suseconds_t) -1;
257 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
258 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
264 static char *format_timestamp_internal(
271 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
272 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
273 static const char * const weekdays[] = {
291 1 + 10 + /* space and date */
292 1 + 8 + /* space and time */
293 (us ? 1 + 6 : 0) + /* "." and microsecond part */
294 1 + 1 + /* space and shortest possible zone */
296 return NULL; /* Not enough space even for the shortest form. */
297 if (t <= 0 || t == USEC_INFINITY)
298 return NULL; /* Timestamp is unset */
300 /* Let's not format times with years > 9999 */
301 if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
304 sec = (time_t) (t / USEC_PER_SEC); /* Round down */
306 if (!localtime_or_gmtime_r(&sec, &tm, utc))
309 /* Start with the week day */
310 assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
311 memcpy(buf, weekdays[tm.tm_wday], 4);
313 /* Add the main components */
314 if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
315 return NULL; /* Doesn't fit */
317 /* Append the microseconds part, if that's requested */
321 return NULL; /* Microseconds part doesn't fit. */
323 sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
326 /* Append the timezone */
329 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
330 * obsolete "GMT" instead. */
332 return NULL; /* "UTC" doesn't fit. */
334 strcpy(buf + n, " UTC");
336 } else if (!isempty(tm.tm_zone)) {
339 /* An explicit timezone is specified, let's use it, if it fits */
340 tn = strlen(tm.tm_zone);
341 if (n + 1 + tn + 1 > l) {
342 /* The full time zone does not fit in. Yuck. */
344 if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
345 return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
347 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
348 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
349 * an overly long, hard to read string on the user. This should be safe, because the user will
350 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
353 strcpy(buf + n, tm.tm_zone);
360 char *format_timestamp(char *buf, size_t l, usec_t t) {
361 return format_timestamp_internal(buf, l, t, false, false);
364 #if 0 /// UNNEEDED by elogind
365 char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
366 return format_timestamp_internal(buf, l, t, true, false);
370 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
371 return format_timestamp_internal(buf, l, t, false, true);
374 #if 0 /// UNNEEDED by elogind
375 char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
376 return format_timestamp_internal(buf, l, t, true, true);
380 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
384 if (t <= 0 || t == USEC_INFINITY)
387 n = now(CLOCK_REALTIME);
396 if (d >= USEC_PER_YEAR)
397 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
399 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
400 else if (d >= USEC_PER_MONTH)
401 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
403 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
404 else if (d >= USEC_PER_WEEK)
405 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
407 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
408 else if (d >= 2*USEC_PER_DAY)
409 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
410 else if (d >= 25*USEC_PER_HOUR)
411 snprintf(buf, l, "1 day " USEC_FMT "h %s",
412 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
413 else if (d >= 6*USEC_PER_HOUR)
414 snprintf(buf, l, USEC_FMT "h %s",
415 d / USEC_PER_HOUR, s);
416 else if (d >= USEC_PER_HOUR)
417 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
419 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
420 else if (d >= 5*USEC_PER_MINUTE)
421 snprintf(buf, l, USEC_FMT "min %s",
422 d / USEC_PER_MINUTE, s);
423 else if (d >= USEC_PER_MINUTE)
424 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
426 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
427 else if (d >= USEC_PER_SEC)
428 snprintf(buf, l, USEC_FMT "s %s",
429 d / USEC_PER_SEC, s);
430 else if (d >= USEC_PER_MSEC)
431 snprintf(buf, l, USEC_FMT "ms %s",
432 d / USEC_PER_MSEC, s);
434 snprintf(buf, l, USEC_FMT"us %s",
437 snprintf(buf, l, "now");
443 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
444 static const struct {
448 { "y", USEC_PER_YEAR },
449 { "month", USEC_PER_MONTH },
450 { "w", USEC_PER_WEEK },
451 { "d", USEC_PER_DAY },
452 { "h", USEC_PER_HOUR },
453 { "min", USEC_PER_MINUTE },
454 { "s", USEC_PER_SEC },
455 { "ms", USEC_PER_MSEC },
461 bool something = false;
466 if (t == USEC_INFINITY) {
467 strncpy(p, "infinity", l-1);
473 strncpy(p, "0", l-1);
478 /* The result of this function can be parsed with parse_sec */
480 for (i = 0; i < ELEMENTSOF(table); i++) {
489 if (t < accuracy && something)
492 if (t < table[i].usec)
498 a = t / table[i].usec;
499 b = t % table[i].usec;
501 /* Let's see if we should shows this in dot notation */
502 if (t < USEC_PER_MINUTE && b > 0) {
507 for (cc = table[i].usec; cc > 1; cc /= 10)
510 for (cc = accuracy; cc > 1; cc /= 10) {
517 "%s"USEC_FMT".%0*"PRI_USEC"%s",
529 /* No? Then let's show it normally */
540 n = MIN((size_t) k, l);
553 #if 0 /// UNNEEDED by elogind
554 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
560 if (!dual_timestamp_is_set(t))
563 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
569 int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
576 pos = strspn(value, WHITESPACE);
577 if (value[pos] == '-')
579 pos += strspn(value + pos, DIGITS);
580 pos += strspn(value + pos, WHITESPACE);
581 if (value[pos] == '-')
584 r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
586 log_debug("Failed to parse dual timestamp value \"%s\".", value);
590 if (value[pos] != '\0')
591 /* trailing garbage */
601 int timestamp_deserialize(const char *value, usec_t *timestamp) {
606 r = safe_atou64(value, timestamp);
608 return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value);
613 #if 0 /// UNNEEDED by elogind
614 static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
615 static const struct {
635 const char *k, *utc = NULL, *tzn = NULL;
638 usec_t x_usec, plus = 0, minus = 0, ret;
639 int r, weekday = -1, dst = -1;
645 * 2012-09-22 16:34:22
646 * 2012-09-22 16:34 (seconds will be set to 0)
647 * 2012-09-22 (time will be set to 00:00:00)
648 * 16:34:22 (date will be set to today)
649 * 16:34 (date will be set to today, seconds to 0)
651 * yesterday (time is set to 00:00:00)
652 * today (time is set to 00:00:00)
653 * tomorrow (time is set to 00:00:00)
656 * @2147483647 (seconds since epoch)
663 if (t[0] == '@' && !with_tz)
664 return parse_sec(t + 1, usec);
666 ret = now(CLOCK_REALTIME);
672 else if (t[0] == '+') {
673 r = parse_sec(t+1, &plus);
679 } else if (t[0] == '-') {
680 r = parse_sec(t+1, &minus);
686 } else if ((k = endswith(t, " ago"))) {
687 t = strndupa(t, k - t);
689 r = parse_sec(t, &minus);
695 } else if ((k = endswith(t, " left"))) {
696 t = strndupa(t, k - t);
698 r = parse_sec(t, &plus);
705 /* See if the timestamp is suffixed with UTC */
706 utc = endswith_no_case(t, " UTC");
708 t = strndupa(t, utc - t);
710 const char *e = NULL;
715 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
716 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
717 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
718 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
719 * support arbitrary timezone specifications. */
721 for (j = 0; j <= 1; j++) {
723 if (isempty(tzname[j]))
726 e = endswith_no_case(t, tzname[j]);
737 if (IN_SET(j, 0, 1)) {
738 /* Found one of the two timezones specified. */
739 t = strndupa(t, e - t - 1);
746 x = (time_t) (ret / USEC_PER_SEC);
749 if (!localtime_or_gmtime_r(&x, &tm, utc))
756 if (streq(t, "today")) {
757 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
760 } else if (streq(t, "yesterday")) {
762 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
765 } else if (streq(t, "tomorrow")) {
767 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
771 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
774 if (!startswith_no_case(t, day_nr[i].name))
777 skip = strlen(day_nr[i].name);
781 weekday = day_nr[i].nr;
787 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
796 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
805 k = strptime(t, "%y-%m-%d %H:%M", &tm);
812 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
819 k = strptime(t, "%y-%m-%d", &tm);
821 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
826 k = strptime(t, "%Y-%m-%d", &tm);
828 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
833 k = strptime(t, "%H:%M:%S", &tm);
842 k = strptime(t, "%H:%M", &tm);
855 r = parse_fractional_part_u(&k, 6, &add);
866 if (weekday >= 0 && tm.tm_wday != weekday)
869 x = mktime_or_timegm(&tm, utc);
873 ret = (usec_t) x * USEC_PER_SEC + x_usec;
874 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
878 if (ret + plus < ret) /* overflow? */
881 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
894 typedef struct ParseTimestampResult {
897 } ParseTimestampResult;
899 int parse_timestamp(const char *t, usec_t *usec) {
900 char *last_space, *tz = NULL;
901 ParseTimestampResult *shared, tmp;
904 last_space = strrchr(t, ' ');
905 if (last_space != NULL && timezone_is_valid(last_space + 1))
908 if (!tz || endswith_no_case(t, " UTC"))
909 return parse_timestamp_impl(t, usec, false);
911 shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
912 if (shared == MAP_FAILED)
913 return negative_errno();
915 r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
917 (void) munmap(shared, sizeof *shared);
923 if (setenv("TZ", tz, 1) != 0) {
924 shared->return_value = negative_errno();
930 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
931 * Otherwise just cut it off */
932 with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]);
934 /*cut off the timezone if we dont need it*/
936 t = strndupa(t, last_space - t);
938 shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
944 if (munmap(shared, sizeof *shared) != 0)
945 return negative_errno();
947 if (tmp.return_value == 0)
950 return tmp.return_value;
954 static char* extract_multiplier(char *p, usec_t *multiplier) {
955 static const struct {
959 { "seconds", USEC_PER_SEC },
960 { "second", USEC_PER_SEC },
961 { "sec", USEC_PER_SEC },
962 { "s", USEC_PER_SEC },
963 { "minutes", USEC_PER_MINUTE },
964 { "minute", USEC_PER_MINUTE },
965 { "min", USEC_PER_MINUTE },
966 { "months", USEC_PER_MONTH },
967 { "month", USEC_PER_MONTH },
968 { "M", USEC_PER_MONTH },
969 { "msec", USEC_PER_MSEC },
970 { "ms", USEC_PER_MSEC },
971 { "m", USEC_PER_MINUTE },
972 { "hours", USEC_PER_HOUR },
973 { "hour", USEC_PER_HOUR },
974 { "hr", USEC_PER_HOUR },
975 { "h", USEC_PER_HOUR },
976 { "days", USEC_PER_DAY },
977 { "day", USEC_PER_DAY },
978 { "d", USEC_PER_DAY },
979 { "weeks", USEC_PER_WEEK },
980 { "week", USEC_PER_WEEK },
981 { "w", USEC_PER_WEEK },
982 { "years", USEC_PER_YEAR },
983 { "year", USEC_PER_YEAR },
984 { "y", USEC_PER_YEAR },
991 for (i = 0; i < ELEMENTSOF(table); i++) {
994 e = startswith(p, table[i].suffix);
996 *multiplier = table[i].usec;
1004 int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
1007 bool something = false;
1011 assert(default_unit > 0);
1015 p += strspn(p, WHITESPACE);
1016 s = startswith(p, "infinity");
1018 s += strspn(s, WHITESPACE);
1022 *usec = USEC_INFINITY;
1030 usec_t multiplier = default_unit, k;
1032 p += strspn(p, WHITESPACE);
1042 l = strtoll(p, &e, 10);
1052 z = strtoll(b, &e, 10);
1067 e += strspn(e, WHITESPACE);
1068 p = extract_multiplier(e, &multiplier);
1072 k = (usec_t) z * multiplier;
1077 r += (usec_t) l * multiplier + k;
1085 int parse_sec(const char *t, usec_t *usec) {
1086 return parse_time(t, usec, USEC_PER_SEC);
1089 #if 0 /// UNNEEDED by elogind
1090 int parse_sec_fix_0(const char *t, usec_t *usec) {
1094 t += strspn(t, WHITESPACE);
1096 if (streq(t, "0")) {
1097 *usec = USEC_INFINITY;
1101 return parse_sec(t, usec);
1104 int parse_nsec(const char *t, nsec_t *nsec) {
1105 static const struct {
1109 { "seconds", NSEC_PER_SEC },
1110 { "second", NSEC_PER_SEC },
1111 { "sec", NSEC_PER_SEC },
1112 { "s", NSEC_PER_SEC },
1113 { "minutes", NSEC_PER_MINUTE },
1114 { "minute", NSEC_PER_MINUTE },
1115 { "min", NSEC_PER_MINUTE },
1116 { "months", NSEC_PER_MONTH },
1117 { "month", NSEC_PER_MONTH },
1118 { "msec", NSEC_PER_MSEC },
1119 { "ms", NSEC_PER_MSEC },
1120 { "m", NSEC_PER_MINUTE },
1121 { "hours", NSEC_PER_HOUR },
1122 { "hour", NSEC_PER_HOUR },
1123 { "hr", NSEC_PER_HOUR },
1124 { "h", NSEC_PER_HOUR },
1125 { "days", NSEC_PER_DAY },
1126 { "day", NSEC_PER_DAY },
1127 { "d", NSEC_PER_DAY },
1128 { "weeks", NSEC_PER_WEEK },
1129 { "week", NSEC_PER_WEEK },
1130 { "w", NSEC_PER_WEEK },
1131 { "years", NSEC_PER_YEAR },
1132 { "year", NSEC_PER_YEAR },
1133 { "y", NSEC_PER_YEAR },
1134 { "usec", NSEC_PER_USEC },
1135 { "us", NSEC_PER_USEC },
1136 { "µs", NSEC_PER_USEC },
1139 { "", 1ULL }, /* default is nsec */
1144 bool something = false;
1151 p += strspn(p, WHITESPACE);
1152 s = startswith(p, "infinity");
1154 s += strspn(s, WHITESPACE);
1158 *nsec = NSEC_INFINITY;
1167 p += strspn(p, WHITESPACE);
1177 l = strtoll(p, &e, 10);
1189 z = strtoll(b, &e, 10);
1204 e += strspn(e, WHITESPACE);
1206 for (i = 0; i < ELEMENTSOF(table); i++)
1207 if (startswith(e, table[i].suffix)) {
1208 nsec_t k = (nsec_t) z * table[i].nsec;
1213 r += (nsec_t) l * table[i].nsec + k;
1214 p = e + strlen(table[i].suffix);
1220 if (i >= ELEMENTSOF(table))
1230 bool ntp_synced(void) {
1231 struct timex txc = {};
1233 if (adjtimex(&txc) < 0)
1236 if (txc.status & STA_UNSYNC)
1242 int get_timezones(char ***ret) {
1243 _cleanup_fclose_ FILE *f = NULL;
1244 _cleanup_strv_free_ char **zones = NULL;
1245 size_t n_zones = 0, n_allocated = 0;
1249 zones = strv_new("UTC", NULL);
1256 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
1260 FOREACH_LINE(l, f, return -errno) {
1266 if (isempty(p) || *p == '#')
1269 /* Skip over country code */
1270 p += strcspn(p, WHITESPACE);
1271 p += strspn(p, WHITESPACE);
1273 /* Skip over coordinates */
1274 p += strcspn(p, WHITESPACE);
1275 p += strspn(p, WHITESPACE);
1277 /* Found timezone name */
1278 k = strcspn(p, WHITESPACE);
1286 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
1291 zones[n_zones++] = w;
1292 zones[n_zones] = NULL;
1297 } else if (errno != ENOENT)
1306 bool timezone_is_valid(const char *name) {
1317 for (p = name; *p; p++) {
1318 if (!(*p >= '0' && *p <= '9') &&
1319 !(*p >= 'a' && *p <= 'z') &&
1320 !(*p >= 'A' && *p <= 'Z') &&
1321 !IN_SET(*p, '-', '_', '+', '/'))
1337 t = strjoina("/usr/share/zoneinfo/", name);
1338 if (stat(t, &st) < 0)
1341 if (!S_ISREG(st.st_mode))
1348 bool clock_boottime_supported(void) {
1349 static int supported = -1;
1351 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1353 if (supported < 0) {
1356 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1368 #if 0 /// UNNEEDED by elogind
1369 clockid_t clock_boottime_or_monotonic(void) {
1370 if (clock_boottime_supported())
1371 return CLOCK_BOOTTIME;
1373 return CLOCK_MONOTONIC;
1377 #if 1 /// let's add a diagnostic push to silence -Wimplicit-fallthrough to elogind
1378 # if defined(__GNUC__) && (__GNUC__ > 6)
1379 # pragma GCC diagnostic push
1380 # pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
1383 bool clock_supported(clockid_t clock) {
1388 case CLOCK_MONOTONIC:
1389 case CLOCK_REALTIME:
1392 case CLOCK_BOOTTIME:
1393 return clock_boottime_supported();
1395 case CLOCK_BOOTTIME_ALARM:
1396 if (!clock_boottime_supported())
1401 /* For everything else, check properly */
1402 return clock_gettime(clock, &ts) >= 0;
1405 #if 1 /// end diagnostic push in elogind
1407 # pragma GCC diagnostic pop
1411 #if 0 /// UNNEEDED by elogind
1412 int get_timezone(char **tz) {
1413 _cleanup_free_ char *t = NULL;
1418 r = readlink_malloc("/etc/localtime", &t);
1420 return r; /* returns EINVAL if not a symlink */
1422 e = path_startswith(t, "/usr/share/zoneinfo/");
1424 e = path_startswith(t, "../usr/share/zoneinfo/");
1428 if (!timezone_is_valid(e))
1439 time_t mktime_or_timegm(struct tm *tm, bool utc) {
1440 return utc ? timegm(tm) : mktime(tm);
1444 struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1445 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1448 #if 0 /// UNNEEDED by elogind
1449 unsigned long usec_to_jiffies(usec_t u) {
1450 static thread_local unsigned long hz = 0;
1454 r = sysconf(_SC_CLK_TCK);
1460 return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
1463 usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
1466 if (x == USEC_INFINITY)
1467 return USEC_INFINITY;
1468 if (map_clock_id(from) == map_clock_id(to))
1475 /* x lies in the future */
1476 return usec_add(b, usec_sub_unsigned(x, a));
1478 /* x lies in the past */
1479 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
1483 bool in_utc_timezone(void) {
1486 return timezone == 0 && daylight == 0;