]>
git.proxmox.com Git - 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>
26 #include "alloc-util.h"
30 #include "path-util.h"
31 #include "string-util.h"
33 #include "time-util.h"
36 usec_t
now(clockid_t clock_id
) {
39 assert_se(clock_gettime(clock_id
, &ts
) == 0);
41 return timespec_load(&ts
);
44 nsec_t
now_nsec(clockid_t clock_id
) {
47 assert_se(clock_gettime(clock_id
, &ts
) == 0);
49 return timespec_load_nsec(&ts
);
52 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
55 ts
->realtime
= now(CLOCK_REALTIME
);
56 ts
->monotonic
= now(CLOCK_MONOTONIC
);
61 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
65 if (u
== USEC_INFINITY
|| u
<= 0) {
66 ts
->realtime
= ts
->monotonic
= u
;
72 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
73 ts
->monotonic
= now(CLOCK_MONOTONIC
);
75 if ((int64_t) ts
->monotonic
> delta
)
76 ts
->monotonic
-= delta
;
83 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
87 if (u
== USEC_INFINITY
) {
88 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
93 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
95 ts
->realtime
= now(CLOCK_REALTIME
);
96 if ((int64_t) ts
->realtime
> delta
)
97 ts
->realtime
-= delta
;
104 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
107 if (u
== USEC_INFINITY
) {
108 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
111 ts
->realtime
= now(CLOCK_REALTIME
);
112 ts
->monotonic
= now(CLOCK_MONOTONIC
);
114 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
116 if ((int64_t) ts
->realtime
> delta
)
117 ts
->realtime
-= delta
;
121 if ((int64_t) ts
->monotonic
> delta
)
122 ts
->monotonic
-= delta
;
130 usec_t
timespec_load(const struct timespec
*ts
) {
133 if (ts
->tv_sec
== (time_t) -1 &&
134 ts
->tv_nsec
== (long) -1)
135 return USEC_INFINITY
;
137 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
138 return USEC_INFINITY
;
141 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
142 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
145 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
148 if (ts
->tv_sec
== (time_t) -1 &&
149 ts
->tv_nsec
== (long) -1)
150 return NSEC_INFINITY
;
153 (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+
154 (nsec_t
) ts
->tv_nsec
;
157 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
160 if (u
== USEC_INFINITY
) {
161 ts
->tv_sec
= (time_t) -1;
162 ts
->tv_nsec
= (long) -1;
166 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
167 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
172 usec_t
timeval_load(const struct timeval
*tv
) {
175 if (tv
->tv_sec
== (time_t) -1 &&
176 tv
->tv_usec
== (suseconds_t
) -1)
177 return USEC_INFINITY
;
179 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
180 return USEC_INFINITY
;
183 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
184 (usec_t
) tv
->tv_usec
;
187 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
190 if (u
== USEC_INFINITY
) {
191 tv
->tv_sec
= (time_t) -1;
192 tv
->tv_usec
= (suseconds_t
) -1;
194 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
195 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
201 static char *format_timestamp_internal(char *buf
, size_t l
, usec_t t
, bool utc
) {
208 if (t
<= 0 || t
== USEC_INFINITY
)
211 sec
= (time_t) (t
/ USEC_PER_SEC
);
212 localtime_or_gmtime_r(&sec
, &tm
, utc
);
214 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", &tm
) <= 0)
220 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
221 return format_timestamp_internal(buf
, l
, t
, false);
224 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
225 return format_timestamp_internal(buf
, l
, t
, true);
228 static char *format_timestamp_internal_us(char *buf
, size_t l
, usec_t t
, bool utc
) {
235 if (t
<= 0 || t
== USEC_INFINITY
)
238 sec
= (time_t) (t
/ USEC_PER_SEC
);
239 localtime_or_gmtime_r(&sec
, &tm
, utc
);
241 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
243 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
244 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
250 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
251 return format_timestamp_internal_us(buf
, l
, t
, false);
254 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
255 return format_timestamp_internal_us(buf
, l
, t
, true);
258 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
262 if (t
<= 0 || t
== USEC_INFINITY
)
265 n
= now(CLOCK_REALTIME
);
274 if (d
>= USEC_PER_YEAR
)
275 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
277 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
278 else if (d
>= USEC_PER_MONTH
)
279 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
281 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
282 else if (d
>= USEC_PER_WEEK
)
283 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
285 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
286 else if (d
>= 2*USEC_PER_DAY
)
287 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
288 else if (d
>= 25*USEC_PER_HOUR
)
289 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
290 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
291 else if (d
>= 6*USEC_PER_HOUR
)
292 snprintf(buf
, l
, USEC_FMT
"h %s",
293 d
/ USEC_PER_HOUR
, s
);
294 else if (d
>= USEC_PER_HOUR
)
295 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
297 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
298 else if (d
>= 5*USEC_PER_MINUTE
)
299 snprintf(buf
, l
, USEC_FMT
"min %s",
300 d
/ USEC_PER_MINUTE
, s
);
301 else if (d
>= USEC_PER_MINUTE
)
302 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
304 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
305 else if (d
>= USEC_PER_SEC
)
306 snprintf(buf
, l
, USEC_FMT
"s %s",
307 d
/ USEC_PER_SEC
, s
);
308 else if (d
>= USEC_PER_MSEC
)
309 snprintf(buf
, l
, USEC_FMT
"ms %s",
310 d
/ USEC_PER_MSEC
, s
);
312 snprintf(buf
, l
, USEC_FMT
"us %s",
315 snprintf(buf
, l
, "now");
321 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
322 static const struct {
326 { "y", USEC_PER_YEAR
},
327 { "month", USEC_PER_MONTH
},
328 { "w", USEC_PER_WEEK
},
329 { "d", USEC_PER_DAY
},
330 { "h", USEC_PER_HOUR
},
331 { "min", USEC_PER_MINUTE
},
332 { "s", USEC_PER_SEC
},
333 { "ms", USEC_PER_MSEC
},
339 bool something
= false;
344 if (t
== USEC_INFINITY
) {
345 strncpy(p
, "infinity", l
-1);
351 strncpy(p
, "0", l
-1);
356 /* The result of this function can be parsed with parse_sec */
358 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
367 if (t
< accuracy
&& something
)
370 if (t
< table
[i
].usec
)
376 a
= t
/ table
[i
].usec
;
377 b
= t
% table
[i
].usec
;
379 /* Let's see if we should shows this in dot notation */
380 if (t
< USEC_PER_MINUTE
&& b
> 0) {
385 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
388 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
395 "%s"USEC_FMT
".%0*llu%s",
399 (unsigned long long) b
,
407 /* No? Then let's show it normally */
418 n
= MIN((size_t) k
, l
);
431 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
437 if (!dual_timestamp_is_set(t
))
440 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
446 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
447 unsigned long long a
, b
;
452 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
453 log_debug("Failed to parse finish timestamp value %s.", value
);
463 int parse_timestamp(const char *t
, usec_t
*usec
) {
464 static const struct {
488 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
495 * 2012-09-22 16:34:22
496 * 2012-09-22 16:34 (seconds will be set to 0)
497 * 2012-09-22 (time will be set to 00:00:00)
498 * 16:34:22 (date will be set to today)
499 * 16:34 (date will be set to today, seconds to 0)
501 * yesterday (time is set to 00:00:00)
502 * today (time is set to 00:00:00)
503 * tomorrow (time is set to 00:00:00)
506 * @2147483647 (seconds since epoch)
514 return parse_sec(t
+ 1, usec
);
516 ret
= now(CLOCK_REALTIME
);
521 else if (t
[0] == '+') {
522 r
= parse_sec(t
+1, &plus
);
528 } else if (t
[0] == '-') {
529 r
= parse_sec(t
+1, &minus
);
535 } else if ((k
= endswith(t
, " ago"))) {
536 t
= strndupa(t
, k
- t
);
538 r
= parse_sec(t
, &minus
);
544 } else if ((k
= endswith(t
, " left"))) {
545 t
= strndupa(t
, k
- t
);
547 r
= parse_sec(t
, &plus
);
554 utc
= endswith_no_case(t
, " UTC");
556 t
= strndupa(t
, utc
- t
);
558 x
= ret
/ USEC_PER_SEC
;
561 assert_se(localtime_or_gmtime_r(&x
, &tm
, utc
));
564 if (streq(t
, "today")) {
565 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
568 } else if (streq(t
, "yesterday")) {
570 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
573 } else if (streq(t
, "tomorrow")) {
575 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
580 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
583 if (!startswith_no_case(t
, day_nr
[i
].name
))
586 skip
= strlen(day_nr
[i
].name
);
590 weekday
= day_nr
[i
].nr
;
596 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
605 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
614 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
621 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
628 k
= strptime(t
, "%y-%m-%d", &tm
);
630 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
635 k
= strptime(t
, "%Y-%m-%d", &tm
);
637 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
642 k
= strptime(t
, "%H:%M:%S", &tm
);
651 k
= strptime(t
, "%H:%M", &tm
);
662 unsigned long long val
;
666 if (*k
< '0' || *k
> '9')
669 /* base 10 instead of base 0, .09 is not base 8 */
671 val
= strtoull(k
, &end
, 10);
677 /* val has l digits, make them 6 */
687 x
= mktime_or_timegm(&tm
, utc
);
688 if (x
== (time_t) -1)
691 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
694 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
708 int parse_time(const char *t
, usec_t
*usec
, usec_t default_unit
) {
710 static const struct {
714 { "seconds", USEC_PER_SEC
},
715 { "second", USEC_PER_SEC
},
716 { "sec", USEC_PER_SEC
},
717 { "s", USEC_PER_SEC
},
718 { "minutes", USEC_PER_MINUTE
},
719 { "minute", USEC_PER_MINUTE
},
720 { "min", USEC_PER_MINUTE
},
721 { "months", USEC_PER_MONTH
},
722 { "month", USEC_PER_MONTH
},
723 { "M", USEC_PER_MONTH
},
724 { "msec", USEC_PER_MSEC
},
725 { "ms", USEC_PER_MSEC
},
726 { "m", USEC_PER_MINUTE
},
727 { "hours", USEC_PER_HOUR
},
728 { "hour", USEC_PER_HOUR
},
729 { "hr", USEC_PER_HOUR
},
730 { "h", USEC_PER_HOUR
},
731 { "days", USEC_PER_DAY
},
732 { "day", USEC_PER_DAY
},
733 { "d", USEC_PER_DAY
},
734 { "weeks", USEC_PER_WEEK
},
735 { "week", USEC_PER_WEEK
},
736 { "w", USEC_PER_WEEK
},
737 { "years", USEC_PER_YEAR
},
738 { "year", USEC_PER_YEAR
},
739 { "y", USEC_PER_YEAR
},
746 bool something
= false;
750 assert(default_unit
> 0);
754 p
+= strspn(p
, WHITESPACE
);
755 s
= startswith(p
, "infinity");
757 s
+= strspn(s
, WHITESPACE
);
761 *usec
= USEC_INFINITY
;
769 usec_t multiplier
, k
;
771 p
+= strspn(p
, WHITESPACE
);
781 l
= strtoll(p
, &e
, 10);
793 z
= strtoll(b
, &e
, 10);
808 e
+= strspn(e
, WHITESPACE
);
810 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
811 if (startswith(e
, table
[i
].suffix
)) {
812 multiplier
= table
[i
].usec
;
813 p
= e
+ strlen(table
[i
].suffix
);
817 if (i
>= ELEMENTSOF(table
)) {
818 multiplier
= default_unit
;
824 k
= (usec_t
) z
* multiplier
;
829 r
+= (usec_t
) l
* multiplier
+ k
;
837 int parse_sec(const char *t
, usec_t
*usec
) {
838 return parse_time(t
, usec
, USEC_PER_SEC
);
841 int parse_nsec(const char *t
, nsec_t
*nsec
) {
842 static const struct {
846 { "seconds", NSEC_PER_SEC
},
847 { "second", NSEC_PER_SEC
},
848 { "sec", NSEC_PER_SEC
},
849 { "s", NSEC_PER_SEC
},
850 { "minutes", NSEC_PER_MINUTE
},
851 { "minute", NSEC_PER_MINUTE
},
852 { "min", NSEC_PER_MINUTE
},
853 { "months", NSEC_PER_MONTH
},
854 { "month", NSEC_PER_MONTH
},
855 { "msec", NSEC_PER_MSEC
},
856 { "ms", NSEC_PER_MSEC
},
857 { "m", NSEC_PER_MINUTE
},
858 { "hours", NSEC_PER_HOUR
},
859 { "hour", NSEC_PER_HOUR
},
860 { "hr", NSEC_PER_HOUR
},
861 { "h", NSEC_PER_HOUR
},
862 { "days", NSEC_PER_DAY
},
863 { "day", NSEC_PER_DAY
},
864 { "d", NSEC_PER_DAY
},
865 { "weeks", NSEC_PER_WEEK
},
866 { "week", NSEC_PER_WEEK
},
867 { "w", NSEC_PER_WEEK
},
868 { "years", NSEC_PER_YEAR
},
869 { "year", NSEC_PER_YEAR
},
870 { "y", NSEC_PER_YEAR
},
871 { "usec", NSEC_PER_USEC
},
872 { "us", NSEC_PER_USEC
},
875 { "", 1ULL }, /* default is nsec */
880 bool something
= false;
887 p
+= strspn(p
, WHITESPACE
);
888 s
= startswith(p
, "infinity");
890 s
+= strspn(s
, WHITESPACE
);
894 *nsec
= NSEC_INFINITY
;
903 p
+= strspn(p
, WHITESPACE
);
913 l
= strtoll(p
, &e
, 10);
925 z
= strtoll(b
, &e
, 10);
940 e
+= strspn(e
, WHITESPACE
);
942 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
943 if (startswith(e
, table
[i
].suffix
)) {
944 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
949 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
950 p
= e
+ strlen(table
[i
].suffix
);
956 if (i
>= ELEMENTSOF(table
))
966 bool ntp_synced(void) {
967 struct timex txc
= {};
969 if (adjtimex(&txc
) < 0)
972 if (txc
.status
& STA_UNSYNC
)
978 int get_timezones(char ***ret
) {
979 _cleanup_fclose_
FILE *f
= NULL
;
980 _cleanup_strv_free_
char **zones
= NULL
;
981 size_t n_zones
= 0, n_allocated
= 0;
985 zones
= strv_new("UTC", NULL
);
992 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
996 FOREACH_LINE(l
, f
, return -errno
) {
1002 if (isempty(p
) || *p
== '#')
1005 /* Skip over country code */
1006 p
+= strcspn(p
, WHITESPACE
);
1007 p
+= strspn(p
, WHITESPACE
);
1009 /* Skip over coordinates */
1010 p
+= strcspn(p
, WHITESPACE
);
1011 p
+= strspn(p
, WHITESPACE
);
1013 /* Found timezone name */
1014 k
= strcspn(p
, WHITESPACE
);
1022 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1027 zones
[n_zones
++] = w
;
1028 zones
[n_zones
] = NULL
;
1033 } else if (errno
!= ENOENT
)
1042 bool timezone_is_valid(const char *name
) {
1053 for (p
= name
; *p
; p
++) {
1054 if (!(*p
>= '0' && *p
<= '9') &&
1055 !(*p
>= 'a' && *p
<= 'z') &&
1056 !(*p
>= 'A' && *p
<= 'Z') &&
1057 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1073 t
= strjoina("/usr/share/zoneinfo/", name
);
1074 if (stat(t
, &st
) < 0)
1077 if (!S_ISREG(st
.st_mode
))
1083 clockid_t
clock_boottime_or_monotonic(void) {
1084 static clockid_t clock
= -1;
1090 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1092 clock
= CLOCK_MONOTONIC
;
1095 clock
= CLOCK_BOOTTIME
;
1101 int get_timezone(char **tz
) {
1102 _cleanup_free_
char *t
= NULL
;
1107 r
= readlink_malloc("/etc/localtime", &t
);
1109 return r
; /* returns EINVAL if not a symlink */
1111 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1113 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1117 if (!timezone_is_valid(e
))
1128 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1129 return utc
? timegm(tm
) : mktime(tm
);
1132 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1133 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1136 unsigned long usec_to_jiffies(usec_t u
) {
1137 static thread_local
unsigned long hz
= 0;
1141 r
= sysconf(_SC_CLK_TCK
);
1144 hz
= (unsigned long) r
;
1147 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);