]>
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>
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_sec(const char *t
, usec_t
*usec
) {
709 static const struct {
713 { "seconds", USEC_PER_SEC
},
714 { "second", USEC_PER_SEC
},
715 { "sec", USEC_PER_SEC
},
716 { "s", USEC_PER_SEC
},
717 { "minutes", USEC_PER_MINUTE
},
718 { "minute", USEC_PER_MINUTE
},
719 { "min", USEC_PER_MINUTE
},
720 { "months", USEC_PER_MONTH
},
721 { "month", 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
},
740 { "", USEC_PER_SEC
}, /* default is sec */
745 bool something
= false;
752 p
+= strspn(p
, WHITESPACE
);
753 s
= startswith(p
, "infinity");
755 s
+= strspn(s
, WHITESPACE
);
759 *usec
= USEC_INFINITY
;
768 p
+= strspn(p
, WHITESPACE
);
778 l
= strtoll(p
, &e
, 10);
790 z
= strtoll(b
, &e
, 10);
805 e
+= strspn(e
, WHITESPACE
);
807 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
808 if (startswith(e
, table
[i
].suffix
)) {
809 usec_t k
= (usec_t
) z
* table
[i
].usec
;
814 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
815 p
= e
+ strlen(table
[i
].suffix
);
821 if (i
>= ELEMENTSOF(table
))
831 int parse_nsec(const char *t
, nsec_t
*nsec
) {
832 static const struct {
836 { "seconds", NSEC_PER_SEC
},
837 { "second", NSEC_PER_SEC
},
838 { "sec", NSEC_PER_SEC
},
839 { "s", NSEC_PER_SEC
},
840 { "minutes", NSEC_PER_MINUTE
},
841 { "minute", NSEC_PER_MINUTE
},
842 { "min", NSEC_PER_MINUTE
},
843 { "months", NSEC_PER_MONTH
},
844 { "month", NSEC_PER_MONTH
},
845 { "msec", NSEC_PER_MSEC
},
846 { "ms", NSEC_PER_MSEC
},
847 { "m", NSEC_PER_MINUTE
},
848 { "hours", NSEC_PER_HOUR
},
849 { "hour", NSEC_PER_HOUR
},
850 { "hr", NSEC_PER_HOUR
},
851 { "h", NSEC_PER_HOUR
},
852 { "days", NSEC_PER_DAY
},
853 { "day", NSEC_PER_DAY
},
854 { "d", NSEC_PER_DAY
},
855 { "weeks", NSEC_PER_WEEK
},
856 { "week", NSEC_PER_WEEK
},
857 { "w", NSEC_PER_WEEK
},
858 { "years", NSEC_PER_YEAR
},
859 { "year", NSEC_PER_YEAR
},
860 { "y", NSEC_PER_YEAR
},
861 { "usec", NSEC_PER_USEC
},
862 { "us", NSEC_PER_USEC
},
865 { "", 1ULL }, /* default is nsec */
870 bool something
= false;
877 p
+= strspn(p
, WHITESPACE
);
878 s
= startswith(p
, "infinity");
880 s
+= strspn(s
, WHITESPACE
);
884 *nsec
= NSEC_INFINITY
;
893 p
+= strspn(p
, WHITESPACE
);
903 l
= strtoll(p
, &e
, 10);
915 z
= strtoll(b
, &e
, 10);
930 e
+= strspn(e
, WHITESPACE
);
932 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
933 if (startswith(e
, table
[i
].suffix
)) {
934 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
939 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
940 p
= e
+ strlen(table
[i
].suffix
);
946 if (i
>= ELEMENTSOF(table
))
956 bool ntp_synced(void) {
957 struct timex txc
= {};
959 if (adjtimex(&txc
) < 0)
962 if (txc
.status
& STA_UNSYNC
)
968 int get_timezones(char ***ret
) {
969 _cleanup_fclose_
FILE *f
= NULL
;
970 _cleanup_strv_free_
char **zones
= NULL
;
971 size_t n_zones
= 0, n_allocated
= 0;
975 zones
= strv_new("UTC", NULL
);
982 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
986 FOREACH_LINE(l
, f
, return -errno
) {
992 if (isempty(p
) || *p
== '#')
995 /* Skip over country code */
996 p
+= strcspn(p
, WHITESPACE
);
997 p
+= strspn(p
, WHITESPACE
);
999 /* Skip over coordinates */
1000 p
+= strcspn(p
, WHITESPACE
);
1001 p
+= strspn(p
, WHITESPACE
);
1003 /* Found timezone name */
1004 k
= strcspn(p
, WHITESPACE
);
1012 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1017 zones
[n_zones
++] = w
;
1018 zones
[n_zones
] = NULL
;
1023 } else if (errno
!= ENOENT
)
1032 bool timezone_is_valid(const char *name
) {
1043 for (p
= name
; *p
; p
++) {
1044 if (!(*p
>= '0' && *p
<= '9') &&
1045 !(*p
>= 'a' && *p
<= 'z') &&
1046 !(*p
>= 'A' && *p
<= 'Z') &&
1047 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1063 t
= strjoina("/usr/share/zoneinfo/", name
);
1064 if (stat(t
, &st
) < 0)
1067 if (!S_ISREG(st
.st_mode
))
1073 clockid_t
clock_boottime_or_monotonic(void) {
1074 static clockid_t clock
= -1;
1080 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1082 clock
= CLOCK_MONOTONIC
;
1085 clock
= CLOCK_BOOTTIME
;
1091 int get_timezone(char **tz
) {
1092 _cleanup_free_
char *t
= NULL
;
1097 r
= readlink_malloc("/etc/localtime", &t
);
1099 return r
; /* returns EINVAL if not a symlink */
1101 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1103 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1107 if (!timezone_is_valid(e
))
1118 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1119 return utc
? timegm(tm
) : mktime(tm
);
1122 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1123 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1126 unsigned long usec_to_jiffies(usec_t u
) {
1127 static thread_local
unsigned long hz
= 0;
1131 r
= sysconf(_SC_CLK_TCK
);
1134 hz
= (unsigned long) r
;
1137 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);