]>
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/>.
24 #include <sys/timex.h>
25 #include <sys/timerfd.h>
28 #include "time-util.h"
29 #include "path-util.h"
32 usec_t
now(clockid_t clock_id
) {
35 assert_se(clock_gettime(clock_id
, &ts
) == 0);
37 return timespec_load(&ts
);
40 nsec_t
now_nsec(clockid_t clock_id
) {
43 assert_se(clock_gettime(clock_id
, &ts
) == 0);
45 return timespec_load_nsec(&ts
);
48 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
51 ts
->realtime
= now(CLOCK_REALTIME
);
52 ts
->monotonic
= now(CLOCK_MONOTONIC
);
57 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
61 if (u
== USEC_INFINITY
|| u
<= 0) {
62 ts
->realtime
= ts
->monotonic
= u
;
68 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
69 ts
->monotonic
= now(CLOCK_MONOTONIC
);
71 if ((int64_t) ts
->monotonic
> delta
)
72 ts
->monotonic
-= delta
;
79 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
83 if (u
== USEC_INFINITY
) {
84 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
89 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
91 ts
->realtime
= now(CLOCK_REALTIME
);
92 if ((int64_t) ts
->realtime
> delta
)
93 ts
->realtime
-= delta
;
100 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
103 if (u
== USEC_INFINITY
) {
104 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
107 ts
->realtime
= now(CLOCK_REALTIME
);
108 ts
->monotonic
= now(CLOCK_MONOTONIC
);
110 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
112 if ((int64_t) ts
->realtime
> delta
)
113 ts
->realtime
-= delta
;
117 if ((int64_t) ts
->monotonic
> delta
)
118 ts
->monotonic
-= delta
;
126 usec_t
timespec_load(const struct timespec
*ts
) {
129 if (ts
->tv_sec
== (time_t) -1 &&
130 ts
->tv_nsec
== (long) -1)
131 return USEC_INFINITY
;
133 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
134 return USEC_INFINITY
;
137 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
138 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
141 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
144 if (ts
->tv_sec
== (time_t) -1 &&
145 ts
->tv_nsec
== (long) -1)
146 return NSEC_INFINITY
;
149 (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+
150 (nsec_t
) ts
->tv_nsec
;
153 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
156 if (u
== USEC_INFINITY
) {
157 ts
->tv_sec
= (time_t) -1;
158 ts
->tv_nsec
= (long) -1;
162 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
163 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
168 usec_t
timeval_load(const struct timeval
*tv
) {
171 if (tv
->tv_sec
== (time_t) -1 &&
172 tv
->tv_usec
== (suseconds_t
) -1)
173 return USEC_INFINITY
;
175 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
176 return USEC_INFINITY
;
179 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
180 (usec_t
) tv
->tv_usec
;
183 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
186 if (u
== USEC_INFINITY
) {
187 tv
->tv_sec
= (time_t) -1;
188 tv
->tv_usec
= (suseconds_t
) -1;
190 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
191 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
197 static char *format_timestamp_internal(char *buf
, size_t l
, usec_t t
, bool utc
) {
204 if (t
<= 0 || t
== USEC_INFINITY
)
207 sec
= (time_t) (t
/ USEC_PER_SEC
);
212 localtime_r(&sec
, &tm
);
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
);
241 localtime_r(&sec
, &tm
);
243 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
245 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
246 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
252 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
253 return format_timestamp_internal_us(buf
, l
, t
, false);
256 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
257 return format_timestamp_internal_us(buf
, l
, t
, true);
260 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
264 if (t
<= 0 || t
== USEC_INFINITY
)
267 n
= now(CLOCK_REALTIME
);
276 if (d
>= USEC_PER_YEAR
)
277 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
279 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
280 else if (d
>= USEC_PER_MONTH
)
281 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
283 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
284 else if (d
>= USEC_PER_WEEK
)
285 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
287 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
288 else if (d
>= 2*USEC_PER_DAY
)
289 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
290 else if (d
>= 25*USEC_PER_HOUR
)
291 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
292 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
293 else if (d
>= 6*USEC_PER_HOUR
)
294 snprintf(buf
, l
, USEC_FMT
"h %s",
295 d
/ USEC_PER_HOUR
, s
);
296 else if (d
>= USEC_PER_HOUR
)
297 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
299 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
300 else if (d
>= 5*USEC_PER_MINUTE
)
301 snprintf(buf
, l
, USEC_FMT
"min %s",
302 d
/ USEC_PER_MINUTE
, s
);
303 else if (d
>= USEC_PER_MINUTE
)
304 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
306 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
307 else if (d
>= USEC_PER_SEC
)
308 snprintf(buf
, l
, USEC_FMT
"s %s",
309 d
/ USEC_PER_SEC
, s
);
310 else if (d
>= USEC_PER_MSEC
)
311 snprintf(buf
, l
, USEC_FMT
"ms %s",
312 d
/ USEC_PER_MSEC
, s
);
314 snprintf(buf
, l
, USEC_FMT
"us %s",
317 snprintf(buf
, l
, "now");
323 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
324 static const struct {
328 { "y", USEC_PER_YEAR
},
329 { "month", USEC_PER_MONTH
},
330 { "w", USEC_PER_WEEK
},
331 { "d", USEC_PER_DAY
},
332 { "h", USEC_PER_HOUR
},
333 { "min", USEC_PER_MINUTE
},
334 { "s", USEC_PER_SEC
},
335 { "ms", USEC_PER_MSEC
},
341 bool something
= false;
346 if (t
== USEC_INFINITY
) {
347 strncpy(p
, "infinity", l
-1);
353 strncpy(p
, "0", l
-1);
358 /* The result of this function can be parsed with parse_sec */
360 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
369 if (t
< accuracy
&& something
)
372 if (t
< table
[i
].usec
)
378 a
= t
/ table
[i
].usec
;
379 b
= t
% table
[i
].usec
;
381 /* Let's see if we should shows this in dot notation */
382 if (t
< USEC_PER_MINUTE
&& b
> 0) {
387 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
390 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
397 "%s"USEC_FMT
".%0*llu%s",
401 (unsigned long long) b
,
409 /* No? Then let's show it normally */
420 n
= MIN((size_t) k
, l
);
433 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
439 if (!dual_timestamp_is_set(t
))
442 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
448 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
449 unsigned long long a
, b
;
454 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
455 log_debug("Failed to parse finish timestamp value %s.", value
);
465 int parse_timestamp(const char *t
, usec_t
*usec
) {
466 static const struct {
489 usec_t plus
= 0, minus
= 0, ret
;
496 * 2012-09-22 16:34:22
497 * 2012-09-22 16:34 (seconds will be set to 0)
498 * 2012-09-22 (time will be set to 00:00:00)
499 * 16:34:22 (date will be set to today)
500 * 16:34 (date will be set to today, seconds to 0)
502 * yesterday (time is set to 00:00:00)
503 * today (time is set to 00:00:00)
504 * tomorrow (time is set to 00:00:00)
507 * @2147483647 (seconds since epoch)
515 assert_se(localtime_r(&x
, &tm
));
521 else if (streq(t
, "today")) {
522 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
525 } else if (streq(t
, "yesterday")) {
527 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
530 } else if (streq(t
, "tomorrow")) {
532 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
535 } else if (t
[0] == '+') {
536 r
= parse_sec(t
+1, &plus
);
542 } else if (t
[0] == '-') {
543 r
= parse_sec(t
+1, &minus
);
549 } else if (t
[0] == '@')
550 return parse_sec(t
+ 1, usec
);
552 else if (endswith(t
, " ago")) {
553 _cleanup_free_
char *z
;
555 z
= strndup(t
, strlen(t
) - 4);
559 r
= parse_sec(z
, &minus
);
564 } else if (endswith(t
, " left")) {
565 _cleanup_free_
char *z
;
567 z
= strndup(t
, strlen(t
) - 4);
571 r
= parse_sec(z
, &plus
);
578 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
581 if (!startswith_no_case(t
, day_nr
[i
].name
))
584 skip
= strlen(day_nr
[i
].name
);
588 weekday
= day_nr
[i
].nr
;
594 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
599 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
604 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
611 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
618 k
= strptime(t
, "%y-%m-%d", &tm
);
620 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
625 k
= strptime(t
, "%Y-%m-%d", &tm
);
627 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
632 k
= strptime(t
, "%H:%M:%S", &tm
);
637 k
= strptime(t
, "%H:%M", &tm
);
647 if (x
== (time_t) -1)
650 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
653 ret
= (usec_t
) x
* USEC_PER_SEC
;
666 int parse_sec(const char *t
, usec_t
*usec
) {
667 static const struct {
671 { "seconds", USEC_PER_SEC
},
672 { "second", USEC_PER_SEC
},
673 { "sec", USEC_PER_SEC
},
674 { "s", USEC_PER_SEC
},
675 { "minutes", USEC_PER_MINUTE
},
676 { "minute", USEC_PER_MINUTE
},
677 { "min", USEC_PER_MINUTE
},
678 { "months", USEC_PER_MONTH
},
679 { "month", USEC_PER_MONTH
},
680 { "msec", USEC_PER_MSEC
},
681 { "ms", USEC_PER_MSEC
},
682 { "m", USEC_PER_MINUTE
},
683 { "hours", USEC_PER_HOUR
},
684 { "hour", USEC_PER_HOUR
},
685 { "hr", USEC_PER_HOUR
},
686 { "h", USEC_PER_HOUR
},
687 { "days", USEC_PER_DAY
},
688 { "day", USEC_PER_DAY
},
689 { "d", USEC_PER_DAY
},
690 { "weeks", USEC_PER_WEEK
},
691 { "week", USEC_PER_WEEK
},
692 { "w", USEC_PER_WEEK
},
693 { "years", USEC_PER_YEAR
},
694 { "year", USEC_PER_YEAR
},
695 { "y", USEC_PER_YEAR
},
698 { "", USEC_PER_SEC
}, /* default is sec */
703 bool something
= false;
710 p
+= strspn(p
, WHITESPACE
);
711 s
= startswith(p
, "infinity");
713 s
+= strspn(s
, WHITESPACE
);
717 *usec
= USEC_INFINITY
;
726 p
+= strspn(p
, WHITESPACE
);
736 l
= strtoll(p
, &e
, 10);
748 z
= strtoll(b
, &e
, 10);
763 e
+= strspn(e
, WHITESPACE
);
765 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
766 if (startswith(e
, table
[i
].suffix
)) {
767 usec_t k
= (usec_t
) z
* table
[i
].usec
;
772 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
773 p
= e
+ strlen(table
[i
].suffix
);
779 if (i
>= ELEMENTSOF(table
))
789 int parse_nsec(const char *t
, nsec_t
*nsec
) {
790 static const struct {
794 { "seconds", NSEC_PER_SEC
},
795 { "second", NSEC_PER_SEC
},
796 { "sec", NSEC_PER_SEC
},
797 { "s", NSEC_PER_SEC
},
798 { "minutes", NSEC_PER_MINUTE
},
799 { "minute", NSEC_PER_MINUTE
},
800 { "min", NSEC_PER_MINUTE
},
801 { "months", NSEC_PER_MONTH
},
802 { "month", NSEC_PER_MONTH
},
803 { "msec", NSEC_PER_MSEC
},
804 { "ms", NSEC_PER_MSEC
},
805 { "m", NSEC_PER_MINUTE
},
806 { "hours", NSEC_PER_HOUR
},
807 { "hour", NSEC_PER_HOUR
},
808 { "hr", NSEC_PER_HOUR
},
809 { "h", NSEC_PER_HOUR
},
810 { "days", NSEC_PER_DAY
},
811 { "day", NSEC_PER_DAY
},
812 { "d", NSEC_PER_DAY
},
813 { "weeks", NSEC_PER_WEEK
},
814 { "week", NSEC_PER_WEEK
},
815 { "w", NSEC_PER_WEEK
},
816 { "years", NSEC_PER_YEAR
},
817 { "year", NSEC_PER_YEAR
},
818 { "y", NSEC_PER_YEAR
},
819 { "usec", NSEC_PER_USEC
},
820 { "us", NSEC_PER_USEC
},
823 { "", 1ULL }, /* default is nsec */
828 bool something
= false;
835 p
+= strspn(p
, WHITESPACE
);
836 s
= startswith(p
, "infinity");
838 s
+= strspn(s
, WHITESPACE
);
842 *nsec
= NSEC_INFINITY
;
851 p
+= strspn(p
, WHITESPACE
);
861 l
= strtoll(p
, &e
, 10);
873 z
= strtoll(b
, &e
, 10);
888 e
+= strspn(e
, WHITESPACE
);
890 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
891 if (startswith(e
, table
[i
].suffix
)) {
892 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
897 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
898 p
= e
+ strlen(table
[i
].suffix
);
904 if (i
>= ELEMENTSOF(table
))
914 bool ntp_synced(void) {
915 struct timex txc
= {};
917 if (adjtimex(&txc
) < 0)
920 if (txc
.status
& STA_UNSYNC
)
926 int get_timezones(char ***ret
) {
927 _cleanup_fclose_
FILE *f
= NULL
;
928 _cleanup_strv_free_
char **zones
= NULL
;
929 size_t n_zones
= 0, n_allocated
= 0;
933 zones
= strv_new("UTC", NULL
);
940 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
944 FOREACH_LINE(l
, f
, return -errno
) {
950 if (isempty(p
) || *p
== '#')
953 /* Skip over country code */
954 p
+= strcspn(p
, WHITESPACE
);
955 p
+= strspn(p
, WHITESPACE
);
957 /* Skip over coordinates */
958 p
+= strcspn(p
, WHITESPACE
);
959 p
+= strspn(p
, WHITESPACE
);
961 /* Found timezone name */
962 k
= strcspn(p
, WHITESPACE
);
970 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
975 zones
[n_zones
++] = w
;
976 zones
[n_zones
] = NULL
;
981 } else if (errno
!= ENOENT
)
990 bool timezone_is_valid(const char *name
) {
1001 for (p
= name
; *p
; p
++) {
1002 if (!(*p
>= '0' && *p
<= '9') &&
1003 !(*p
>= 'a' && *p
<= 'z') &&
1004 !(*p
>= 'A' && *p
<= 'Z') &&
1005 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1021 t
= strjoina("/usr/share/zoneinfo/", name
);
1022 if (stat(t
, &st
) < 0)
1025 if (!S_ISREG(st
.st_mode
))
1031 clockid_t
clock_boottime_or_monotonic(void) {
1032 static clockid_t clock
= -1;
1038 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1040 clock
= CLOCK_MONOTONIC
;
1043 clock
= CLOCK_BOOTTIME
;
1049 int get_timezone(char **timezone
) {
1050 _cleanup_free_
char *t
= NULL
;
1055 r
= readlink_malloc("/etc/localtime", &t
);
1057 return r
; /* returns EINVAL if not a symlink */
1059 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1061 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1065 if (!timezone_is_valid(e
))