]>
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/>.
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 usec_t
now(clockid_t clock_id
) {
48 assert_se(clock_gettime(clock_id
, &ts
) == 0);
50 return timespec_load(&ts
);
53 nsec_t
now_nsec(clockid_t clock_id
) {
56 assert_se(clock_gettime(clock_id
, &ts
) == 0);
58 return timespec_load_nsec(&ts
);
61 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
64 ts
->realtime
= now(CLOCK_REALTIME
);
65 ts
->monotonic
= now(CLOCK_MONOTONIC
);
70 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
74 if (u
== USEC_INFINITY
|| u
<= 0) {
75 ts
->realtime
= ts
->monotonic
= u
;
81 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
82 ts
->monotonic
= now(CLOCK_MONOTONIC
);
84 if ((int64_t) ts
->monotonic
> delta
)
85 ts
->monotonic
-= delta
;
92 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
96 if (u
== USEC_INFINITY
) {
97 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
102 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
104 ts
->realtime
= now(CLOCK_REALTIME
);
105 if ((int64_t) ts
->realtime
> delta
)
106 ts
->realtime
-= delta
;
113 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
116 if (u
== USEC_INFINITY
) {
117 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
120 ts
->realtime
= now(CLOCK_REALTIME
);
121 ts
->monotonic
= now(CLOCK_MONOTONIC
);
123 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
125 if ((int64_t) ts
->realtime
> delta
)
126 ts
->realtime
-= delta
;
130 if ((int64_t) ts
->monotonic
> delta
)
131 ts
->monotonic
-= delta
;
139 usec_t
timespec_load(const struct timespec
*ts
) {
142 if (ts
->tv_sec
== (time_t) -1 &&
143 ts
->tv_nsec
== (long) -1)
144 return USEC_INFINITY
;
146 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
147 return USEC_INFINITY
;
150 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
151 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
154 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
157 if (ts
->tv_sec
== (time_t) -1 &&
158 ts
->tv_nsec
== (long) -1)
159 return NSEC_INFINITY
;
162 (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+
163 (nsec_t
) ts
->tv_nsec
;
166 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
169 if (u
== USEC_INFINITY
) {
170 ts
->tv_sec
= (time_t) -1;
171 ts
->tv_nsec
= (long) -1;
175 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
176 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
181 usec_t
timeval_load(const struct timeval
*tv
) {
184 if (tv
->tv_sec
== (time_t) -1 &&
185 tv
->tv_usec
== (suseconds_t
) -1)
186 return USEC_INFINITY
;
188 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
189 return USEC_INFINITY
;
192 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
193 (usec_t
) tv
->tv_usec
;
196 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
199 if (u
== USEC_INFINITY
) {
200 tv
->tv_sec
= (time_t) -1;
201 tv
->tv_usec
= (suseconds_t
) -1;
203 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
204 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
210 static char *format_timestamp_internal(char *buf
, size_t l
, usec_t t
, bool utc
) {
217 if (t
<= 0 || t
== USEC_INFINITY
)
220 sec
= (time_t) (t
/ USEC_PER_SEC
);
221 localtime_or_gmtime_r(&sec
, &tm
, utc
);
223 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", &tm
) <= 0)
229 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
230 return format_timestamp_internal(buf
, l
, t
, false);
233 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
234 return format_timestamp_internal(buf
, l
, t
, true);
237 static char *format_timestamp_internal_us(char *buf
, size_t l
, usec_t t
, bool utc
) {
244 if (t
<= 0 || t
== USEC_INFINITY
)
247 sec
= (time_t) (t
/ USEC_PER_SEC
);
248 localtime_or_gmtime_r(&sec
, &tm
, utc
);
250 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
252 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
253 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
259 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
260 return format_timestamp_internal_us(buf
, l
, t
, false);
263 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
264 return format_timestamp_internal_us(buf
, l
, t
, true);
267 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
271 if (t
<= 0 || t
== USEC_INFINITY
)
274 n
= now(CLOCK_REALTIME
);
283 if (d
>= USEC_PER_YEAR
)
284 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
286 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
287 else if (d
>= USEC_PER_MONTH
)
288 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
290 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
291 else if (d
>= USEC_PER_WEEK
)
292 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
294 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
295 else if (d
>= 2*USEC_PER_DAY
)
296 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
297 else if (d
>= 25*USEC_PER_HOUR
)
298 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
299 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
300 else if (d
>= 6*USEC_PER_HOUR
)
301 snprintf(buf
, l
, USEC_FMT
"h %s",
302 d
/ USEC_PER_HOUR
, s
);
303 else if (d
>= USEC_PER_HOUR
)
304 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
306 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
307 else if (d
>= 5*USEC_PER_MINUTE
)
308 snprintf(buf
, l
, USEC_FMT
"min %s",
309 d
/ USEC_PER_MINUTE
, s
);
310 else if (d
>= USEC_PER_MINUTE
)
311 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
313 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
314 else if (d
>= USEC_PER_SEC
)
315 snprintf(buf
, l
, USEC_FMT
"s %s",
316 d
/ USEC_PER_SEC
, s
);
317 else if (d
>= USEC_PER_MSEC
)
318 snprintf(buf
, l
, USEC_FMT
"ms %s",
319 d
/ USEC_PER_MSEC
, s
);
321 snprintf(buf
, l
, USEC_FMT
"us %s",
324 snprintf(buf
, l
, "now");
330 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
331 static const struct {
335 { "y", USEC_PER_YEAR
},
336 { "month", USEC_PER_MONTH
},
337 { "w", USEC_PER_WEEK
},
338 { "d", USEC_PER_DAY
},
339 { "h", USEC_PER_HOUR
},
340 { "min", USEC_PER_MINUTE
},
341 { "s", USEC_PER_SEC
},
342 { "ms", USEC_PER_MSEC
},
348 bool something
= false;
353 if (t
== USEC_INFINITY
) {
354 strncpy(p
, "infinity", l
-1);
360 strncpy(p
, "0", l
-1);
365 /* The result of this function can be parsed with parse_sec */
367 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
376 if (t
< accuracy
&& something
)
379 if (t
< table
[i
].usec
)
385 a
= t
/ table
[i
].usec
;
386 b
= t
% table
[i
].usec
;
388 /* Let's see if we should shows this in dot notation */
389 if (t
< USEC_PER_MINUTE
&& b
> 0) {
394 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
397 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
404 "%s"USEC_FMT
".%0*llu%s",
408 (unsigned long long) b
,
416 /* No? Then let's show it normally */
427 n
= MIN((size_t) k
, l
);
440 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
446 if (!dual_timestamp_is_set(t
))
449 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
455 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
456 unsigned long long a
, b
;
461 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
462 log_debug("Failed to parse finish timestamp value %s.", value
);
472 int parse_timestamp(const char *t
, usec_t
*usec
) {
473 static const struct {
497 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
504 * 2012-09-22 16:34:22
505 * 2012-09-22 16:34 (seconds will be set to 0)
506 * 2012-09-22 (time will be set to 00:00:00)
507 * 16:34:22 (date will be set to today)
508 * 16:34 (date will be set to today, seconds to 0)
510 * yesterday (time is set to 00:00:00)
511 * today (time is set to 00:00:00)
512 * tomorrow (time is set to 00:00:00)
515 * @2147483647 (seconds since epoch)
523 return parse_sec(t
+ 1, usec
);
525 ret
= now(CLOCK_REALTIME
);
530 else if (t
[0] == '+') {
531 r
= parse_sec(t
+1, &plus
);
537 } else if (t
[0] == '-') {
538 r
= parse_sec(t
+1, &minus
);
544 } else if ((k
= endswith(t
, " ago"))) {
545 t
= strndupa(t
, k
- t
);
547 r
= parse_sec(t
, &minus
);
553 } else if ((k
= endswith(t
, " left"))) {
554 t
= strndupa(t
, k
- t
);
556 r
= parse_sec(t
, &plus
);
563 utc
= endswith_no_case(t
, " UTC");
565 t
= strndupa(t
, utc
- t
);
567 x
= ret
/ USEC_PER_SEC
;
570 assert_se(localtime_or_gmtime_r(&x
, &tm
, utc
));
573 if (streq(t
, "today")) {
574 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
577 } else if (streq(t
, "yesterday")) {
579 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
582 } else if (streq(t
, "tomorrow")) {
584 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
589 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
592 if (!startswith_no_case(t
, day_nr
[i
].name
))
595 skip
= strlen(day_nr
[i
].name
);
599 weekday
= day_nr
[i
].nr
;
605 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
614 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
623 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
630 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
637 k
= strptime(t
, "%y-%m-%d", &tm
);
639 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
644 k
= strptime(t
, "%Y-%m-%d", &tm
);
646 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
651 k
= strptime(t
, "%H:%M:%S", &tm
);
660 k
= strptime(t
, "%H:%M", &tm
);
673 r
= parse_fractional_part_u(&k
, 6, &add
);
685 x
= mktime_or_timegm(&tm
, utc
);
686 if (x
== (time_t) -1)
689 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
692 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
706 int parse_time(const char *t
, usec_t
*usec
, usec_t default_unit
) {
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 { "M", USEC_PER_MONTH
},
722 { "msec", USEC_PER_MSEC
},
723 { "ms", USEC_PER_MSEC
},
724 { "m", USEC_PER_MINUTE
},
725 { "hours", USEC_PER_HOUR
},
726 { "hour", USEC_PER_HOUR
},
727 { "hr", USEC_PER_HOUR
},
728 { "h", USEC_PER_HOUR
},
729 { "days", USEC_PER_DAY
},
730 { "day", USEC_PER_DAY
},
731 { "d", USEC_PER_DAY
},
732 { "weeks", USEC_PER_WEEK
},
733 { "week", USEC_PER_WEEK
},
734 { "w", USEC_PER_WEEK
},
735 { "years", USEC_PER_YEAR
},
736 { "year", USEC_PER_YEAR
},
737 { "y", USEC_PER_YEAR
},
744 bool something
= false;
748 assert(default_unit
> 0);
752 p
+= strspn(p
, WHITESPACE
);
753 s
= startswith(p
, "infinity");
755 s
+= strspn(s
, WHITESPACE
);
759 *usec
= USEC_INFINITY
;
767 usec_t multiplier
, k
;
769 p
+= strspn(p
, WHITESPACE
);
779 l
= strtoll(p
, &e
, 10);
791 z
= strtoll(b
, &e
, 10);
806 e
+= strspn(e
, WHITESPACE
);
808 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
809 if (startswith(e
, table
[i
].suffix
)) {
810 multiplier
= table
[i
].usec
;
811 p
= e
+ strlen(table
[i
].suffix
);
815 if (i
>= ELEMENTSOF(table
)) {
816 multiplier
= default_unit
;
822 k
= (usec_t
) z
* multiplier
;
827 r
+= (usec_t
) l
* multiplier
+ k
;
835 int parse_sec(const char *t
, usec_t
*usec
) {
836 return parse_time(t
, usec
, USEC_PER_SEC
);
839 int parse_nsec(const char *t
, nsec_t
*nsec
) {
840 static const struct {
844 { "seconds", NSEC_PER_SEC
},
845 { "second", NSEC_PER_SEC
},
846 { "sec", NSEC_PER_SEC
},
847 { "s", NSEC_PER_SEC
},
848 { "minutes", NSEC_PER_MINUTE
},
849 { "minute", NSEC_PER_MINUTE
},
850 { "min", NSEC_PER_MINUTE
},
851 { "months", NSEC_PER_MONTH
},
852 { "month", NSEC_PER_MONTH
},
853 { "msec", NSEC_PER_MSEC
},
854 { "ms", NSEC_PER_MSEC
},
855 { "m", NSEC_PER_MINUTE
},
856 { "hours", NSEC_PER_HOUR
},
857 { "hour", NSEC_PER_HOUR
},
858 { "hr", NSEC_PER_HOUR
},
859 { "h", NSEC_PER_HOUR
},
860 { "days", NSEC_PER_DAY
},
861 { "day", NSEC_PER_DAY
},
862 { "d", NSEC_PER_DAY
},
863 { "weeks", NSEC_PER_WEEK
},
864 { "week", NSEC_PER_WEEK
},
865 { "w", NSEC_PER_WEEK
},
866 { "years", NSEC_PER_YEAR
},
867 { "year", NSEC_PER_YEAR
},
868 { "y", NSEC_PER_YEAR
},
869 { "usec", NSEC_PER_USEC
},
870 { "us", NSEC_PER_USEC
},
873 { "", 1ULL }, /* default is nsec */
878 bool something
= false;
885 p
+= strspn(p
, WHITESPACE
);
886 s
= startswith(p
, "infinity");
888 s
+= strspn(s
, WHITESPACE
);
892 *nsec
= NSEC_INFINITY
;
901 p
+= strspn(p
, WHITESPACE
);
911 l
= strtoll(p
, &e
, 10);
923 z
= strtoll(b
, &e
, 10);
938 e
+= strspn(e
, WHITESPACE
);
940 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
941 if (startswith(e
, table
[i
].suffix
)) {
942 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
947 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
948 p
= e
+ strlen(table
[i
].suffix
);
954 if (i
>= ELEMENTSOF(table
))
964 bool ntp_synced(void) {
965 struct timex txc
= {};
967 if (adjtimex(&txc
) < 0)
970 if (txc
.status
& STA_UNSYNC
)
976 int get_timezones(char ***ret
) {
977 _cleanup_fclose_
FILE *f
= NULL
;
978 _cleanup_strv_free_
char **zones
= NULL
;
979 size_t n_zones
= 0, n_allocated
= 0;
983 zones
= strv_new("UTC", NULL
);
990 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
994 FOREACH_LINE(l
, f
, return -errno
) {
1000 if (isempty(p
) || *p
== '#')
1003 /* Skip over country code */
1004 p
+= strcspn(p
, WHITESPACE
);
1005 p
+= strspn(p
, WHITESPACE
);
1007 /* Skip over coordinates */
1008 p
+= strcspn(p
, WHITESPACE
);
1009 p
+= strspn(p
, WHITESPACE
);
1011 /* Found timezone name */
1012 k
= strcspn(p
, WHITESPACE
);
1020 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1025 zones
[n_zones
++] = w
;
1026 zones
[n_zones
] = NULL
;
1031 } else if (errno
!= ENOENT
)
1040 bool timezone_is_valid(const char *name
) {
1051 for (p
= name
; *p
; p
++) {
1052 if (!(*p
>= '0' && *p
<= '9') &&
1053 !(*p
>= 'a' && *p
<= 'z') &&
1054 !(*p
>= 'A' && *p
<= 'Z') &&
1055 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1071 t
= strjoina("/usr/share/zoneinfo/", name
);
1072 if (stat(t
, &st
) < 0)
1075 if (!S_ISREG(st
.st_mode
))
1081 clockid_t
clock_boottime_or_monotonic(void) {
1082 static clockid_t clock
= -1;
1088 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1090 clock
= CLOCK_MONOTONIC
;
1093 clock
= CLOCK_BOOTTIME
;
1099 int get_timezone(char **tz
) {
1100 _cleanup_free_
char *t
= NULL
;
1105 r
= readlink_malloc("/etc/localtime", &t
);
1107 return r
; /* returns EINVAL if not a symlink */
1109 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1111 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1115 if (!timezone_is_valid(e
))
1126 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1127 return utc
? timegm(tm
) : mktime(tm
);
1130 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1131 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1134 unsigned long usec_to_jiffies(usec_t u
) {
1135 static thread_local
unsigned long hz
= 0;
1139 r
= sysconf(_SC_CLK_TCK
);
1142 hz
= (unsigned long) r
;
1145 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);