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 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
);
82 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
85 ts
->realtime
= now(CLOCK_REALTIME
);
86 ts
->monotonic
= now(CLOCK_MONOTONIC
);
91 triple_timestamp
* triple_timestamp_get(triple_timestamp
*ts
) {
94 ts
->realtime
= now(CLOCK_REALTIME
);
95 ts
->monotonic
= now(CLOCK_MONOTONIC
);
96 ts
->boottime
= clock_boottime_supported() ? now(CLOCK_BOOTTIME
) : USEC_INFINITY
;
101 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
105 if (u
== USEC_INFINITY
|| u
<= 0) {
106 ts
->realtime
= ts
->monotonic
= u
;
112 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
113 ts
->monotonic
= usec_sub_signed(now(CLOCK_MONOTONIC
), delta
);
118 triple_timestamp
* triple_timestamp_from_realtime(triple_timestamp
*ts
, usec_t u
) {
123 if (u
== USEC_INFINITY
|| u
<= 0) {
124 ts
->realtime
= ts
->monotonic
= ts
->boottime
= u
;
129 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
130 ts
->monotonic
= usec_sub_signed(now(CLOCK_MONOTONIC
), delta
);
131 ts
->boottime
= clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME
), delta
) : USEC_INFINITY
;
136 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
140 if (u
== USEC_INFINITY
) {
141 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
146 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
147 ts
->realtime
= usec_sub_signed(now(CLOCK_REALTIME
), delta
);
152 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
155 if (u
== USEC_INFINITY
) {
156 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
160 dual_timestamp_get(ts
);
161 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
162 ts
->realtime
= usec_sub_signed(ts
->realtime
, delta
);
163 ts
->monotonic
= usec_sub_signed(ts
->monotonic
, delta
);
168 usec_t
triple_timestamp_by_clock(triple_timestamp
*ts
, clockid_t clock
) {
173 case CLOCK_REALTIME_ALARM
:
176 case CLOCK_MONOTONIC
:
177 return ts
->monotonic
;
180 case CLOCK_BOOTTIME_ALARM
:
184 return USEC_INFINITY
;
188 usec_t
timespec_load(const struct timespec
*ts
) {
191 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
192 return USEC_INFINITY
;
194 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
195 return USEC_INFINITY
;
198 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
199 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
202 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
205 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
206 return NSEC_INFINITY
;
208 if ((nsec_t
) ts
->tv_sec
>= (UINT64_MAX
- ts
->tv_nsec
) / NSEC_PER_SEC
)
209 return NSEC_INFINITY
;
211 return (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+ (nsec_t
) ts
->tv_nsec
;
214 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
217 if (u
== USEC_INFINITY
||
218 u
/ USEC_PER_SEC
>= TIME_T_MAX
) {
219 ts
->tv_sec
= (time_t) -1;
220 ts
->tv_nsec
= (long) -1;
224 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
225 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
230 usec_t
timeval_load(const struct timeval
*tv
) {
233 if (tv
->tv_sec
< 0 || tv
->tv_usec
< 0)
234 return USEC_INFINITY
;
236 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
237 return USEC_INFINITY
;
240 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
241 (usec_t
) tv
->tv_usec
;
244 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
247 if (u
== USEC_INFINITY
||
248 u
/ USEC_PER_SEC
> TIME_T_MAX
) {
249 tv
->tv_sec
= (time_t) -1;
250 tv
->tv_usec
= (suseconds_t
) -1;
252 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
253 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
259 static char *format_timestamp_internal(
266 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
267 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
268 static const char * const weekdays
[] = {
286 1 + 10 + /* space and date */
287 1 + 8 + /* space and time */
288 (us
? 1 + 6 : 0) + /* "." and microsecond part */
289 1 + 1 + /* space and shortest possible zone */
291 return NULL
; /* Not enough space even for the shortest form. */
292 if (t
<= 0 || t
== USEC_INFINITY
)
293 return NULL
; /* Timestamp is unset */
295 /* Let's not format times with years > 9999 */
296 if (t
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
299 sec
= (time_t) (t
/ USEC_PER_SEC
); /* Round down */
301 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
304 /* Start with the week day */
305 assert((size_t) tm
.tm_wday
< ELEMENTSOF(weekdays
));
306 memcpy(buf
, weekdays
[tm
.tm_wday
], 4);
308 /* Add the main components */
309 if (strftime(buf
+ 3, l
- 3, " %Y-%m-%d %H:%M:%S", &tm
) <= 0)
310 return NULL
; /* Doesn't fit */
312 /* Append the microseconds part, if that's requested */
316 return NULL
; /* Microseconds part doesn't fit. */
318 sprintf(buf
+ n
, ".%06"PRI_USEC
, t
% USEC_PER_SEC
);
321 /* Append the timezone */
324 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
325 * obsolete "GMT" instead. */
327 return NULL
; /* "UTC" doesn't fit. */
329 strcpy(buf
+ n
, " UTC");
331 } else if (!isempty(tm
.tm_zone
)) {
334 /* An explicit timezone is specified, let's use it, if it fits */
335 tn
= strlen(tm
.tm_zone
);
336 if (n
+ 1 + tn
+ 1 > l
) {
337 /* The full time zone does not fit in. Yuck. */
339 if (n
+ 1 + _POSIX_TZNAME_MAX
+ 1 > l
)
340 return NULL
; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
342 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
343 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
344 * an overly long, hard to read string on the user. This should be safe, because the user will
345 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
348 strcpy(buf
+ n
, tm
.tm_zone
);
355 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
356 return format_timestamp_internal(buf
, l
, t
, false, false);
359 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
360 return format_timestamp_internal(buf
, l
, t
, true, false);
363 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
364 return format_timestamp_internal(buf
, l
, t
, false, true);
367 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
368 return format_timestamp_internal(buf
, l
, t
, true, true);
371 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
375 if (t
<= 0 || t
== USEC_INFINITY
)
378 n
= now(CLOCK_REALTIME
);
387 if (d
>= USEC_PER_YEAR
)
388 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
390 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
391 else if (d
>= USEC_PER_MONTH
)
392 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
394 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
395 else if (d
>= USEC_PER_WEEK
)
396 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
398 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
399 else if (d
>= 2*USEC_PER_DAY
)
400 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
401 else if (d
>= 25*USEC_PER_HOUR
)
402 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
403 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
404 else if (d
>= 6*USEC_PER_HOUR
)
405 snprintf(buf
, l
, USEC_FMT
"h %s",
406 d
/ USEC_PER_HOUR
, s
);
407 else if (d
>= USEC_PER_HOUR
)
408 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
410 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
411 else if (d
>= 5*USEC_PER_MINUTE
)
412 snprintf(buf
, l
, USEC_FMT
"min %s",
413 d
/ USEC_PER_MINUTE
, s
);
414 else if (d
>= USEC_PER_MINUTE
)
415 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
417 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
418 else if (d
>= USEC_PER_SEC
)
419 snprintf(buf
, l
, USEC_FMT
"s %s",
420 d
/ USEC_PER_SEC
, s
);
421 else if (d
>= USEC_PER_MSEC
)
422 snprintf(buf
, l
, USEC_FMT
"ms %s",
423 d
/ USEC_PER_MSEC
, s
);
425 snprintf(buf
, l
, USEC_FMT
"us %s",
428 snprintf(buf
, l
, "now");
434 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
435 static const struct {
439 { "y", USEC_PER_YEAR
},
440 { "month", USEC_PER_MONTH
},
441 { "w", USEC_PER_WEEK
},
442 { "d", USEC_PER_DAY
},
443 { "h", USEC_PER_HOUR
},
444 { "min", USEC_PER_MINUTE
},
445 { "s", USEC_PER_SEC
},
446 { "ms", USEC_PER_MSEC
},
452 bool something
= false;
457 if (t
== USEC_INFINITY
) {
458 strncpy(p
, "infinity", l
-1);
464 strncpy(p
, "0", l
-1);
469 /* The result of this function can be parsed with parse_sec */
471 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
480 if (t
< accuracy
&& something
)
483 if (t
< table
[i
].usec
)
489 a
= t
/ table
[i
].usec
;
490 b
= t
% table
[i
].usec
;
492 /* Let's see if we should shows this in dot notation */
493 if (t
< USEC_PER_MINUTE
&& b
> 0) {
498 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
501 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
508 "%s"USEC_FMT
".%0*"PRI_USEC
"%s",
520 /* No? Then let's show it normally */
531 n
= MIN((size_t) k
, l
);
544 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
550 if (!dual_timestamp_is_set(t
))
553 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
559 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
566 pos
= strspn(value
, WHITESPACE
);
567 if (value
[pos
] == '-')
569 pos
+= strspn(value
+ pos
, DIGITS
);
570 pos
+= strspn(value
+ pos
, WHITESPACE
);
571 if (value
[pos
] == '-')
574 r
= sscanf(value
, "%" PRIu64
"%" PRIu64
"%n", &a
, &b
, &pos
);
576 log_debug("Failed to parse dual timestamp value \"%s\".", value
);
580 if (value
[pos
] != '\0')
581 /* trailing garbage */
590 int timestamp_deserialize(const char *value
, usec_t
*timestamp
) {
595 r
= safe_atou64(value
, timestamp
);
597 return log_debug_errno(r
, "Failed to parse timestamp value \"%s\": %m", value
);
602 static int parse_timestamp_impl(const char *t
, usec_t
*usec
, bool with_tz
) {
603 static const struct {
623 const char *k
, *utc
= NULL
, *tzn
= NULL
;
626 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
627 int r
, weekday
= -1, dst
= -1;
633 * 2012-09-22 16:34:22
634 * 2012-09-22 16:34 (seconds will be set to 0)
635 * 2012-09-22 (time will be set to 00:00:00)
636 * 16:34:22 (date will be set to today)
637 * 16:34 (date will be set to today, seconds to 0)
639 * yesterday (time is set to 00:00:00)
640 * today (time is set to 00:00:00)
641 * tomorrow (time is set to 00:00:00)
644 * @2147483647 (seconds since epoch)
651 if (t
[0] == '@' && !with_tz
)
652 return parse_sec(t
+ 1, usec
);
654 ret
= now(CLOCK_REALTIME
);
660 else if (t
[0] == '+') {
661 r
= parse_sec(t
+1, &plus
);
667 } else if (t
[0] == '-') {
668 r
= parse_sec(t
+1, &minus
);
674 } else if ((k
= endswith(t
, " ago"))) {
675 t
= strndupa(t
, k
- t
);
677 r
= parse_sec(t
, &minus
);
683 } else if ((k
= endswith(t
, " left"))) {
684 t
= strndupa(t
, k
- t
);
686 r
= parse_sec(t
, &plus
);
693 /* See if the timestamp is suffixed with UTC */
694 utc
= endswith_no_case(t
, " UTC");
696 t
= strndupa(t
, utc
- t
);
698 const char *e
= NULL
;
703 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
704 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
705 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
706 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
707 * support arbitrary timezone specifications. */
709 for (j
= 0; j
<= 1; j
++) {
711 if (isempty(tzname
[j
]))
714 e
= endswith_no_case(t
, tzname
[j
]);
725 if (IN_SET(j
, 0, 1)) {
726 /* Found one of the two timezones specified. */
727 t
= strndupa(t
, e
- t
- 1);
734 x
= (time_t) (ret
/ USEC_PER_SEC
);
737 if (!localtime_or_gmtime_r(&x
, &tm
, utc
))
744 if (streq(t
, "today")) {
745 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
748 } else if (streq(t
, "yesterday")) {
750 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
753 } else if (streq(t
, "tomorrow")) {
755 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
759 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
762 if (!startswith_no_case(t
, day_nr
[i
].name
))
765 skip
= strlen(day_nr
[i
].name
);
769 weekday
= day_nr
[i
].nr
;
775 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
784 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
793 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
800 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
807 k
= strptime(t
, "%y-%m-%d", &tm
);
809 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
814 k
= strptime(t
, "%Y-%m-%d", &tm
);
816 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
821 k
= strptime(t
, "%H:%M:%S", &tm
);
830 k
= strptime(t
, "%H:%M", &tm
);
843 r
= parse_fractional_part_u(&k
, 6, &add
);
854 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
857 x
= mktime_or_timegm(&tm
, utc
);
861 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
862 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
866 if (ret
+ plus
< ret
) /* overflow? */
869 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
882 typedef struct ParseTimestampResult
{
885 } ParseTimestampResult
;
887 int parse_timestamp(const char *t
, usec_t
*usec
) {
888 char *last_space
, *tz
= NULL
;
889 ParseTimestampResult
*shared
, tmp
;
892 last_space
= strrchr(t
, ' ');
893 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1))
896 if (!tz
|| endswith_no_case(t
, " UTC"))
897 return parse_timestamp_impl(t
, usec
, false);
899 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
900 if (shared
== MAP_FAILED
)
901 return negative_errno();
903 r
= safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_WAIT
, NULL
);
905 (void) munmap(shared
, sizeof *shared
);
911 if (setenv("TZ", tz
, 1) != 0) {
912 shared
->return_value
= negative_errno();
918 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
919 * Otherwise just cut it off */
920 with_tz
= !STR_IN_SET(tz
, tzname
[0], tzname
[1]);
922 /*cut off the timezone if we dont need it*/
924 t
= strndupa(t
, last_space
- t
);
926 shared
->return_value
= parse_timestamp_impl(t
, &shared
->usec
, with_tz
);
932 if (munmap(shared
, sizeof *shared
) != 0)
933 return negative_errno();
935 if (tmp
.return_value
== 0)
938 return tmp
.return_value
;
941 static char* extract_multiplier(char *p
, usec_t
*multiplier
) {
942 static const struct {
946 { "seconds", USEC_PER_SEC
},
947 { "second", USEC_PER_SEC
},
948 { "sec", USEC_PER_SEC
},
949 { "s", USEC_PER_SEC
},
950 { "minutes", USEC_PER_MINUTE
},
951 { "minute", USEC_PER_MINUTE
},
952 { "min", USEC_PER_MINUTE
},
953 { "months", USEC_PER_MONTH
},
954 { "month", USEC_PER_MONTH
},
955 { "M", USEC_PER_MONTH
},
956 { "msec", USEC_PER_MSEC
},
957 { "ms", USEC_PER_MSEC
},
958 { "m", USEC_PER_MINUTE
},
959 { "hours", USEC_PER_HOUR
},
960 { "hour", USEC_PER_HOUR
},
961 { "hr", USEC_PER_HOUR
},
962 { "h", USEC_PER_HOUR
},
963 { "days", USEC_PER_DAY
},
964 { "day", USEC_PER_DAY
},
965 { "d", USEC_PER_DAY
},
966 { "weeks", USEC_PER_WEEK
},
967 { "week", USEC_PER_WEEK
},
968 { "w", USEC_PER_WEEK
},
969 { "years", USEC_PER_YEAR
},
970 { "year", USEC_PER_YEAR
},
971 { "y", USEC_PER_YEAR
},
978 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
981 e
= startswith(p
, table
[i
].suffix
);
983 *multiplier
= table
[i
].usec
;
991 int parse_time(const char *t
, usec_t
*usec
, usec_t default_unit
) {
994 bool something
= false;
998 assert(default_unit
> 0);
1002 p
+= strspn(p
, WHITESPACE
);
1003 s
= startswith(p
, "infinity");
1005 s
+= strspn(s
, WHITESPACE
);
1009 *usec
= USEC_INFINITY
;
1017 usec_t multiplier
= default_unit
, k
;
1019 p
+= strspn(p
, WHITESPACE
);
1029 l
= strtoll(p
, &e
, 10);
1039 z
= strtoll(b
, &e
, 10);
1054 e
+= strspn(e
, WHITESPACE
);
1055 p
= extract_multiplier(e
, &multiplier
);
1059 k
= (usec_t
) z
* multiplier
;
1064 r
+= (usec_t
) l
* multiplier
+ k
;
1072 int parse_sec(const char *t
, usec_t
*usec
) {
1073 return parse_time(t
, usec
, USEC_PER_SEC
);
1076 int parse_sec_fix_0(const char *t
, usec_t
*usec
) {
1080 t
+= strspn(t
, WHITESPACE
);
1082 if (streq(t
, "0")) {
1083 *usec
= USEC_INFINITY
;
1087 return parse_sec(t
, usec
);
1090 int parse_nsec(const char *t
, nsec_t
*nsec
) {
1091 static const struct {
1095 { "seconds", NSEC_PER_SEC
},
1096 { "second", NSEC_PER_SEC
},
1097 { "sec", NSEC_PER_SEC
},
1098 { "s", NSEC_PER_SEC
},
1099 { "minutes", NSEC_PER_MINUTE
},
1100 { "minute", NSEC_PER_MINUTE
},
1101 { "min", NSEC_PER_MINUTE
},
1102 { "months", NSEC_PER_MONTH
},
1103 { "month", NSEC_PER_MONTH
},
1104 { "msec", NSEC_PER_MSEC
},
1105 { "ms", NSEC_PER_MSEC
},
1106 { "m", NSEC_PER_MINUTE
},
1107 { "hours", NSEC_PER_HOUR
},
1108 { "hour", NSEC_PER_HOUR
},
1109 { "hr", NSEC_PER_HOUR
},
1110 { "h", NSEC_PER_HOUR
},
1111 { "days", NSEC_PER_DAY
},
1112 { "day", NSEC_PER_DAY
},
1113 { "d", NSEC_PER_DAY
},
1114 { "weeks", NSEC_PER_WEEK
},
1115 { "week", NSEC_PER_WEEK
},
1116 { "w", NSEC_PER_WEEK
},
1117 { "years", NSEC_PER_YEAR
},
1118 { "year", NSEC_PER_YEAR
},
1119 { "y", NSEC_PER_YEAR
},
1120 { "usec", NSEC_PER_USEC
},
1121 { "us", NSEC_PER_USEC
},
1122 { "µs", NSEC_PER_USEC
},
1125 { "", 1ULL }, /* default is nsec */
1130 bool something
= false;
1137 p
+= strspn(p
, WHITESPACE
);
1138 s
= startswith(p
, "infinity");
1140 s
+= strspn(s
, WHITESPACE
);
1144 *nsec
= NSEC_INFINITY
;
1153 p
+= strspn(p
, WHITESPACE
);
1163 l
= strtoll(p
, &e
, 10);
1175 z
= strtoll(b
, &e
, 10);
1190 e
+= strspn(e
, WHITESPACE
);
1192 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
1193 if (startswith(e
, table
[i
].suffix
)) {
1194 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
1199 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
1200 p
= e
+ strlen(table
[i
].suffix
);
1206 if (i
>= ELEMENTSOF(table
))
1216 bool ntp_synced(void) {
1217 struct timex txc
= {};
1219 if (adjtimex(&txc
) < 0)
1222 if (txc
.status
& STA_UNSYNC
)
1228 int get_timezones(char ***ret
) {
1229 _cleanup_fclose_
FILE *f
= NULL
;
1230 _cleanup_strv_free_
char **zones
= NULL
;
1231 size_t n_zones
= 0, n_allocated
= 0;
1235 zones
= strv_new("UTC", NULL
);
1242 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
1246 FOREACH_LINE(l
, f
, return -errno
) {
1252 if (isempty(p
) || *p
== '#')
1255 /* Skip over country code */
1256 p
+= strcspn(p
, WHITESPACE
);
1257 p
+= strspn(p
, WHITESPACE
);
1259 /* Skip over coordinates */
1260 p
+= strcspn(p
, WHITESPACE
);
1261 p
+= strspn(p
, WHITESPACE
);
1263 /* Found timezone name */
1264 k
= strcspn(p
, WHITESPACE
);
1272 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1277 zones
[n_zones
++] = w
;
1278 zones
[n_zones
] = NULL
;
1283 } else if (errno
!= ENOENT
)
1286 *ret
= TAKE_PTR(zones
);
1291 bool timezone_is_valid(const char *name
) {
1302 for (p
= name
; *p
; p
++) {
1303 if (!(*p
>= '0' && *p
<= '9') &&
1304 !(*p
>= 'a' && *p
<= 'z') &&
1305 !(*p
>= 'A' && *p
<= 'Z') &&
1306 !IN_SET(*p
, '-', '_', '+', '/'))
1322 t
= strjoina("/usr/share/zoneinfo/", name
);
1323 if (stat(t
, &st
) < 0)
1326 if (!S_ISREG(st
.st_mode
))
1332 bool clock_boottime_supported(void) {
1333 static int supported
= -1;
1335 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1337 if (supported
< 0) {
1340 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1352 clockid_t
clock_boottime_or_monotonic(void) {
1353 if (clock_boottime_supported())
1354 return CLOCK_BOOTTIME
;
1356 return CLOCK_MONOTONIC
;
1359 bool clock_supported(clockid_t clock
) {
1364 case CLOCK_MONOTONIC
:
1365 case CLOCK_REALTIME
:
1368 case CLOCK_BOOTTIME
:
1369 return clock_boottime_supported();
1371 case CLOCK_BOOTTIME_ALARM
:
1372 if (!clock_boottime_supported())
1377 /* For everything else, check properly */
1378 return clock_gettime(clock
, &ts
) >= 0;
1382 int get_timezone(char **tz
) {
1383 _cleanup_free_
char *t
= NULL
;
1388 r
= readlink_malloc("/etc/localtime", &t
);
1390 return r
; /* returns EINVAL if not a symlink */
1392 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1394 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1398 if (!timezone_is_valid(e
))
1409 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1410 return utc
? timegm(tm
) : mktime(tm
);
1413 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1414 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1417 unsigned long usec_to_jiffies(usec_t u
) {
1418 static thread_local
unsigned long hz
= 0;
1422 r
= sysconf(_SC_CLK_TCK
);
1428 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);
1431 usec_t
usec_shift_clock(usec_t x
, clockid_t from
, clockid_t to
) {
1434 if (x
== USEC_INFINITY
)
1435 return USEC_INFINITY
;
1436 if (map_clock_id(from
) == map_clock_id(to
))
1443 /* x lies in the future */
1444 return usec_add(b
, usec_sub_unsigned(x
, a
));
1446 /* x lies in the past */
1447 return usec_sub_unsigned(b
, usec_sub_unsigned(a
, x
));
1450 bool in_utc_timezone(void) {
1453 return timezone
== 0 && daylight
== 0;