]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/time-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/timerfd.h>
24 #include <sys/timex.h>
29 #include "path-util.h"
30 #include "string-util.h"
32 #include "time-util.h"
35 usec_t
now(clockid_t clock_id
) {
38 assert_se(clock_gettime(clock_id
, &ts
) == 0);
40 return timespec_load(&ts
);
43 nsec_t
now_nsec(clockid_t clock_id
) {
46 assert_se(clock_gettime(clock_id
, &ts
) == 0);
48 return timespec_load_nsec(&ts
);
51 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
54 ts
->realtime
= now(CLOCK_REALTIME
);
55 ts
->monotonic
= now(CLOCK_MONOTONIC
);
60 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
64 if (u
== USEC_INFINITY
|| u
<= 0) {
65 ts
->realtime
= ts
->monotonic
= u
;
71 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
72 ts
->monotonic
= now(CLOCK_MONOTONIC
);
74 if ((int64_t) ts
->monotonic
> delta
)
75 ts
->monotonic
-= delta
;
82 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
86 if (u
== USEC_INFINITY
) {
87 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
92 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
94 ts
->realtime
= now(CLOCK_REALTIME
);
95 if ((int64_t) ts
->realtime
> delta
)
96 ts
->realtime
-= delta
;
103 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
106 if (u
== USEC_INFINITY
) {
107 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
110 ts
->realtime
= now(CLOCK_REALTIME
);
111 ts
->monotonic
= now(CLOCK_MONOTONIC
);
113 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
115 if ((int64_t) ts
->realtime
> delta
)
116 ts
->realtime
-= delta
;
120 if ((int64_t) ts
->monotonic
> delta
)
121 ts
->monotonic
-= delta
;
129 usec_t
timespec_load(const struct timespec
*ts
) {
132 if (ts
->tv_sec
== (time_t) -1 &&
133 ts
->tv_nsec
== (long) -1)
134 return USEC_INFINITY
;
136 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
137 return USEC_INFINITY
;
140 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
141 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
144 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
147 if (ts
->tv_sec
== (time_t) -1 &&
148 ts
->tv_nsec
== (long) -1)
149 return NSEC_INFINITY
;
152 (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+
153 (nsec_t
) ts
->tv_nsec
;
156 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
159 if (u
== USEC_INFINITY
) {
160 ts
->tv_sec
= (time_t) -1;
161 ts
->tv_nsec
= (long) -1;
165 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
166 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
171 usec_t
timeval_load(const struct timeval
*tv
) {
174 if (tv
->tv_sec
== (time_t) -1 &&
175 tv
->tv_usec
== (suseconds_t
) -1)
176 return USEC_INFINITY
;
178 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
179 return USEC_INFINITY
;
182 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
183 (usec_t
) tv
->tv_usec
;
186 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
189 if (u
== USEC_INFINITY
) {
190 tv
->tv_sec
= (time_t) -1;
191 tv
->tv_usec
= (suseconds_t
) -1;
193 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
194 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
200 static char *format_timestamp_internal(char *buf
, size_t l
, usec_t t
, bool utc
) {
207 if (t
<= 0 || t
== USEC_INFINITY
)
210 sec
= (time_t) (t
/ USEC_PER_SEC
);
211 localtime_or_gmtime_r(&sec
, &tm
, utc
);
213 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", &tm
) <= 0)
219 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
220 return format_timestamp_internal(buf
, l
, t
, false);
223 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
224 return format_timestamp_internal(buf
, l
, t
, true);
227 static char *format_timestamp_internal_us(char *buf
, size_t l
, usec_t t
, bool utc
) {
234 if (t
<= 0 || t
== USEC_INFINITY
)
237 sec
= (time_t) (t
/ USEC_PER_SEC
);
238 localtime_or_gmtime_r(&sec
, &tm
, utc
);
240 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
242 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
243 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
249 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
250 return format_timestamp_internal_us(buf
, l
, t
, false);
253 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
254 return format_timestamp_internal_us(buf
, l
, t
, true);
257 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
261 if (t
<= 0 || t
== USEC_INFINITY
)
264 n
= now(CLOCK_REALTIME
);
273 if (d
>= USEC_PER_YEAR
)
274 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
276 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
277 else if (d
>= USEC_PER_MONTH
)
278 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
280 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
281 else if (d
>= USEC_PER_WEEK
)
282 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
284 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
285 else if (d
>= 2*USEC_PER_DAY
)
286 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
287 else if (d
>= 25*USEC_PER_HOUR
)
288 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
289 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
290 else if (d
>= 6*USEC_PER_HOUR
)
291 snprintf(buf
, l
, USEC_FMT
"h %s",
292 d
/ USEC_PER_HOUR
, s
);
293 else if (d
>= USEC_PER_HOUR
)
294 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
296 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
297 else if (d
>= 5*USEC_PER_MINUTE
)
298 snprintf(buf
, l
, USEC_FMT
"min %s",
299 d
/ USEC_PER_MINUTE
, s
);
300 else if (d
>= USEC_PER_MINUTE
)
301 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
303 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
304 else if (d
>= USEC_PER_SEC
)
305 snprintf(buf
, l
, USEC_FMT
"s %s",
306 d
/ USEC_PER_SEC
, s
);
307 else if (d
>= USEC_PER_MSEC
)
308 snprintf(buf
, l
, USEC_FMT
"ms %s",
309 d
/ USEC_PER_MSEC
, s
);
311 snprintf(buf
, l
, USEC_FMT
"us %s",
314 snprintf(buf
, l
, "now");
320 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
321 static const struct {
325 { "y", USEC_PER_YEAR
},
326 { "month", USEC_PER_MONTH
},
327 { "w", USEC_PER_WEEK
},
328 { "d", USEC_PER_DAY
},
329 { "h", USEC_PER_HOUR
},
330 { "min", USEC_PER_MINUTE
},
331 { "s", USEC_PER_SEC
},
332 { "ms", USEC_PER_MSEC
},
338 bool something
= false;
343 if (t
== USEC_INFINITY
) {
344 strncpy(p
, "infinity", l
-1);
350 strncpy(p
, "0", l
-1);
355 /* The result of this function can be parsed with parse_sec */
357 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
366 if (t
< accuracy
&& something
)
369 if (t
< table
[i
].usec
)
375 a
= t
/ table
[i
].usec
;
376 b
= t
% table
[i
].usec
;
378 /* Let's see if we should shows this in dot notation */
379 if (t
< USEC_PER_MINUTE
&& b
> 0) {
384 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
387 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
394 "%s"USEC_FMT
".%0*llu%s",
398 (unsigned long long) b
,
406 /* No? Then let's show it normally */
417 n
= MIN((size_t) k
, l
);
430 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
436 if (!dual_timestamp_is_set(t
))
439 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
445 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
446 unsigned long long a
, b
;
451 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
452 log_debug("Failed to parse finish timestamp value %s.", value
);
462 int parse_timestamp(const char *t
, usec_t
*usec
) {
463 static const struct {
487 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
494 * 2012-09-22 16:34:22
495 * 2012-09-22 16:34 (seconds will be set to 0)
496 * 2012-09-22 (time will be set to 00:00:00)
497 * 16:34:22 (date will be set to today)
498 * 16:34 (date will be set to today, seconds to 0)
500 * yesterday (time is set to 00:00:00)
501 * today (time is set to 00:00:00)
502 * tomorrow (time is set to 00:00:00)
505 * @2147483647 (seconds since epoch)
513 return parse_sec(t
+ 1, usec
);
515 ret
= now(CLOCK_REALTIME
);
520 else if (t
[0] == '+') {
521 r
= parse_sec(t
+1, &plus
);
527 } else if (t
[0] == '-') {
528 r
= parse_sec(t
+1, &minus
);
534 } else if ((k
= endswith(t
, " ago"))) {
535 t
= strndupa(t
, k
- t
);
537 r
= parse_sec(t
, &minus
);
543 } else if ((k
= endswith(t
, " left"))) {
544 t
= strndupa(t
, k
- t
);
546 r
= parse_sec(t
, &plus
);
553 utc
= endswith_no_case(t
, " UTC");
555 t
= strndupa(t
, utc
- t
);
557 x
= ret
/ USEC_PER_SEC
;
560 assert_se(localtime_or_gmtime_r(&x
, &tm
, utc
));
563 if (streq(t
, "today")) {
564 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
567 } else if (streq(t
, "yesterday")) {
569 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
572 } else if (streq(t
, "tomorrow")) {
574 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
579 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
582 if (!startswith_no_case(t
, day_nr
[i
].name
))
585 skip
= strlen(day_nr
[i
].name
);
589 weekday
= day_nr
[i
].nr
;
595 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
604 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
613 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
620 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
627 k
= strptime(t
, "%y-%m-%d", &tm
);
629 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
634 k
= strptime(t
, "%Y-%m-%d", &tm
);
636 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
641 k
= strptime(t
, "%H:%M:%S", &tm
);
650 k
= strptime(t
, "%H:%M", &tm
);
661 unsigned long long val
;
665 if (*k
< '0' || *k
> '9')
668 /* base 10 instead of base 0, .09 is not base 8 */
670 val
= strtoull(k
, &end
, 10);
676 /* val has l digits, make them 6 */
686 x
= mktime_or_timegm(&tm
, utc
);
687 if (x
== (time_t) -1)
690 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
693 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
707 int parse_sec(const char *t
, usec_t
*usec
) {
708 static const struct {
712 { "seconds", USEC_PER_SEC
},
713 { "second", USEC_PER_SEC
},
714 { "sec", USEC_PER_SEC
},
715 { "s", USEC_PER_SEC
},
716 { "minutes", USEC_PER_MINUTE
},
717 { "minute", USEC_PER_MINUTE
},
718 { "min", USEC_PER_MINUTE
},
719 { "months", USEC_PER_MONTH
},
720 { "month", USEC_PER_MONTH
},
721 { "msec", USEC_PER_MSEC
},
722 { "ms", USEC_PER_MSEC
},
723 { "m", USEC_PER_MINUTE
},
724 { "hours", USEC_PER_HOUR
},
725 { "hour", USEC_PER_HOUR
},
726 { "hr", USEC_PER_HOUR
},
727 { "h", USEC_PER_HOUR
},
728 { "days", USEC_PER_DAY
},
729 { "day", USEC_PER_DAY
},
730 { "d", USEC_PER_DAY
},
731 { "weeks", USEC_PER_WEEK
},
732 { "week", USEC_PER_WEEK
},
733 { "w", USEC_PER_WEEK
},
734 { "years", USEC_PER_YEAR
},
735 { "year", USEC_PER_YEAR
},
736 { "y", USEC_PER_YEAR
},
739 { "", USEC_PER_SEC
}, /* default is sec */
744 bool something
= false;
751 p
+= strspn(p
, WHITESPACE
);
752 s
= startswith(p
, "infinity");
754 s
+= strspn(s
, WHITESPACE
);
758 *usec
= USEC_INFINITY
;
767 p
+= strspn(p
, WHITESPACE
);
777 l
= strtoll(p
, &e
, 10);
789 z
= strtoll(b
, &e
, 10);
804 e
+= strspn(e
, WHITESPACE
);
806 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
807 if (startswith(e
, table
[i
].suffix
)) {
808 usec_t k
= (usec_t
) z
* table
[i
].usec
;
813 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
814 p
= e
+ strlen(table
[i
].suffix
);
820 if (i
>= ELEMENTSOF(table
))
830 int parse_nsec(const char *t
, nsec_t
*nsec
) {
831 static const struct {
835 { "seconds", NSEC_PER_SEC
},
836 { "second", NSEC_PER_SEC
},
837 { "sec", NSEC_PER_SEC
},
838 { "s", NSEC_PER_SEC
},
839 { "minutes", NSEC_PER_MINUTE
},
840 { "minute", NSEC_PER_MINUTE
},
841 { "min", NSEC_PER_MINUTE
},
842 { "months", NSEC_PER_MONTH
},
843 { "month", NSEC_PER_MONTH
},
844 { "msec", NSEC_PER_MSEC
},
845 { "ms", NSEC_PER_MSEC
},
846 { "m", NSEC_PER_MINUTE
},
847 { "hours", NSEC_PER_HOUR
},
848 { "hour", NSEC_PER_HOUR
},
849 { "hr", NSEC_PER_HOUR
},
850 { "h", NSEC_PER_HOUR
},
851 { "days", NSEC_PER_DAY
},
852 { "day", NSEC_PER_DAY
},
853 { "d", NSEC_PER_DAY
},
854 { "weeks", NSEC_PER_WEEK
},
855 { "week", NSEC_PER_WEEK
},
856 { "w", NSEC_PER_WEEK
},
857 { "years", NSEC_PER_YEAR
},
858 { "year", NSEC_PER_YEAR
},
859 { "y", NSEC_PER_YEAR
},
860 { "usec", NSEC_PER_USEC
},
861 { "us", NSEC_PER_USEC
},
864 { "", 1ULL }, /* default is nsec */
869 bool something
= false;
876 p
+= strspn(p
, WHITESPACE
);
877 s
= startswith(p
, "infinity");
879 s
+= strspn(s
, WHITESPACE
);
883 *nsec
= NSEC_INFINITY
;
892 p
+= strspn(p
, WHITESPACE
);
902 l
= strtoll(p
, &e
, 10);
914 z
= strtoll(b
, &e
, 10);
929 e
+= strspn(e
, WHITESPACE
);
931 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
932 if (startswith(e
, table
[i
].suffix
)) {
933 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
938 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
939 p
= e
+ strlen(table
[i
].suffix
);
945 if (i
>= ELEMENTSOF(table
))
955 bool ntp_synced(void) {
956 struct timex txc
= {};
958 if (adjtimex(&txc
) < 0)
961 if (txc
.status
& STA_UNSYNC
)
967 int get_timezones(char ***ret
) {
968 _cleanup_fclose_
FILE *f
= NULL
;
969 _cleanup_strv_free_
char **zones
= NULL
;
970 size_t n_zones
= 0, n_allocated
= 0;
974 zones
= strv_new("UTC", NULL
);
981 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
985 FOREACH_LINE(l
, f
, return -errno
) {
991 if (isempty(p
) || *p
== '#')
994 /* Skip over country code */
995 p
+= strcspn(p
, WHITESPACE
);
996 p
+= strspn(p
, WHITESPACE
);
998 /* Skip over coordinates */
999 p
+= strcspn(p
, WHITESPACE
);
1000 p
+= strspn(p
, WHITESPACE
);
1002 /* Found timezone name */
1003 k
= strcspn(p
, WHITESPACE
);
1011 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1016 zones
[n_zones
++] = w
;
1017 zones
[n_zones
] = NULL
;
1022 } else if (errno
!= ENOENT
)
1031 bool timezone_is_valid(const char *name
) {
1042 for (p
= name
; *p
; p
++) {
1043 if (!(*p
>= '0' && *p
<= '9') &&
1044 !(*p
>= 'a' && *p
<= 'z') &&
1045 !(*p
>= 'A' && *p
<= 'Z') &&
1046 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1062 t
= strjoina("/usr/share/zoneinfo/", name
);
1063 if (stat(t
, &st
) < 0)
1066 if (!S_ISREG(st
.st_mode
))
1072 clockid_t
clock_boottime_or_monotonic(void) {
1073 static clockid_t clock
= -1;
1079 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1081 clock
= CLOCK_MONOTONIC
;
1084 clock
= CLOCK_BOOTTIME
;
1090 int get_timezone(char **tz
) {
1091 _cleanup_free_
char *t
= NULL
;
1096 r
= readlink_malloc("/etc/localtime", &t
);
1098 return r
; /* returns EINVAL if not a symlink */
1100 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1102 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1106 if (!timezone_is_valid(e
))
1117 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1118 return utc
? timegm(tm
) : mktime(tm
);
1121 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1122 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);