1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include <sys/timerfd.h>
10 #include <sys/types.h>
13 #include "alloc-util.h"
20 #include "missing_threads.h"
21 #include "missing_timerfd.h"
22 #include "parse-util.h"
23 #include "path-util.h"
24 #include "process-util.h"
25 #include "stat-util.h"
26 #include "string-table.h"
27 #include "string-util.h"
29 #include "time-util.h"
31 static clockid_t
map_clock_id(clockid_t c
) {
33 /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus,
34 * clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM
35 * pendants (their only difference is when timers are set on them), let's just map them
36 * accordingly. This way, we can get the correct time even on those archs. */
40 case CLOCK_BOOTTIME_ALARM
:
41 return CLOCK_BOOTTIME
;
43 case CLOCK_REALTIME_ALARM
:
44 return CLOCK_REALTIME
;
51 usec_t
now(clockid_t clock_id
) {
54 assert_se(clock_gettime(map_clock_id(clock_id
), &ts
) == 0);
56 return timespec_load(&ts
);
59 nsec_t
now_nsec(clockid_t clock_id
) {
62 assert_se(clock_gettime(map_clock_id(clock_id
), &ts
) == 0);
64 return timespec_load_nsec(&ts
);
67 dual_timestamp
* dual_timestamp_now(dual_timestamp
*ts
) {
70 ts
->realtime
= now(CLOCK_REALTIME
);
71 ts
->monotonic
= now(CLOCK_MONOTONIC
);
76 triple_timestamp
* triple_timestamp_now(triple_timestamp
*ts
) {
79 ts
->realtime
= now(CLOCK_REALTIME
);
80 ts
->monotonic
= now(CLOCK_MONOTONIC
);
81 ts
->boottime
= now(CLOCK_BOOTTIME
);
86 static usec_t
map_clock_usec_internal(usec_t from
, usec_t from_base
, usec_t to_base
) {
88 /* Maps the time 'from' between two clocks, based on a common reference point where the first clock
89 * is at 'from_base' and the second clock at 'to_base'. Basically calculates:
91 * from - from_base + to_base
93 * But takes care of overflows/underflows and avoids signed operations. */
95 if (from
>= from_base
) { /* In the future */
96 usec_t delta
= from
- from_base
;
98 if (to_base
>= USEC_INFINITY
- delta
) /* overflow? */
101 return to_base
+ delta
;
103 } else { /* In the past */
104 usec_t delta
= from_base
- from
;
106 if (to_base
<= delta
) /* underflow? */
109 return to_base
- delta
;
113 usec_t
map_clock_usec(usec_t from
, clockid_t from_clock
, clockid_t to_clock
) {
115 /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
117 if (map_clock_id(from_clock
) == map_clock_id(to_clock
))
120 /* Keep infinity as is */
121 if (from
== USEC_INFINITY
)
124 return map_clock_usec_internal(from
, now(from_clock
), now(to_clock
));
127 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
130 if (!timestamp_is_set(u
)) {
131 ts
->realtime
= ts
->monotonic
= u
;
136 ts
->monotonic
= map_clock_usec(u
, CLOCK_REALTIME
, CLOCK_MONOTONIC
);
140 triple_timestamp
* triple_timestamp_from_realtime(triple_timestamp
*ts
, usec_t u
) {
145 if (!timestamp_is_set(u
)) {
146 ts
->realtime
= ts
->monotonic
= ts
->boottime
= u
;
150 nowr
= now(CLOCK_REALTIME
);
153 ts
->monotonic
= map_clock_usec_internal(u
, nowr
, now(CLOCK_MONOTONIC
));
154 ts
->boottime
= map_clock_usec_internal(u
, nowr
, now(CLOCK_BOOTTIME
));
159 triple_timestamp
* triple_timestamp_from_boottime(triple_timestamp
*ts
, usec_t u
) {
164 if (u
== USEC_INFINITY
) {
165 ts
->realtime
= ts
->monotonic
= ts
->boottime
= u
;
169 nowb
= now(CLOCK_BOOTTIME
);
172 ts
->monotonic
= map_clock_usec_internal(u
, nowb
, now(CLOCK_MONOTONIC
));
173 ts
->realtime
= map_clock_usec_internal(u
, nowb
, now(CLOCK_REALTIME
));
178 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
181 if (u
== USEC_INFINITY
) {
182 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
187 ts
->realtime
= map_clock_usec(u
, CLOCK_MONOTONIC
, CLOCK_REALTIME
);
191 dual_timestamp
* dual_timestamp_from_boottime(dual_timestamp
*ts
, usec_t u
) {
196 if (u
== USEC_INFINITY
) {
197 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
201 nowm
= now(CLOCK_BOOTTIME
);
202 ts
->monotonic
= map_clock_usec_internal(u
, nowm
, now(CLOCK_MONOTONIC
));
203 ts
->realtime
= map_clock_usec_internal(u
, nowm
, now(CLOCK_REALTIME
));
207 usec_t
triple_timestamp_by_clock(triple_timestamp
*ts
, clockid_t clock
) {
213 case CLOCK_REALTIME_ALARM
:
216 case CLOCK_MONOTONIC
:
217 return ts
->monotonic
;
220 case CLOCK_BOOTTIME_ALARM
:
224 return USEC_INFINITY
;
228 usec_t
timespec_load(const struct timespec
*ts
) {
231 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
232 return USEC_INFINITY
;
234 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
235 return USEC_INFINITY
;
238 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
239 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
242 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
245 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
246 return NSEC_INFINITY
;
248 if ((nsec_t
) ts
->tv_sec
>= (UINT64_MAX
- ts
->tv_nsec
) / NSEC_PER_SEC
)
249 return NSEC_INFINITY
;
251 return (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+ (nsec_t
) ts
->tv_nsec
;
254 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
257 if (u
== USEC_INFINITY
||
258 u
/ USEC_PER_SEC
>= TIME_T_MAX
) {
259 ts
->tv_sec
= (time_t) -1;
264 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
265 ts
->tv_nsec
= (long) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
270 struct timespec
*timespec_store_nsec(struct timespec
*ts
, nsec_t n
) {
273 if (n
== NSEC_INFINITY
||
274 n
/ NSEC_PER_SEC
>= TIME_T_MAX
) {
275 ts
->tv_sec
= (time_t) -1;
280 ts
->tv_sec
= (time_t) (n
/ NSEC_PER_SEC
);
281 ts
->tv_nsec
= (long) (n
% NSEC_PER_SEC
);
286 usec_t
timeval_load(const struct timeval
*tv
) {
289 if (tv
->tv_sec
< 0 || tv
->tv_usec
< 0)
290 return USEC_INFINITY
;
292 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
293 return USEC_INFINITY
;
296 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
297 (usec_t
) tv
->tv_usec
;
300 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
303 if (u
== USEC_INFINITY
||
304 u
/ USEC_PER_SEC
> TIME_T_MAX
) {
305 tv
->tv_sec
= (time_t) -1;
306 tv
->tv_usec
= (suseconds_t
) -1;
308 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
309 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
315 char *format_timestamp_style(
319 TimestampStyle style
) {
321 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that
322 * our generated timestamps may be parsed with parse_timestamp(), and always read the same. */
323 static const char * const weekdays
[] = {
340 assert(style
< _TIMESTAMP_STYLE_MAX
);
342 if (!timestamp_is_set(t
))
343 return NULL
; /* Timestamp is unset */
345 if (style
== TIMESTAMP_UNIX
) {
346 if (l
< (size_t) (1 + 1 + 1))
347 return NULL
; /* not enough space for even the shortest of forms */
349 return snprintf_ok(buf
, l
, "@" USEC_FMT
, t
/ USEC_PER_SEC
); /* round down μs → s */
352 utc
= IN_SET(style
, TIMESTAMP_UTC
, TIMESTAMP_US_UTC
, TIMESTAMP_DATE
);
353 us
= IN_SET(style
, TIMESTAMP_US
, TIMESTAMP_US_UTC
);
355 if (l
< (size_t) (3 + /* week day */
356 1 + 10 + /* space and date */
357 style
== TIMESTAMP_DATE
? 0 :
358 (1 + 8 + /* space and time */
359 (us
? 1 + 6 : 0) + /* "." and microsecond part */
360 1 + (utc
? 3 : 1)) + /* space and shortest possible zone */
362 return NULL
; /* Not enough space even for the shortest form. */
364 /* Let's not format times with years > 9999 */
365 if (t
> USEC_TIMESTAMP_FORMATTABLE_MAX
) {
366 static const char* const xxx
[_TIMESTAMP_STYLE_MAX
] = {
367 [TIMESTAMP_PRETTY
] = "--- XXXX-XX-XX XX:XX:XX",
368 [TIMESTAMP_US
] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX",
369 [TIMESTAMP_UTC
] = "--- XXXX-XX-XX XX:XX:XX UTC",
370 [TIMESTAMP_US_UTC
] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC",
371 [TIMESTAMP_DATE
] = "--- XXXX-XX-XX",
374 assert(l
>= strlen(xxx
[style
]) + 1);
375 return strcpy(buf
, xxx
[style
]);
378 sec
= (time_t) (t
/ USEC_PER_SEC
); /* Round down */
380 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
383 /* Start with the week day */
384 assert((size_t) tm
.tm_wday
< ELEMENTSOF(weekdays
));
385 memcpy(buf
, weekdays
[tm
.tm_wday
], 4);
387 if (style
== TIMESTAMP_DATE
) {
388 /* Special format string if only date should be shown. */
389 if (strftime(buf
+ 3, l
- 3, " %Y-%m-%d", &tm
) <= 0)
390 return NULL
; /* Doesn't fit */
395 /* Add the main components */
396 if (strftime(buf
+ 3, l
- 3, " %Y-%m-%d %H:%M:%S", &tm
) <= 0)
397 return NULL
; /* Doesn't fit */
399 /* Append the microseconds part, if that's requested */
403 return NULL
; /* Microseconds part doesn't fit. */
405 sprintf(buf
+ n
, ".%06"PRI_USEC
, t
% USEC_PER_SEC
);
408 /* Append the timezone */
411 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r()
412 * normally uses the obsolete "GMT" instead. */
414 return NULL
; /* "UTC" doesn't fit. */
416 strcpy(buf
+ n
, " UTC");
418 } else if (!isempty(tm
.tm_zone
)) {
421 /* An explicit timezone is specified, let's use it, if it fits */
422 tn
= strlen(tm
.tm_zone
);
423 if (n
+ 1 + tn
+ 1 > l
) {
424 /* The full time zone does not fit in. Yuck. */
426 if (n
+ 1 + _POSIX_TZNAME_MAX
+ 1 > l
)
427 return NULL
; /* Not even enough space for the POSIX minimum (of 6)? In that
428 * case, complain that it doesn't fit. */
430 /* So the time zone doesn't fit in fully, but the caller passed enough space for the
431 * POSIX minimum time zone length. In this case suppress the timezone entirely, in
432 * order not to dump an overly long, hard to read string on the user. This should be
433 * safe, because the user will assume the local timezone anyway if none is shown. And
434 * so does parse_timestamp(). */
437 strcpy(buf
+ n
, tm
.tm_zone
);
444 char* format_timestamp_relative_full(char *buf
, size_t l
, usec_t t
, clockid_t clock
, bool implicit_left
) {
450 if (!timestamp_is_set(t
))
459 s
= implicit_left
? "" : " left";
462 if (d
>= USEC_PER_YEAR
) {
463 usec_t years
= d
/ USEC_PER_YEAR
;
464 usec_t months
= (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
;
466 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s%s",
468 years
== 1 ? "year" : "years",
470 months
== 1 ? "month" : "months",
472 } else if (d
>= USEC_PER_MONTH
) {
473 usec_t months
= d
/ USEC_PER_MONTH
;
474 usec_t days
= (d
% USEC_PER_MONTH
) / USEC_PER_DAY
;
476 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s%s",
478 months
== 1 ? "month" : "months",
480 days
== 1 ? "day" : "days",
482 } else if (d
>= USEC_PER_WEEK
) {
483 usec_t weeks
= d
/ USEC_PER_WEEK
;
484 usec_t days
= (d
% USEC_PER_WEEK
) / USEC_PER_DAY
;
486 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s%s",
488 weeks
== 1 ? "week" : "weeks",
490 days
== 1 ? "day" : "days",
492 } else if (d
>= 2*USEC_PER_DAY
)
493 (void) snprintf(buf
, l
, USEC_FMT
" days%s", d
/ USEC_PER_DAY
,s
);
494 else if (d
>= 25*USEC_PER_HOUR
)
495 (void) snprintf(buf
, l
, "1 day " USEC_FMT
"h%s",
496 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
497 else if (d
>= 6*USEC_PER_HOUR
)
498 (void) snprintf(buf
, l
, USEC_FMT
"h%s",
499 d
/ USEC_PER_HOUR
, s
);
500 else if (d
>= USEC_PER_HOUR
)
501 (void) snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min%s",
503 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
504 else if (d
>= 5*USEC_PER_MINUTE
)
505 (void) snprintf(buf
, l
, USEC_FMT
"min%s",
506 d
/ USEC_PER_MINUTE
, s
);
507 else if (d
>= USEC_PER_MINUTE
)
508 (void) snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s%s",
510 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
511 else if (d
>= USEC_PER_SEC
)
512 (void) snprintf(buf
, l
, USEC_FMT
"s%s",
513 d
/ USEC_PER_SEC
, s
);
514 else if (d
>= USEC_PER_MSEC
)
515 (void) snprintf(buf
, l
, USEC_FMT
"ms%s",
516 d
/ USEC_PER_MSEC
, s
);
518 (void) snprintf(buf
, l
, USEC_FMT
"us%s",
521 (void) snprintf(buf
, l
, "now");
527 char* format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
528 static const struct {
532 { "y", USEC_PER_YEAR
},
533 { "month", USEC_PER_MONTH
},
534 { "w", USEC_PER_WEEK
},
535 { "d", USEC_PER_DAY
},
536 { "h", USEC_PER_HOUR
},
537 { "min", USEC_PER_MINUTE
},
538 { "s", USEC_PER_SEC
},
539 { "ms", USEC_PER_MSEC
},
543 char *p
= ASSERT_PTR(buf
);
544 bool something
= false;
548 if (t
== USEC_INFINITY
) {
549 strncpy(p
, "infinity", l
-1);
555 strncpy(p
, "0", l
-1);
560 /* The result of this function can be parsed with parse_sec */
562 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
571 if (t
< accuracy
&& something
)
574 if (t
< table
[i
].usec
)
580 a
= t
/ table
[i
].usec
;
581 b
= t
% table
[i
].usec
;
583 /* Let's see if we should shows this in dot notation */
584 if (t
< USEC_PER_MINUTE
&& b
> 0) {
587 for (usec_t cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
590 for (usec_t cc
= accuracy
; cc
> 1; cc
/= 10) {
597 "%s"USEC_FMT
".%0*"PRI_USEC
"%s",
609 /* No? Then let's show it normally */
620 n
= MIN((size_t) k
, l
-1);
633 static int parse_timestamp_impl(
641 static const struct {
661 _cleanup_free_
char *t_alloc
= NULL
;
662 usec_t usec
, plus
= 0, minus
= 0;
663 bool with_tz
= false;
665 unsigned fractional
= 0;
672 * 2012-09-22 16:34:22.1[2[3[4[5[6]]]]]
673 * 2012-09-22 16:34:22 (µsec will be set to 0)
674 * 2012-09-22 16:34 (seconds will be set to 0)
675 * 2012-09-22T16:34:22.1[2[3[4[5[6]]]]]
676 * 2012-09-22T16:34:22 (µsec will be set to 0)
677 * 2012-09-22T16:34 (seconds will be set to 0)
678 * 2012-09-22 (time will be set to 00:00:00)
679 * 16:34:22 (date will be set to today)
680 * 16:34 (date will be set to today, seconds to 0)
682 * yesterday (time is set to 00:00:00)
683 * today (time is set to 00:00:00)
684 * tomorrow (time is set to 00:00:00)
687 * @2147483647 (seconds since epoch)
689 * Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted.
690 * E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT".
692 * A simplified strptime-spelled RFC3339 ABNF looks like
693 * "%Y-%m-%d" "T" "%H" ":" "%M" ":" "%S" [".%N"] ("Z" / (("+" / "-") "%H:%M"))
694 * We additionally allow no seconds and inherited timezone
695 * for symmetry with our other syntaxes and improved interactive usability:
696 * "%Y-%m-%d" "T" "%H" ":" "%M" ":" ["%S" [".%N"]] ["Z" / (("+" / "-") "%H:%M")]
697 * RFC3339 defines time-secfrac to as "." 1*DIGIT, but we limit to 6 digits,
698 * since we're limited to 1µs resolution.
699 * We also accept "Sat 2012-09-22T16:34:22", RFC3339 warns against it.
704 if (max_len
!= SIZE_MAX
) {
705 /* If the input string contains timezone, then cut it here. */
707 if (max_len
== 0) /* Can't be the only field */
710 t_alloc
= strndup(t
, max_len
);
719 /* glibc accepts gmtoff more than 24 hours, but we refuse it. */
720 if ((usec_t
) labs(gmtoff
) * USEC_PER_SEC
> USEC_PER_DAY
)
727 if (t
[0] == '@' && !with_tz
)
728 return parse_sec(t
+ 1, ret
);
730 usec
= now(CLOCK_REALTIME
);
737 r
= parse_sec(t
+1, &plus
);
745 r
= parse_sec(t
+1, &minus
);
752 if ((k
= endswith(t
, " ago"))) {
753 _cleanup_free_
char *buf
= NULL
;
755 buf
= strndup(t
, k
- t
);
759 r
= parse_sec(buf
, &minus
);
766 if ((k
= endswith(t
, " left"))) {
767 _cleanup_free_
char *buf
= NULL
;
769 buf
= strndup(t
, k
- t
);
773 r
= parse_sec(buf
, &plus
);
781 sec
= (time_t) (usec
/ USEC_PER_SEC
);
783 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
788 if (streq(t
, "today")) {
789 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
792 } else if (streq(t
, "yesterday")) {
794 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
797 } else if (streq(t
, "tomorrow")) {
799 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
803 for (size_t i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
804 k
= startswith_no_case(t
, day_nr
[i
].name
);
808 weekday
= day_nr
[i
].nr
;
814 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
822 /* Our "canonical" RFC3339 syntax variant */
824 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
834 k
= strptime(t
, "%Y-%m-%dT%H:%M:%S", &tm
);
842 /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
844 k
= strptime(t
, "%b %d %H:%M:%S", &tm
);
853 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
859 /* Our "canonical" RFC3339 syntax variant without seconds */
861 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
867 /* RFC3339 syntax without seconds */
869 k
= strptime(t
, "%Y-%m-%dT%H:%M", &tm
);
876 k
= strptime(t
, "%y-%m-%d", &tm
);
878 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
883 k
= strptime(t
, "%Y-%m-%d", &tm
);
885 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
890 k
= strptime(t
, "%H:%M:%S", &tm
);
899 k
= strptime(t
, "%H:%M", &tm
);
909 r
= parse_fractional_part_u(&k
, 6, &fractional
);
919 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
923 plus
= -gmtoff
* USEC_PER_SEC
;
925 /* If gmtoff is negative, the string may be too old to be parsed as UTC.
926 * E.g. 1969-12-31 23:00:00 -06 == 1970-01-01 05:00:00 UTC
927 * We assumed that gmtoff is in the range of -24:00…+24:00, hence the only date we need to
928 * handle here is 1969-12-31. So, let's shift the date with one day, then subtract the shift
930 if (tm
.tm_year
== 69 && tm
.tm_mon
== 11 && tm
.tm_mday
== 31) {
931 /* Thu 1970-01-01-00:00:00 */
937 minus
= USEC_PER_DAY
;
940 minus
= gmtoff
* USEC_PER_SEC
;
942 sec
= mktime_or_timegm(&tm
, utc
);
946 usec
= usec_add(sec
* USEC_PER_SEC
, fractional
);
949 usec
= usec_add(usec
, plus
);
954 usec
= usec_sub_unsigned(usec
, minus
);
956 if (usec
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
964 static int parse_timestamp_maybe_with_tz(const char *t
, size_t tz_offset
, bool valid_tz
, usec_t
*ret
) {
969 for (int j
= 0; j
<= 1; j
++) {
970 if (isempty(tzname
[j
]))
973 if (!streq(t
+ tz_offset
, tzname
[j
]))
976 /* The specified timezone matches tzname[] of the local timezone. */
977 return parse_timestamp_impl(t
, tz_offset
- 1, /* utc = */ false, /* isdst = */ j
, /* gmtoff = */ 0, ret
);
980 /* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone
981 * and parse the remaining string as a local time. If we know that the last word is not a timezone,
982 * then assume that it is a part of the time and try to parse the whole string as a local time. */
983 return parse_timestamp_impl(t
, valid_tz
? tz_offset
- 1 : SIZE_MAX
,
984 /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret
);
987 typedef struct ParseTimestampResult
{
990 } ParseTimestampResult
;
992 int parse_timestamp(const char *t
, usec_t
*ret
) {
993 ParseTimestampResult
*shared
, tmp
;
994 const char *k
, *tz
, *current_tz
;
995 size_t max_len
, t_len
;
1002 if (t_len
> 2 && t
[t_len
- 1] == 'Z' && t
[t_len
- 2] != ' ') /* RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */
1003 return parse_timestamp_impl(t
, t_len
- 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret
);
1005 if (t_len
> 7 && IN_SET(t
[t_len
- 6], '+', '-') && t
[t_len
- 7] != ' ') { /* RFC3339-style welded offset: "1990-12-31T15:59:60-08:00" */
1006 k
= strptime(&t
[t_len
- 6], "%z", &tm
);
1007 if (k
&& *k
== '\0')
1008 return parse_timestamp_impl(t
, t_len
- 6, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm
.tm_gmtoff
, ret
);
1011 tz
= strrchr(t
, ' ');
1013 return parse_timestamp_impl(t
, /* max_len = */ SIZE_MAX
, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret
);
1018 /* Shortcut, parse the string as UTC. */
1019 if (streq(tz
, "UTC"))
1020 return parse_timestamp_impl(t
, max_len
, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret
);
1022 /* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
1023 * UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as
1024 * tzname[] may be in the same format. */
1025 k
= strptime(tz
, "%z", &tm
);
1026 if (k
&& *k
== '\0')
1027 return parse_timestamp_impl(t
, max_len
, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm
.tm_gmtoff
, ret
);
1029 /* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
1030 * tzname[] of the local timezone, e.g. JST or CEST. */
1031 if (!timezone_is_valid(tz
, LOG_DEBUG
))
1032 return parse_timestamp_maybe_with_tz(t
, tz
- t
, /* valid_tz = */ false, ret
);
1034 /* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
1036 current_tz
= getenv("TZ");
1037 if (current_tz
&& *current_tz
== ':' && streq(current_tz
+ 1, tz
))
1038 return parse_timestamp_maybe_with_tz(t
, tz
- t
, /* valid_tz = */ true, ret
);
1040 /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
1041 * the specified timezone in the child process. */
1043 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
1044 if (shared
== MAP_FAILED
)
1045 return negative_errno();
1047 r
= safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGKILL
|FORK_WAIT
, NULL
);
1049 (void) munmap(shared
, sizeof *shared
);
1053 const char *colon_tz
;
1055 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
1056 colon_tz
= strjoina(":", tz
);
1058 if (setenv("TZ", colon_tz
, 1) != 0) {
1059 shared
->return_value
= negative_errno();
1060 _exit(EXIT_FAILURE
);
1063 shared
->return_value
= parse_timestamp_maybe_with_tz(t
, tz
- t
, /* valid_tz = */ true, &shared
->usec
);
1065 _exit(EXIT_SUCCESS
);
1069 if (munmap(shared
, sizeof *shared
) != 0)
1070 return negative_errno();
1072 if (tmp
.return_value
== 0 && ret
)
1075 return tmp
.return_value
;
1078 static const char* extract_multiplier(const char *p
, usec_t
*ret
) {
1079 static const struct {
1083 { "seconds", USEC_PER_SEC
},
1084 { "second", USEC_PER_SEC
},
1085 { "sec", USEC_PER_SEC
},
1086 { "s", USEC_PER_SEC
},
1087 { "minutes", USEC_PER_MINUTE
},
1088 { "minute", USEC_PER_MINUTE
},
1089 { "min", USEC_PER_MINUTE
},
1090 { "months", USEC_PER_MONTH
},
1091 { "month", USEC_PER_MONTH
},
1092 { "M", USEC_PER_MONTH
},
1093 { "msec", USEC_PER_MSEC
},
1094 { "ms", USEC_PER_MSEC
},
1095 { "m", USEC_PER_MINUTE
},
1096 { "hours", USEC_PER_HOUR
},
1097 { "hour", USEC_PER_HOUR
},
1098 { "hr", USEC_PER_HOUR
},
1099 { "h", USEC_PER_HOUR
},
1100 { "days", USEC_PER_DAY
},
1101 { "day", USEC_PER_DAY
},
1102 { "d", USEC_PER_DAY
},
1103 { "weeks", USEC_PER_WEEK
},
1104 { "week", USEC_PER_WEEK
},
1105 { "w", USEC_PER_WEEK
},
1106 { "years", USEC_PER_YEAR
},
1107 { "year", USEC_PER_YEAR
},
1108 { "y", USEC_PER_YEAR
},
1111 { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */
1112 { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */
1118 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
1121 e
= startswith(p
, table
[i
].suffix
);
1123 *ret
= table
[i
].usec
;
1131 int parse_time(const char *t
, usec_t
*ret
, usec_t default_unit
) {
1134 bool something
= false;
1137 assert(default_unit
> 0);
1141 p
+= strspn(p
, WHITESPACE
);
1142 s
= startswith(p
, "infinity");
1144 s
+= strspn(s
, WHITESPACE
);
1149 *ret
= USEC_INFINITY
;
1154 usec_t multiplier
= default_unit
, k
;
1158 p
+= strspn(p
, WHITESPACE
);
1167 if (*p
== '-') /* Don't allow "-0" */
1171 l
= strtoll(p
, &e
, 10);
1179 p
+= strspn(p
, DIGITS
);
1185 s
= extract_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1186 if (s
== p
&& *s
!= '\0')
1187 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1192 if ((usec_t
) l
>= USEC_INFINITY
/ multiplier
)
1195 k
= (usec_t
) l
* multiplier
;
1196 if (k
>= USEC_INFINITY
- usec
)
1204 usec_t m
= multiplier
/ 10;
1207 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1208 k
= (usec_t
) (*b
- '0') * m
;
1209 if (k
>= USEC_INFINITY
- usec
)
1215 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1226 int parse_sec(const char *t
, usec_t
*ret
) {
1227 return parse_time(t
, ret
, USEC_PER_SEC
);
1230 int parse_sec_fix_0(const char *t
, usec_t
*ret
) {
1237 r
= parse_sec(t
, &k
);
1241 *ret
= k
== 0 ? USEC_INFINITY
: k
;
1245 int parse_sec_def_infinity(const char *t
, usec_t
*ret
) {
1249 t
+= strspn(t
, WHITESPACE
);
1251 *ret
= USEC_INFINITY
;
1254 return parse_sec(t
, ret
);
1257 static const char* extract_nsec_multiplier(const char *p
, nsec_t
*ret
) {
1258 static const struct {
1262 { "seconds", NSEC_PER_SEC
},
1263 { "second", NSEC_PER_SEC
},
1264 { "sec", NSEC_PER_SEC
},
1265 { "s", NSEC_PER_SEC
},
1266 { "minutes", NSEC_PER_MINUTE
},
1267 { "minute", NSEC_PER_MINUTE
},
1268 { "min", NSEC_PER_MINUTE
},
1269 { "months", NSEC_PER_MONTH
},
1270 { "month", NSEC_PER_MONTH
},
1271 { "M", NSEC_PER_MONTH
},
1272 { "msec", NSEC_PER_MSEC
},
1273 { "ms", NSEC_PER_MSEC
},
1274 { "m", NSEC_PER_MINUTE
},
1275 { "hours", NSEC_PER_HOUR
},
1276 { "hour", NSEC_PER_HOUR
},
1277 { "hr", NSEC_PER_HOUR
},
1278 { "h", NSEC_PER_HOUR
},
1279 { "days", NSEC_PER_DAY
},
1280 { "day", NSEC_PER_DAY
},
1281 { "d", NSEC_PER_DAY
},
1282 { "weeks", NSEC_PER_WEEK
},
1283 { "week", NSEC_PER_WEEK
},
1284 { "w", NSEC_PER_WEEK
},
1285 { "years", NSEC_PER_YEAR
},
1286 { "year", NSEC_PER_YEAR
},
1287 { "y", NSEC_PER_YEAR
},
1288 { "usec", NSEC_PER_USEC
},
1289 { "us", NSEC_PER_USEC
},
1290 { "μs", NSEC_PER_USEC
}, /* U+03bc (aka GREEK LETTER MU) */
1291 { "µs", NSEC_PER_USEC
}, /* U+b5 (aka MICRO SIGN) */
1294 { "", 1ULL }, /* default is nsec */
1301 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
1304 e
= startswith(p
, table
[i
].suffix
);
1306 *ret
= table
[i
].nsec
;
1314 int parse_nsec(const char *t
, nsec_t
*ret
) {
1317 bool something
= false;
1324 p
+= strspn(p
, WHITESPACE
);
1325 s
= startswith(p
, "infinity");
1327 s
+= strspn(s
, WHITESPACE
);
1331 *ret
= NSEC_INFINITY
;
1336 nsec_t multiplier
= 1, k
;
1340 p
+= strspn(p
, WHITESPACE
);
1349 if (*p
== '-') /* Don't allow "-0" */
1353 l
= strtoll(p
, &e
, 10);
1361 p
+= strspn(p
, DIGITS
);
1367 s
= extract_nsec_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1368 if (s
== p
&& *s
!= '\0')
1369 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1374 if ((nsec_t
) l
>= NSEC_INFINITY
/ multiplier
)
1377 k
= (nsec_t
) l
* multiplier
;
1378 if (k
>= NSEC_INFINITY
- nsec
)
1386 nsec_t m
= multiplier
/ 10;
1389 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1390 k
= (nsec_t
) (*b
- '0') * m
;
1391 if (k
>= NSEC_INFINITY
- nsec
)
1397 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1408 static int get_timezones_from_zone1970_tab(char ***ret
) {
1409 _cleanup_fclose_
FILE *f
= NULL
;
1410 _cleanup_strv_free_
char **zones
= NULL
;
1415 f
= fopen("/usr/share/zoneinfo/zone1970.tab", "re");
1420 _cleanup_free_
char *line
= NULL
, *cc
= NULL
, *co
= NULL
, *tz
= NULL
;
1422 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1428 const char *p
= line
;
1431 * 'country codes' 'coordinates' 'timezone' 'comments' */
1432 r
= extract_many_words(&p
, NULL
, 0, &cc
, &co
, &tz
, NULL
);
1436 /* Lines that start with # are comments. */
1440 r
= strv_extend(&zones
, tz
);
1445 *ret
= TAKE_PTR(zones
);
1449 static int get_timezones_from_tzdata_zi(char ***ret
) {
1450 _cleanup_fclose_
FILE *f
= NULL
;
1451 _cleanup_strv_free_
char **zones
= NULL
;
1456 f
= fopen("/usr/share/zoneinfo/tzdata.zi", "re");
1461 _cleanup_free_
char *line
= NULL
, *type
= NULL
, *f1
= NULL
, *f2
= NULL
;
1463 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1469 const char *p
= line
;
1471 /* The only lines we care about are Zone and Link lines.
1472 * Zone line format is:
1473 * 'Zone' 'timezone' ...
1474 * Link line format is:
1475 * 'Link' 'target' 'alias'
1476 * See 'man zic' for more detail. */
1477 r
= extract_many_words(&p
, NULL
, 0, &type
, &f1
, &f2
, NULL
);
1482 if (IN_SET(*type
, 'Z', 'z'))
1483 /* Zone lines have timezone in field 1. */
1485 else if (IN_SET(*type
, 'L', 'l'))
1486 /* Link lines have timezone in field 2. */
1489 /* Not a line we care about. */
1492 r
= strv_extend(&zones
, tz
);
1497 *ret
= TAKE_PTR(zones
);
1501 int get_timezones(char ***ret
) {
1502 _cleanup_strv_free_
char **zones
= NULL
;
1507 r
= get_timezones_from_tzdata_zi(&zones
);
1509 log_debug_errno(r
, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
1510 r
= get_timezones_from_zone1970_tab(&zones
);
1512 log_debug_errno(r
, "Could not get timezone data from zone1970.tab, using UTC: %m");
1514 if (r
< 0 && r
!= -ENOENT
)
1517 /* Always include UTC */
1518 r
= strv_extend(&zones
, "UTC");
1525 *ret
= TAKE_PTR(zones
);
1529 int verify_timezone(const char *name
, int log_level
) {
1532 _cleanup_close_
int fd
= -EBADF
;
1539 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1540 if (streq(name
, "UTC"))
1546 for (p
= name
; *p
; p
++) {
1547 if (!ascii_isdigit(*p
) &&
1548 !ascii_isalpha(*p
) &&
1549 !IN_SET(*p
, '-', '_', '+', '/'))
1565 if (p
- name
>= PATH_MAX
)
1566 return -ENAMETOOLONG
;
1568 t
= strjoina("/usr/share/zoneinfo/", name
);
1570 fd
= open(t
, O_RDONLY
|O_CLOEXEC
);
1572 return log_full_errno(log_level
, errno
, "Failed to open timezone file '%s': %m", t
);
1574 r
= fd_verify_regular(fd
);
1576 return log_full_errno(log_level
, r
, "Timezone file '%s' is not a regular file: %m", t
);
1578 r
= loop_read_exact(fd
, buf
, 4, false);
1580 return log_full_errno(log_level
, r
, "Failed to read from timezone file '%s': %m", t
);
1582 /* Magic from tzfile(5) */
1583 if (memcmp(buf
, "TZif", 4) != 0)
1584 return log_full_errno(log_level
, SYNTHETIC_ERRNO(EBADMSG
),
1585 "Timezone file '%s' has wrong magic bytes", t
);
1590 bool clock_supported(clockid_t clock
) {
1595 case CLOCK_MONOTONIC
:
1596 case CLOCK_REALTIME
:
1597 case CLOCK_BOOTTIME
:
1598 /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */
1602 /* For everything else, check properly */
1603 return clock_gettime(clock
, &ts
) >= 0;
1607 int get_timezone(char **ret
) {
1608 _cleanup_free_
char *t
= NULL
;
1615 r
= readlink_malloc("/etc/localtime", &t
);
1617 /* If the symlink does not exist, assume "UTC", like glibc does */
1626 return r
; /* returns EINVAL if not a symlink */
1628 e
= PATH_STARTSWITH_SET(t
, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
1632 if (!timezone_is_valid(e
, LOG_DEBUG
))
1643 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1646 return utc
? timegm(tm
) : mktime(tm
);
1649 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1653 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1656 static uint32_t sysconf_clock_ticks_cached(void) {
1657 static thread_local
uint32_t hz
= 0;
1661 r
= sysconf(_SC_CLK_TCK
);
1670 uint32_t usec_to_jiffies(usec_t u
) {
1671 uint32_t hz
= sysconf_clock_ticks_cached();
1672 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);
1675 usec_t
jiffies_to_usec(uint32_t j
) {
1676 uint32_t hz
= sysconf_clock_ticks_cached();
1677 return DIV_ROUND_UP(j
* USEC_PER_SEC
, hz
);
1680 usec_t
usec_shift_clock(usec_t x
, clockid_t from
, clockid_t to
) {
1683 if (x
== USEC_INFINITY
)
1684 return USEC_INFINITY
;
1685 if (map_clock_id(from
) == map_clock_id(to
))
1692 /* x lies in the future */
1693 return usec_add(b
, usec_sub_unsigned(x
, a
));
1695 /* x lies in the past */
1696 return usec_sub_unsigned(b
, usec_sub_unsigned(a
, x
));
1699 bool in_utc_timezone(void) {
1702 return timezone
== 0 && daylight
== 0;
1705 int time_change_fd(void) {
1707 /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1708 static const struct itimerspec its
= {
1709 .it_value
.tv_sec
= TIME_T_MAX
,
1712 _cleanup_close_
int fd
= -EBADF
;
1714 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX
));
1716 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1717 * CLOCK_MONOTONIC. */
1719 fd
= timerfd_create(CLOCK_REALTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1723 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its
, NULL
) >= 0)
1726 /* So apparently there are systems where time_t is 64-bit, but the kernel actually doesn't support
1727 * 64-bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
1728 * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
1729 * of a black magic thing though, but what can we do?
1731 * We don't want this code on x86-64, hence let's conditionalize this for systems with 64-bit time_t
1732 * but where "long" is shorter than 64-bit, i.e. 32-bit archs.
1734 * See: https://github.com/systemd/systemd/issues/14362 */
1736 #if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
1737 if (ERRNO_IS_NOT_SUPPORTED(errno
) || errno
== EOVERFLOW
) {
1738 static const struct itimerspec its32
= {
1739 .it_value
.tv_sec
= INT32_MAX
,
1742 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its32
, NULL
) >= 0)
1750 static const char* const timestamp_style_table
[_TIMESTAMP_STYLE_MAX
] = {
1751 [TIMESTAMP_PRETTY
] = "pretty",
1752 [TIMESTAMP_US
] = "us",
1753 [TIMESTAMP_UTC
] = "utc",
1754 [TIMESTAMP_US_UTC
] = "us+utc",
1755 [TIMESTAMP_UNIX
] = "unix",
1758 /* Use the macro for enum → string to allow for aliases */
1759 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style
, TimestampStyle
);
1761 /* For the string → enum mapping we use the generic implementation, but also support two aliases */
1762 TimestampStyle
timestamp_style_from_string(const char *s
) {
1765 t
= (TimestampStyle
) string_table_lookup(timestamp_style_table
, ELEMENTSOF(timestamp_style_table
), s
);
1768 if (STRPTR_IN_SET(s
, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
1769 return TIMESTAMP_US
;
1770 if (STRPTR_IN_SET(s
, "µs+utc", "μs+utc"))
1771 return TIMESTAMP_US_UTC
;