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 nsec_t
now_nsec(clockid_t clock_id
) {
76 assert_se(clock_gettime(map_clock_id(clock_id
), &ts
) == 0);
78 return timespec_load_nsec(&ts
);
81 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
84 ts
->realtime
= now(CLOCK_REALTIME
);
85 ts
->monotonic
= now(CLOCK_MONOTONIC
);
90 triple_timestamp
* triple_timestamp_get(triple_timestamp
*ts
) {
93 ts
->realtime
= now(CLOCK_REALTIME
);
94 ts
->monotonic
= now(CLOCK_MONOTONIC
);
95 ts
->boottime
= clock_boottime_supported() ? now(CLOCK_BOOTTIME
) : USEC_INFINITY
;
100 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
104 if (u
== USEC_INFINITY
|| u
<= 0) {
105 ts
->realtime
= ts
->monotonic
= u
;
111 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
112 ts
->monotonic
= usec_sub_signed(now(CLOCK_MONOTONIC
), delta
);
117 triple_timestamp
* triple_timestamp_from_realtime(triple_timestamp
*ts
, usec_t u
) {
122 if (u
== USEC_INFINITY
|| u
<= 0) {
123 ts
->realtime
= ts
->monotonic
= ts
->boottime
= u
;
128 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
129 ts
->monotonic
= usec_sub_signed(now(CLOCK_MONOTONIC
), delta
);
130 ts
->boottime
= clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME
), delta
) : USEC_INFINITY
;
135 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
139 if (u
== USEC_INFINITY
) {
140 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
145 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
146 ts
->realtime
= usec_sub_signed(now(CLOCK_REALTIME
), delta
);
151 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
154 if (u
== USEC_INFINITY
) {
155 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
159 dual_timestamp_get(ts
);
160 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
161 ts
->realtime
= usec_sub_signed(ts
->realtime
, delta
);
162 ts
->monotonic
= usec_sub_signed(ts
->monotonic
, delta
);
167 usec_t
triple_timestamp_by_clock(triple_timestamp
*ts
, clockid_t clock
) {
172 case CLOCK_REALTIME_ALARM
:
175 case CLOCK_MONOTONIC
:
176 return ts
->monotonic
;
179 case CLOCK_BOOTTIME_ALARM
:
183 return USEC_INFINITY
;
187 usec_t
timespec_load(const struct timespec
*ts
) {
190 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
191 return USEC_INFINITY
;
193 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
194 return USEC_INFINITY
;
197 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
198 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
201 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
204 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
205 return NSEC_INFINITY
;
207 if ((nsec_t
) ts
->tv_sec
>= (UINT64_MAX
- ts
->tv_nsec
) / NSEC_PER_SEC
)
208 return NSEC_INFINITY
;
210 return (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+ (nsec_t
) ts
->tv_nsec
;
213 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
216 if (u
== USEC_INFINITY
||
217 u
/ USEC_PER_SEC
>= TIME_T_MAX
) {
218 ts
->tv_sec
= (time_t) -1;
219 ts
->tv_nsec
= (long) -1;
223 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
224 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
229 usec_t
timeval_load(const struct timeval
*tv
) {
232 if (tv
->tv_sec
< 0 || tv
->tv_usec
< 0)
233 return USEC_INFINITY
;
235 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
236 return USEC_INFINITY
;
239 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
240 (usec_t
) tv
->tv_usec
;
243 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
246 if (u
== USEC_INFINITY
||
247 u
/ USEC_PER_SEC
> TIME_T_MAX
) {
248 tv
->tv_sec
= (time_t) -1;
249 tv
->tv_usec
= (suseconds_t
) -1;
251 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
252 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
258 static char *format_timestamp_internal(
265 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
266 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
267 static const char * const weekdays
[] = {
285 1 + 10 + /* space and date */
286 1 + 8 + /* space and time */
287 (us
? 1 + 6 : 0) + /* "." and microsecond part */
288 1 + 1 + /* space and shortest possible zone */
290 return NULL
; /* Not enough space even for the shortest form. */
291 if (t
<= 0 || t
== USEC_INFINITY
)
292 return NULL
; /* Timestamp is unset */
294 /* Let's not format times with years > 9999 */
295 if (t
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
298 sec
= (time_t) (t
/ USEC_PER_SEC
); /* Round down */
300 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
303 /* Start with the week day */
304 assert((size_t) tm
.tm_wday
< ELEMENTSOF(weekdays
));
305 memcpy(buf
, weekdays
[tm
.tm_wday
], 4);
307 /* Add the main components */
308 if (strftime(buf
+ 3, l
- 3, " %Y-%m-%d %H:%M:%S", &tm
) <= 0)
309 return NULL
; /* Doesn't fit */
311 /* Append the microseconds part, if that's requested */
315 return NULL
; /* Microseconds part doesn't fit. */
317 sprintf(buf
+ n
, ".%06"PRI_USEC
, t
% USEC_PER_SEC
);
320 /* Append the timezone */
323 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
324 * obsolete "GMT" instead. */
326 return NULL
; /* "UTC" doesn't fit. */
328 strcpy(buf
+ n
, " UTC");
330 } else if (!isempty(tm
.tm_zone
)) {
333 /* An explicit timezone is specified, let's use it, if it fits */
334 tn
= strlen(tm
.tm_zone
);
335 if (n
+ 1 + tn
+ 1 > l
) {
336 /* The full time zone does not fit in. Yuck. */
338 if (n
+ 1 + _POSIX_TZNAME_MAX
+ 1 > l
)
339 return NULL
; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
341 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
342 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
343 * an overly long, hard to read string on the user. This should be safe, because the user will
344 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
347 strcpy(buf
+ n
, tm
.tm_zone
);
354 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
355 return format_timestamp_internal(buf
, l
, t
, false, false);
358 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
359 return format_timestamp_internal(buf
, l
, t
, true, false);
362 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
363 return format_timestamp_internal(buf
, l
, t
, false, true);
366 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
367 return format_timestamp_internal(buf
, l
, t
, true, true);
370 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
374 if (t
<= 0 || t
== USEC_INFINITY
)
377 n
= now(CLOCK_REALTIME
);
386 if (d
>= USEC_PER_YEAR
)
387 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
389 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
390 else if (d
>= USEC_PER_MONTH
)
391 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
393 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
394 else if (d
>= USEC_PER_WEEK
)
395 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
397 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
398 else if (d
>= 2*USEC_PER_DAY
)
399 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
400 else if (d
>= 25*USEC_PER_HOUR
)
401 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
402 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
403 else if (d
>= 6*USEC_PER_HOUR
)
404 snprintf(buf
, l
, USEC_FMT
"h %s",
405 d
/ USEC_PER_HOUR
, s
);
406 else if (d
>= USEC_PER_HOUR
)
407 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
409 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
410 else if (d
>= 5*USEC_PER_MINUTE
)
411 snprintf(buf
, l
, USEC_FMT
"min %s",
412 d
/ USEC_PER_MINUTE
, s
);
413 else if (d
>= USEC_PER_MINUTE
)
414 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
416 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
417 else if (d
>= USEC_PER_SEC
)
418 snprintf(buf
, l
, USEC_FMT
"s %s",
419 d
/ USEC_PER_SEC
, s
);
420 else if (d
>= USEC_PER_MSEC
)
421 snprintf(buf
, l
, USEC_FMT
"ms %s",
422 d
/ USEC_PER_MSEC
, s
);
424 snprintf(buf
, l
, USEC_FMT
"us %s",
427 snprintf(buf
, l
, "now");
433 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
434 static const struct {
438 { "y", USEC_PER_YEAR
},
439 { "month", USEC_PER_MONTH
},
440 { "w", USEC_PER_WEEK
},
441 { "d", USEC_PER_DAY
},
442 { "h", USEC_PER_HOUR
},
443 { "min", USEC_PER_MINUTE
},
444 { "s", USEC_PER_SEC
},
445 { "ms", USEC_PER_MSEC
},
451 bool something
= false;
456 if (t
== USEC_INFINITY
) {
457 strncpy(p
, "infinity", l
-1);
463 strncpy(p
, "0", l
-1);
468 /* The result of this function can be parsed with parse_sec */
470 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
479 if (t
< accuracy
&& something
)
482 if (t
< table
[i
].usec
)
488 a
= t
/ table
[i
].usec
;
489 b
= t
% table
[i
].usec
;
491 /* Let's see if we should shows this in dot notation */
492 if (t
< USEC_PER_MINUTE
&& b
> 0) {
497 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
500 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
507 "%s"USEC_FMT
".%0*"PRI_USEC
"%s",
519 /* No? Then let's show it normally */
530 n
= MIN((size_t) k
, l
);
543 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
549 if (!dual_timestamp_is_set(t
))
552 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
558 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
565 pos
= strspn(value
, WHITESPACE
);
566 if (value
[pos
] == '-')
568 pos
+= strspn(value
+ pos
, DIGITS
);
569 pos
+= strspn(value
+ pos
, WHITESPACE
);
570 if (value
[pos
] == '-')
573 r
= sscanf(value
, "%" PRIu64
"%" PRIu64
"%n", &a
, &b
, &pos
);
575 log_debug("Failed to parse dual timestamp value \"%s\".", value
);
579 if (value
[pos
] != '\0')
580 /* trailing garbage */
589 int timestamp_deserialize(const char *value
, usec_t
*timestamp
) {
594 r
= safe_atou64(value
, timestamp
);
596 return log_debug_errno(r
, "Failed to parse timestamp value \"%s\": %m", value
);
601 static int parse_timestamp_impl(const char *t
, usec_t
*usec
, bool with_tz
) {
602 static const struct {
622 const char *k
, *utc
= NULL
, *tzn
= NULL
;
625 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
626 int r
, weekday
= -1, dst
= -1;
632 * 2012-09-22 16:34:22
633 * 2012-09-22 16:34 (seconds will be set to 0)
634 * 2012-09-22 (time will be set to 00:00:00)
635 * 16:34:22 (date will be set to today)
636 * 16:34 (date will be set to today, seconds to 0)
638 * yesterday (time is set to 00:00:00)
639 * today (time is set to 00:00:00)
640 * tomorrow (time is set to 00:00:00)
643 * @2147483647 (seconds since epoch)
650 if (t
[0] == '@' && !with_tz
)
651 return parse_sec(t
+ 1, usec
);
653 ret
= now(CLOCK_REALTIME
);
659 else if (t
[0] == '+') {
660 r
= parse_sec(t
+1, &plus
);
666 } else if (t
[0] == '-') {
667 r
= parse_sec(t
+1, &minus
);
673 } else if ((k
= endswith(t
, " ago"))) {
674 t
= strndupa(t
, k
- t
);
676 r
= parse_sec(t
, &minus
);
682 } else if ((k
= endswith(t
, " left"))) {
683 t
= strndupa(t
, k
- t
);
685 r
= parse_sec(t
, &plus
);
692 /* See if the timestamp is suffixed with UTC */
693 utc
= endswith_no_case(t
, " UTC");
695 t
= strndupa(t
, utc
- t
);
697 const char *e
= NULL
;
702 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
703 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
704 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
705 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
706 * support arbitrary timezone specifications. */
708 for (j
= 0; j
<= 1; j
++) {
710 if (isempty(tzname
[j
]))
713 e
= endswith_no_case(t
, tzname
[j
]);
724 if (IN_SET(j
, 0, 1)) {
725 /* Found one of the two timezones specified. */
726 t
= strndupa(t
, e
- t
- 1);
733 x
= (time_t) (ret
/ USEC_PER_SEC
);
736 if (!localtime_or_gmtime_r(&x
, &tm
, utc
))
743 if (streq(t
, "today")) {
744 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
747 } else if (streq(t
, "yesterday")) {
749 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
752 } else if (streq(t
, "tomorrow")) {
754 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
758 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
761 if (!startswith_no_case(t
, day_nr
[i
].name
))
764 skip
= strlen(day_nr
[i
].name
);
768 weekday
= day_nr
[i
].nr
;
774 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
783 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
792 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
799 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
806 k
= strptime(t
, "%y-%m-%d", &tm
);
808 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
813 k
= strptime(t
, "%Y-%m-%d", &tm
);
815 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
820 k
= strptime(t
, "%H:%M:%S", &tm
);
829 k
= strptime(t
, "%H:%M", &tm
);
842 r
= parse_fractional_part_u(&k
, 6, &add
);
853 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
856 x
= mktime_or_timegm(&tm
, utc
);
860 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
861 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
865 if (ret
+ plus
< ret
) /* overflow? */
868 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
881 typedef struct ParseTimestampResult
{
884 } ParseTimestampResult
;
886 int parse_timestamp(const char *t
, usec_t
*usec
) {
887 char *last_space
, *tz
= NULL
;
888 ParseTimestampResult
*shared
, tmp
;
892 last_space
= strrchr(t
, ' ');
893 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1))
896 if (tz
== NULL
|| 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();
906 int fork_errno
= errno
;
907 (void) munmap(shared
, sizeof *shared
);
914 if (setenv("TZ", tz
, 1) != 0) {
915 shared
->return_value
= negative_errno();
921 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
922 * Otherwise just cut it off */
923 with_tz
= !STR_IN_SET(tz
, tzname
[0], tzname
[1]);
925 /*cut off the timezone if we dont need it*/
927 t
= strndupa(t
, last_space
- t
);
929 shared
->return_value
= parse_timestamp_impl(t
, &shared
->usec
, with_tz
);
934 r
= wait_for_terminate(pid
, NULL
);
936 (void) munmap(shared
, sizeof *shared
);
941 if (munmap(shared
, sizeof *shared
) != 0)
942 return negative_errno();
944 if (tmp
.return_value
== 0)
947 return tmp
.return_value
;
950 static char* extract_multiplier(char *p
, usec_t
*multiplier
) {
951 static const struct {
955 { "seconds", USEC_PER_SEC
},
956 { "second", USEC_PER_SEC
},
957 { "sec", USEC_PER_SEC
},
958 { "s", USEC_PER_SEC
},
959 { "minutes", USEC_PER_MINUTE
},
960 { "minute", USEC_PER_MINUTE
},
961 { "min", USEC_PER_MINUTE
},
962 { "months", USEC_PER_MONTH
},
963 { "month", USEC_PER_MONTH
},
964 { "M", USEC_PER_MONTH
},
965 { "msec", USEC_PER_MSEC
},
966 { "ms", USEC_PER_MSEC
},
967 { "m", USEC_PER_MINUTE
},
968 { "hours", USEC_PER_HOUR
},
969 { "hour", USEC_PER_HOUR
},
970 { "hr", USEC_PER_HOUR
},
971 { "h", USEC_PER_HOUR
},
972 { "days", USEC_PER_DAY
},
973 { "day", USEC_PER_DAY
},
974 { "d", USEC_PER_DAY
},
975 { "weeks", USEC_PER_WEEK
},
976 { "week", USEC_PER_WEEK
},
977 { "w", USEC_PER_WEEK
},
978 { "years", USEC_PER_YEAR
},
979 { "year", USEC_PER_YEAR
},
980 { "y", USEC_PER_YEAR
},
987 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
990 e
= startswith(p
, table
[i
].suffix
);
992 *multiplier
= table
[i
].usec
;
1000 int parse_time(const char *t
, usec_t
*usec
, usec_t default_unit
) {
1003 bool something
= false;
1007 assert(default_unit
> 0);
1011 p
+= strspn(p
, WHITESPACE
);
1012 s
= startswith(p
, "infinity");
1014 s
+= strspn(s
, WHITESPACE
);
1018 *usec
= USEC_INFINITY
;
1026 usec_t multiplier
= default_unit
, k
;
1028 p
+= strspn(p
, WHITESPACE
);
1038 l
= strtoll(p
, &e
, 10);
1048 z
= strtoll(b
, &e
, 10);
1063 e
+= strspn(e
, WHITESPACE
);
1064 p
= extract_multiplier(e
, &multiplier
);
1068 k
= (usec_t
) z
* multiplier
;
1073 r
+= (usec_t
) l
* multiplier
+ k
;
1081 int parse_sec(const char *t
, usec_t
*usec
) {
1082 return parse_time(t
, usec
, USEC_PER_SEC
);
1085 int parse_sec_fix_0(const char *t
, usec_t
*usec
) {
1089 t
+= strspn(t
, WHITESPACE
);
1091 if (streq(t
, "0")) {
1092 *usec
= USEC_INFINITY
;
1096 return parse_sec(t
, usec
);
1099 int parse_nsec(const char *t
, nsec_t
*nsec
) {
1100 static const struct {
1104 { "seconds", NSEC_PER_SEC
},
1105 { "second", NSEC_PER_SEC
},
1106 { "sec", NSEC_PER_SEC
},
1107 { "s", NSEC_PER_SEC
},
1108 { "minutes", NSEC_PER_MINUTE
},
1109 { "minute", NSEC_PER_MINUTE
},
1110 { "min", NSEC_PER_MINUTE
},
1111 { "months", NSEC_PER_MONTH
},
1112 { "month", NSEC_PER_MONTH
},
1113 { "msec", NSEC_PER_MSEC
},
1114 { "ms", NSEC_PER_MSEC
},
1115 { "m", NSEC_PER_MINUTE
},
1116 { "hours", NSEC_PER_HOUR
},
1117 { "hour", NSEC_PER_HOUR
},
1118 { "hr", NSEC_PER_HOUR
},
1119 { "h", NSEC_PER_HOUR
},
1120 { "days", NSEC_PER_DAY
},
1121 { "day", NSEC_PER_DAY
},
1122 { "d", NSEC_PER_DAY
},
1123 { "weeks", NSEC_PER_WEEK
},
1124 { "week", NSEC_PER_WEEK
},
1125 { "w", NSEC_PER_WEEK
},
1126 { "years", NSEC_PER_YEAR
},
1127 { "year", NSEC_PER_YEAR
},
1128 { "y", NSEC_PER_YEAR
},
1129 { "usec", NSEC_PER_USEC
},
1130 { "us", NSEC_PER_USEC
},
1131 { "µs", NSEC_PER_USEC
},
1134 { "", 1ULL }, /* default is nsec */
1139 bool something
= false;
1146 p
+= strspn(p
, WHITESPACE
);
1147 s
= startswith(p
, "infinity");
1149 s
+= strspn(s
, WHITESPACE
);
1153 *nsec
= NSEC_INFINITY
;
1162 p
+= strspn(p
, WHITESPACE
);
1172 l
= strtoll(p
, &e
, 10);
1184 z
= strtoll(b
, &e
, 10);
1199 e
+= strspn(e
, WHITESPACE
);
1201 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
1202 if (startswith(e
, table
[i
].suffix
)) {
1203 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
1208 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
1209 p
= e
+ strlen(table
[i
].suffix
);
1215 if (i
>= ELEMENTSOF(table
))
1225 bool ntp_synced(void) {
1226 struct timex txc
= {};
1228 if (adjtimex(&txc
) < 0)
1231 if (txc
.status
& STA_UNSYNC
)
1237 int get_timezones(char ***ret
) {
1238 _cleanup_fclose_
FILE *f
= NULL
;
1239 _cleanup_strv_free_
char **zones
= NULL
;
1240 size_t n_zones
= 0, n_allocated
= 0;
1244 zones
= strv_new("UTC", NULL
);
1251 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
1255 FOREACH_LINE(l
, f
, return -errno
) {
1261 if (isempty(p
) || *p
== '#')
1264 /* Skip over country code */
1265 p
+= strcspn(p
, WHITESPACE
);
1266 p
+= strspn(p
, WHITESPACE
);
1268 /* Skip over coordinates */
1269 p
+= strcspn(p
, WHITESPACE
);
1270 p
+= strspn(p
, WHITESPACE
);
1272 /* Found timezone name */
1273 k
= strcspn(p
, WHITESPACE
);
1281 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1286 zones
[n_zones
++] = w
;
1287 zones
[n_zones
] = NULL
;
1292 } else if (errno
!= ENOENT
)
1301 bool timezone_is_valid(const char *name
) {
1312 for (p
= name
; *p
; p
++) {
1313 if (!(*p
>= '0' && *p
<= '9') &&
1314 !(*p
>= 'a' && *p
<= 'z') &&
1315 !(*p
>= 'A' && *p
<= 'Z') &&
1316 !IN_SET(*p
, '-', '_', '+', '/'))
1332 t
= strjoina("/usr/share/zoneinfo/", name
);
1333 if (stat(t
, &st
) < 0)
1336 if (!S_ISREG(st
.st_mode
))
1342 bool clock_boottime_supported(void) {
1343 static int supported
= -1;
1345 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1347 if (supported
< 0) {
1350 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1362 clockid_t
clock_boottime_or_monotonic(void) {
1363 if (clock_boottime_supported())
1364 return CLOCK_BOOTTIME
;
1366 return CLOCK_MONOTONIC
;
1369 bool clock_supported(clockid_t clock
) {
1374 case CLOCK_MONOTONIC
:
1375 case CLOCK_REALTIME
:
1378 case CLOCK_BOOTTIME
:
1379 return clock_boottime_supported();
1381 case CLOCK_BOOTTIME_ALARM
:
1382 if (!clock_boottime_supported())
1388 /* For everything else, check properly */
1389 return clock_gettime(clock
, &ts
) >= 0;
1393 int get_timezone(char **tz
) {
1394 _cleanup_free_
char *t
= NULL
;
1399 r
= readlink_malloc("/etc/localtime", &t
);
1401 return r
; /* returns EINVAL if not a symlink */
1403 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1405 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1409 if (!timezone_is_valid(e
))
1420 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1421 return utc
? timegm(tm
) : mktime(tm
);
1424 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1425 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1428 unsigned long usec_to_jiffies(usec_t u
) {
1429 static thread_local
unsigned long hz
= 0;
1433 r
= sysconf(_SC_CLK_TCK
);
1439 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);
1442 usec_t
usec_shift_clock(usec_t x
, clockid_t from
, clockid_t to
) {
1445 if (x
== USEC_INFINITY
)
1446 return USEC_INFINITY
;
1447 if (map_clock_id(from
) == map_clock_id(to
))
1454 /* x lies in the future */
1455 return usec_add(b
, usec_sub_unsigned(x
, a
));
1457 /* x lies in the past */
1458 return usec_sub_unsigned(b
, usec_sub_unsigned(a
, x
));