]>
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/timex.h>
24 #include <sys/timerfd.h>
27 #include "time-util.h"
28 #include "path-util.h"
31 usec_t
now(clockid_t clock_id
) {
34 assert_se(clock_gettime(clock_id
, &ts
) == 0);
36 return timespec_load(&ts
);
39 nsec_t
now_nsec(clockid_t clock_id
) {
42 assert_se(clock_gettime(clock_id
, &ts
) == 0);
44 return timespec_load_nsec(&ts
);
47 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
50 ts
->realtime
= now(CLOCK_REALTIME
);
51 ts
->monotonic
= now(CLOCK_MONOTONIC
);
56 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
60 if (u
== USEC_INFINITY
|| u
<= 0) {
61 ts
->realtime
= ts
->monotonic
= u
;
67 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
68 ts
->monotonic
= now(CLOCK_MONOTONIC
);
70 if ((int64_t) ts
->monotonic
> delta
)
71 ts
->monotonic
-= delta
;
78 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
82 if (u
== USEC_INFINITY
) {
83 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
88 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
90 ts
->realtime
= now(CLOCK_REALTIME
);
91 if ((int64_t) ts
->realtime
> delta
)
92 ts
->realtime
-= delta
;
99 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
102 if (u
== USEC_INFINITY
) {
103 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
106 ts
->realtime
= now(CLOCK_REALTIME
);
107 ts
->monotonic
= now(CLOCK_MONOTONIC
);
109 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
111 if ((int64_t) ts
->realtime
> delta
)
112 ts
->realtime
-= delta
;
116 if ((int64_t) ts
->monotonic
> delta
)
117 ts
->monotonic
-= delta
;
125 usec_t
timespec_load(const struct timespec
*ts
) {
128 if (ts
->tv_sec
== (time_t) -1 &&
129 ts
->tv_nsec
== (long) -1)
130 return USEC_INFINITY
;
132 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
133 return USEC_INFINITY
;
136 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
137 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
140 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
143 if (ts
->tv_sec
== (time_t) -1 &&
144 ts
->tv_nsec
== (long) -1)
145 return NSEC_INFINITY
;
148 (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+
149 (nsec_t
) ts
->tv_nsec
;
152 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
155 if (u
== USEC_INFINITY
) {
156 ts
->tv_sec
= (time_t) -1;
157 ts
->tv_nsec
= (long) -1;
161 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
162 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
167 usec_t
timeval_load(const struct timeval
*tv
) {
170 if (tv
->tv_sec
== (time_t) -1 &&
171 tv
->tv_usec
== (suseconds_t
) -1)
172 return USEC_INFINITY
;
174 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
175 return USEC_INFINITY
;
178 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
179 (usec_t
) tv
->tv_usec
;
182 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
185 if (u
== USEC_INFINITY
) {
186 tv
->tv_sec
= (time_t) -1;
187 tv
->tv_usec
= (suseconds_t
) -1;
189 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
190 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
196 static char *format_timestamp_internal(char *buf
, size_t l
, usec_t t
, bool utc
) {
203 if (t
<= 0 || t
== USEC_INFINITY
)
206 sec
= (time_t) (t
/ USEC_PER_SEC
);
207 localtime_or_gmtime_r(&sec
, &tm
, utc
);
209 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", &tm
) <= 0)
215 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
216 return format_timestamp_internal(buf
, l
, t
, false);
219 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
220 return format_timestamp_internal(buf
, l
, t
, true);
223 static char *format_timestamp_internal_us(char *buf
, size_t l
, usec_t t
, bool utc
) {
230 if (t
<= 0 || t
== USEC_INFINITY
)
233 sec
= (time_t) (t
/ USEC_PER_SEC
);
234 localtime_or_gmtime_r(&sec
, &tm
, utc
);
236 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
238 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
239 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
245 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
246 return format_timestamp_internal_us(buf
, l
, t
, false);
249 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
250 return format_timestamp_internal_us(buf
, l
, t
, true);
253 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
257 if (t
<= 0 || t
== USEC_INFINITY
)
260 n
= now(CLOCK_REALTIME
);
269 if (d
>= USEC_PER_YEAR
)
270 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
272 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
273 else if (d
>= USEC_PER_MONTH
)
274 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
276 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
277 else if (d
>= USEC_PER_WEEK
)
278 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
280 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
281 else if (d
>= 2*USEC_PER_DAY
)
282 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
283 else if (d
>= 25*USEC_PER_HOUR
)
284 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
285 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
286 else if (d
>= 6*USEC_PER_HOUR
)
287 snprintf(buf
, l
, USEC_FMT
"h %s",
288 d
/ USEC_PER_HOUR
, s
);
289 else if (d
>= USEC_PER_HOUR
)
290 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
292 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
293 else if (d
>= 5*USEC_PER_MINUTE
)
294 snprintf(buf
, l
, USEC_FMT
"min %s",
295 d
/ USEC_PER_MINUTE
, s
);
296 else if (d
>= USEC_PER_MINUTE
)
297 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
299 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
300 else if (d
>= USEC_PER_SEC
)
301 snprintf(buf
, l
, USEC_FMT
"s %s",
302 d
/ USEC_PER_SEC
, s
);
303 else if (d
>= USEC_PER_MSEC
)
304 snprintf(buf
, l
, USEC_FMT
"ms %s",
305 d
/ USEC_PER_MSEC
, s
);
307 snprintf(buf
, l
, USEC_FMT
"us %s",
310 snprintf(buf
, l
, "now");
316 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
317 static const struct {
321 { "y", USEC_PER_YEAR
},
322 { "month", USEC_PER_MONTH
},
323 { "w", USEC_PER_WEEK
},
324 { "d", USEC_PER_DAY
},
325 { "h", USEC_PER_HOUR
},
326 { "min", USEC_PER_MINUTE
},
327 { "s", USEC_PER_SEC
},
328 { "ms", USEC_PER_MSEC
},
334 bool something
= false;
339 if (t
== USEC_INFINITY
) {
340 strncpy(p
, "infinity", l
-1);
346 strncpy(p
, "0", l
-1);
351 /* The result of this function can be parsed with parse_sec */
353 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
362 if (t
< accuracy
&& something
)
365 if (t
< table
[i
].usec
)
371 a
= t
/ table
[i
].usec
;
372 b
= t
% table
[i
].usec
;
374 /* Let's see if we should shows this in dot notation */
375 if (t
< USEC_PER_MINUTE
&& b
> 0) {
380 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
383 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
390 "%s"USEC_FMT
".%0*llu%s",
394 (unsigned long long) b
,
402 /* No? Then let's show it normally */
413 n
= MIN((size_t) k
, l
);
426 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
432 if (!dual_timestamp_is_set(t
))
435 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
441 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
442 unsigned long long a
, b
;
447 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
448 log_debug("Failed to parse finish timestamp value %s.", value
);
458 int parse_timestamp(const char *t
, usec_t
*usec
) {
459 static const struct {
483 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
490 * 2012-09-22 16:34:22
491 * 2012-09-22 16:34 (seconds will be set to 0)
492 * 2012-09-22 (time will be set to 00:00:00)
493 * 16:34:22 (date will be set to today)
494 * 16:34 (date will be set to today, seconds to 0)
496 * yesterday (time is set to 00:00:00)
497 * today (time is set to 00:00:00)
498 * tomorrow (time is set to 00:00:00)
501 * @2147483647 (seconds since epoch)
509 return parse_sec(t
+ 1, usec
);
511 ret
= now(CLOCK_REALTIME
);
516 else if (t
[0] == '+') {
517 r
= parse_sec(t
+1, &plus
);
523 } else if (t
[0] == '-') {
524 r
= parse_sec(t
+1, &minus
);
530 } else if (endswith(t
, " ago")) {
531 t
= strndupa(t
, strlen(t
) - strlen(" ago"));
533 r
= parse_sec(t
, &minus
);
539 } else if (endswith(t
, " left")) {
540 t
= strndupa(t
, strlen(t
) - strlen(" left"));
542 r
= parse_sec(t
, &plus
);
549 utc
= endswith_no_case(t
, " UTC");
551 t
= strndupa(t
, strlen(t
) - strlen(" UTC"));
553 x
= ret
/ USEC_PER_SEC
;
556 assert_se(localtime_or_gmtime_r(&x
, &tm
, utc
));
559 if (streq(t
, "today")) {
560 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
563 } else if (streq(t
, "yesterday")) {
565 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
568 } else if (streq(t
, "tomorrow")) {
570 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
575 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
578 if (!startswith_no_case(t
, day_nr
[i
].name
))
581 skip
= strlen(day_nr
[i
].name
);
585 weekday
= day_nr
[i
].nr
;
591 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
600 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
609 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
616 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
623 k
= strptime(t
, "%y-%m-%d", &tm
);
625 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
630 k
= strptime(t
, "%Y-%m-%d", &tm
);
632 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
637 k
= strptime(t
, "%H:%M:%S", &tm
);
646 k
= strptime(t
, "%H:%M", &tm
);
657 unsigned long long val
;
661 if (*k
< '0' || *k
> '9')
664 /* base 10 instead of base 0, .09 is not base 8 */
666 val
= strtoull(k
, &end
, 10);
672 /* val has l digits, make them 6 */
682 x
= mktime_or_timegm(&tm
, utc
);
683 if (x
== (time_t) -1)
686 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
689 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
703 int parse_sec(const char *t
, usec_t
*usec
) {
704 static const struct {
708 { "seconds", USEC_PER_SEC
},
709 { "second", USEC_PER_SEC
},
710 { "sec", USEC_PER_SEC
},
711 { "s", USEC_PER_SEC
},
712 { "minutes", USEC_PER_MINUTE
},
713 { "minute", USEC_PER_MINUTE
},
714 { "min", USEC_PER_MINUTE
},
715 { "months", USEC_PER_MONTH
},
716 { "month", USEC_PER_MONTH
},
717 { "msec", USEC_PER_MSEC
},
718 { "ms", USEC_PER_MSEC
},
719 { "m", USEC_PER_MINUTE
},
720 { "hours", USEC_PER_HOUR
},
721 { "hour", USEC_PER_HOUR
},
722 { "hr", USEC_PER_HOUR
},
723 { "h", USEC_PER_HOUR
},
724 { "days", USEC_PER_DAY
},
725 { "day", USEC_PER_DAY
},
726 { "d", USEC_PER_DAY
},
727 { "weeks", USEC_PER_WEEK
},
728 { "week", USEC_PER_WEEK
},
729 { "w", USEC_PER_WEEK
},
730 { "years", USEC_PER_YEAR
},
731 { "year", USEC_PER_YEAR
},
732 { "y", USEC_PER_YEAR
},
735 { "", USEC_PER_SEC
}, /* default is sec */
740 bool something
= false;
747 p
+= strspn(p
, WHITESPACE
);
748 s
= startswith(p
, "infinity");
750 s
+= strspn(s
, WHITESPACE
);
754 *usec
= USEC_INFINITY
;
763 p
+= strspn(p
, WHITESPACE
);
773 l
= strtoll(p
, &e
, 10);
785 z
= strtoll(b
, &e
, 10);
800 e
+= strspn(e
, WHITESPACE
);
802 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
803 if (startswith(e
, table
[i
].suffix
)) {
804 usec_t k
= (usec_t
) z
* table
[i
].usec
;
809 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
810 p
= e
+ strlen(table
[i
].suffix
);
816 if (i
>= ELEMENTSOF(table
))
826 int parse_nsec(const char *t
, nsec_t
*nsec
) {
827 static const struct {
831 { "seconds", NSEC_PER_SEC
},
832 { "second", NSEC_PER_SEC
},
833 { "sec", NSEC_PER_SEC
},
834 { "s", NSEC_PER_SEC
},
835 { "minutes", NSEC_PER_MINUTE
},
836 { "minute", NSEC_PER_MINUTE
},
837 { "min", NSEC_PER_MINUTE
},
838 { "months", NSEC_PER_MONTH
},
839 { "month", NSEC_PER_MONTH
},
840 { "msec", NSEC_PER_MSEC
},
841 { "ms", NSEC_PER_MSEC
},
842 { "m", NSEC_PER_MINUTE
},
843 { "hours", NSEC_PER_HOUR
},
844 { "hour", NSEC_PER_HOUR
},
845 { "hr", NSEC_PER_HOUR
},
846 { "h", NSEC_PER_HOUR
},
847 { "days", NSEC_PER_DAY
},
848 { "day", NSEC_PER_DAY
},
849 { "d", NSEC_PER_DAY
},
850 { "weeks", NSEC_PER_WEEK
},
851 { "week", NSEC_PER_WEEK
},
852 { "w", NSEC_PER_WEEK
},
853 { "years", NSEC_PER_YEAR
},
854 { "year", NSEC_PER_YEAR
},
855 { "y", NSEC_PER_YEAR
},
856 { "usec", NSEC_PER_USEC
},
857 { "us", NSEC_PER_USEC
},
860 { "", 1ULL }, /* default is nsec */
865 bool something
= false;
872 p
+= strspn(p
, WHITESPACE
);
873 s
= startswith(p
, "infinity");
875 s
+= strspn(s
, WHITESPACE
);
879 *nsec
= NSEC_INFINITY
;
888 p
+= strspn(p
, WHITESPACE
);
898 l
= strtoll(p
, &e
, 10);
910 z
= strtoll(b
, &e
, 10);
925 e
+= strspn(e
, WHITESPACE
);
927 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
928 if (startswith(e
, table
[i
].suffix
)) {
929 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
934 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
935 p
= e
+ strlen(table
[i
].suffix
);
941 if (i
>= ELEMENTSOF(table
))
951 bool ntp_synced(void) {
952 struct timex txc
= {};
954 if (adjtimex(&txc
) < 0)
957 if (txc
.status
& STA_UNSYNC
)
963 int get_timezones(char ***ret
) {
964 _cleanup_fclose_
FILE *f
= NULL
;
965 _cleanup_strv_free_
char **zones
= NULL
;
966 size_t n_zones
= 0, n_allocated
= 0;
970 zones
= strv_new("UTC", NULL
);
977 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
981 FOREACH_LINE(l
, f
, return -errno
) {
987 if (isempty(p
) || *p
== '#')
990 /* Skip over country code */
991 p
+= strcspn(p
, WHITESPACE
);
992 p
+= strspn(p
, WHITESPACE
);
994 /* Skip over coordinates */
995 p
+= strcspn(p
, WHITESPACE
);
996 p
+= strspn(p
, WHITESPACE
);
998 /* Found timezone name */
999 k
= strcspn(p
, WHITESPACE
);
1007 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1012 zones
[n_zones
++] = w
;
1013 zones
[n_zones
] = NULL
;
1018 } else if (errno
!= ENOENT
)
1027 bool timezone_is_valid(const char *name
) {
1038 for (p
= name
; *p
; p
++) {
1039 if (!(*p
>= '0' && *p
<= '9') &&
1040 !(*p
>= 'a' && *p
<= 'z') &&
1041 !(*p
>= 'A' && *p
<= 'Z') &&
1042 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1058 t
= strjoina("/usr/share/zoneinfo/", name
);
1059 if (stat(t
, &st
) < 0)
1062 if (!S_ISREG(st
.st_mode
))
1068 clockid_t
clock_boottime_or_monotonic(void) {
1069 static clockid_t clock
= -1;
1075 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1077 clock
= CLOCK_MONOTONIC
;
1080 clock
= CLOCK_BOOTTIME
;
1086 int get_timezone(char **tz
) {
1087 _cleanup_free_
char *t
= NULL
;
1092 r
= readlink_malloc("/etc/localtime", &t
);
1094 return r
; /* returns EINVAL if not a symlink */
1096 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1098 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1102 if (!timezone_is_valid(e
))
1113 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1114 return utc
? timegm(tm
) : mktime(tm
);
1117 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1118 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);