]>
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 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
43 ts
->realtime
= now(CLOCK_REALTIME
);
44 ts
->monotonic
= now(CLOCK_MONOTONIC
);
49 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
53 if (u
== USEC_INFINITY
|| u
<= 0) {
54 ts
->realtime
= ts
->monotonic
= u
;
60 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
61 ts
->monotonic
= now(CLOCK_MONOTONIC
);
63 if ((int64_t) ts
->monotonic
> delta
)
64 ts
->monotonic
-= delta
;
71 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
75 if (u
== USEC_INFINITY
) {
76 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
81 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
83 ts
->realtime
= now(CLOCK_REALTIME
);
84 if ((int64_t) ts
->realtime
> delta
)
85 ts
->realtime
-= delta
;
92 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
95 if (u
== USEC_INFINITY
) {
96 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
99 ts
->realtime
= now(CLOCK_REALTIME
);
100 ts
->monotonic
= now(CLOCK_MONOTONIC
);
102 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
104 if ((int64_t) ts
->realtime
> delta
)
105 ts
->realtime
-= delta
;
109 if ((int64_t) ts
->monotonic
> delta
)
110 ts
->monotonic
-= delta
;
118 usec_t
timespec_load(const struct timespec
*ts
) {
121 if (ts
->tv_sec
== (time_t) -1 &&
122 ts
->tv_nsec
== (long) -1)
123 return USEC_INFINITY
;
125 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
126 return USEC_INFINITY
;
129 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
130 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
133 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
136 if (u
== USEC_INFINITY
) {
137 ts
->tv_sec
= (time_t) -1;
138 ts
->tv_nsec
= (long) -1;
142 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
143 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
148 usec_t
timeval_load(const struct timeval
*tv
) {
151 if (tv
->tv_sec
== (time_t) -1 &&
152 tv
->tv_usec
== (suseconds_t
) -1)
153 return USEC_INFINITY
;
155 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
156 return USEC_INFINITY
;
159 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
160 (usec_t
) tv
->tv_usec
;
163 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
166 if (u
== USEC_INFINITY
) {
167 tv
->tv_sec
= (time_t) -1;
168 tv
->tv_usec
= (suseconds_t
) -1;
170 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
171 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
177 static char *format_timestamp_internal(char *buf
, size_t l
, usec_t t
, bool utc
) {
184 if (t
<= 0 || t
== USEC_INFINITY
)
187 sec
= (time_t) (t
/ USEC_PER_SEC
);
192 localtime_r(&sec
, &tm
);
193 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", &tm
) <= 0)
199 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
200 return format_timestamp_internal(buf
, l
, t
, false);
203 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
204 return format_timestamp_internal(buf
, l
, t
, true);
207 static char *format_timestamp_internal_us(char *buf
, size_t l
, usec_t t
, bool utc
) {
214 if (t
<= 0 || t
== USEC_INFINITY
)
217 sec
= (time_t) (t
/ USEC_PER_SEC
);
221 localtime_r(&sec
, &tm
);
223 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
225 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
226 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
232 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
233 return format_timestamp_internal_us(buf
, l
, t
, false);
236 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
237 return format_timestamp_internal_us(buf
, l
, t
, true);
240 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
244 if (t
<= 0 || t
== USEC_INFINITY
)
247 n
= now(CLOCK_REALTIME
);
256 if (d
>= USEC_PER_YEAR
)
257 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
259 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
260 else if (d
>= USEC_PER_MONTH
)
261 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
263 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
264 else if (d
>= USEC_PER_WEEK
)
265 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
267 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
268 else if (d
>= 2*USEC_PER_DAY
)
269 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
270 else if (d
>= 25*USEC_PER_HOUR
)
271 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
272 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
273 else if (d
>= 6*USEC_PER_HOUR
)
274 snprintf(buf
, l
, USEC_FMT
"h %s",
275 d
/ USEC_PER_HOUR
, s
);
276 else if (d
>= USEC_PER_HOUR
)
277 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
279 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
280 else if (d
>= 5*USEC_PER_MINUTE
)
281 snprintf(buf
, l
, USEC_FMT
"min %s",
282 d
/ USEC_PER_MINUTE
, s
);
283 else if (d
>= USEC_PER_MINUTE
)
284 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
286 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
287 else if (d
>= USEC_PER_SEC
)
288 snprintf(buf
, l
, USEC_FMT
"s %s",
289 d
/ USEC_PER_SEC
, s
);
290 else if (d
>= USEC_PER_MSEC
)
291 snprintf(buf
, l
, USEC_FMT
"ms %s",
292 d
/ USEC_PER_MSEC
, s
);
294 snprintf(buf
, l
, USEC_FMT
"us %s",
297 snprintf(buf
, l
, "now");
303 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
304 static const struct {
308 { "y", USEC_PER_YEAR
},
309 { "month", USEC_PER_MONTH
},
310 { "w", USEC_PER_WEEK
},
311 { "d", USEC_PER_DAY
},
312 { "h", USEC_PER_HOUR
},
313 { "min", USEC_PER_MINUTE
},
314 { "s", USEC_PER_SEC
},
315 { "ms", USEC_PER_MSEC
},
321 bool something
= false;
326 if (t
== USEC_INFINITY
) {
327 strncpy(p
, "infinity", l
-1);
333 strncpy(p
, "0", l
-1);
338 /* The result of this function can be parsed with parse_sec */
340 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
349 if (t
< accuracy
&& something
)
352 if (t
< table
[i
].usec
)
358 a
= t
/ table
[i
].usec
;
359 b
= t
% table
[i
].usec
;
361 /* Let's see if we should shows this in dot notation */
362 if (t
< USEC_PER_MINUTE
&& b
> 0) {
367 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
370 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
377 "%s"USEC_FMT
".%0*llu%s",
381 (unsigned long long) b
,
389 /* No? Then let's show it normally */
400 n
= MIN((size_t) k
, l
);
413 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
419 if (!dual_timestamp_is_set(t
))
422 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
428 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
429 unsigned long long a
, b
;
434 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
435 log_debug("Failed to parse finish timestamp value %s.", value
);
445 int parse_timestamp(const char *t
, usec_t
*usec
) {
446 static const struct {
469 usec_t plus
= 0, minus
= 0, ret
;
476 * 2012-09-22 16:34:22
477 * 2012-09-22 16:34 (seconds will be set to 0)
478 * 2012-09-22 (time will be set to 00:00:00)
479 * 16:34:22 (date will be set to today)
480 * 16:34 (date will be set to today, seconds to 0)
482 * yesterday (time is set to 00:00:00)
483 * today (time is set to 00:00:00)
484 * tomorrow (time is set to 00:00:00)
487 * @2147483647 (seconds since epoch)
495 assert_se(localtime_r(&x
, &tm
));
501 else if (streq(t
, "today")) {
502 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
505 } else if (streq(t
, "yesterday")) {
507 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
510 } else if (streq(t
, "tomorrow")) {
512 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
515 } else if (t
[0] == '+') {
516 r
= parse_sec(t
+1, &plus
);
522 } else if (t
[0] == '-') {
523 r
= parse_sec(t
+1, &minus
);
529 } else if (t
[0] == '@')
530 return parse_sec(t
+ 1, usec
);
532 else if (endswith(t
, " ago")) {
533 _cleanup_free_
char *z
;
535 z
= strndup(t
, strlen(t
) - 4);
539 r
= parse_sec(z
, &minus
);
544 } else if (endswith(t
, " left")) {
545 _cleanup_free_
char *z
;
547 z
= strndup(t
, strlen(t
) - 4);
551 r
= parse_sec(z
, &plus
);
558 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
561 if (!startswith_no_case(t
, day_nr
[i
].name
))
564 skip
= strlen(day_nr
[i
].name
);
568 weekday
= day_nr
[i
].nr
;
574 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
579 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
584 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
591 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
598 k
= strptime(t
, "%y-%m-%d", &tm
);
600 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
605 k
= strptime(t
, "%Y-%m-%d", &tm
);
607 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
612 k
= strptime(t
, "%H:%M:%S", &tm
);
617 k
= strptime(t
, "%H:%M", &tm
);
627 if (x
== (time_t) -1)
630 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
633 ret
= (usec_t
) x
* USEC_PER_SEC
;
646 int parse_sec(const char *t
, usec_t
*usec
) {
647 static const struct {
651 { "seconds", USEC_PER_SEC
},
652 { "second", USEC_PER_SEC
},
653 { "sec", USEC_PER_SEC
},
654 { "s", USEC_PER_SEC
},
655 { "minutes", USEC_PER_MINUTE
},
656 { "minute", USEC_PER_MINUTE
},
657 { "min", USEC_PER_MINUTE
},
658 { "months", USEC_PER_MONTH
},
659 { "month", USEC_PER_MONTH
},
660 { "msec", USEC_PER_MSEC
},
661 { "ms", USEC_PER_MSEC
},
662 { "m", USEC_PER_MINUTE
},
663 { "hours", USEC_PER_HOUR
},
664 { "hour", USEC_PER_HOUR
},
665 { "hr", USEC_PER_HOUR
},
666 { "h", USEC_PER_HOUR
},
667 { "days", USEC_PER_DAY
},
668 { "day", USEC_PER_DAY
},
669 { "d", USEC_PER_DAY
},
670 { "weeks", USEC_PER_WEEK
},
671 { "week", USEC_PER_WEEK
},
672 { "w", USEC_PER_WEEK
},
673 { "years", USEC_PER_YEAR
},
674 { "year", USEC_PER_YEAR
},
675 { "y", USEC_PER_YEAR
},
678 { "", USEC_PER_SEC
}, /* default is sec */
683 bool something
= false;
690 p
+= strspn(p
, WHITESPACE
);
691 s
= startswith(p
, "infinity");
693 s
+= strspn(s
, WHITESPACE
);
697 *usec
= USEC_INFINITY
;
706 p
+= strspn(p
, WHITESPACE
);
716 l
= strtoll(p
, &e
, 10);
728 z
= strtoll(b
, &e
, 10);
743 e
+= strspn(e
, WHITESPACE
);
745 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
746 if (startswith(e
, table
[i
].suffix
)) {
747 usec_t k
= (usec_t
) z
* table
[i
].usec
;
752 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
753 p
= e
+ strlen(table
[i
].suffix
);
759 if (i
>= ELEMENTSOF(table
))
769 int parse_nsec(const char *t
, nsec_t
*nsec
) {
770 static const struct {
774 { "seconds", NSEC_PER_SEC
},
775 { "second", NSEC_PER_SEC
},
776 { "sec", NSEC_PER_SEC
},
777 { "s", NSEC_PER_SEC
},
778 { "minutes", NSEC_PER_MINUTE
},
779 { "minute", NSEC_PER_MINUTE
},
780 { "min", NSEC_PER_MINUTE
},
781 { "months", NSEC_PER_MONTH
},
782 { "month", NSEC_PER_MONTH
},
783 { "msec", NSEC_PER_MSEC
},
784 { "ms", NSEC_PER_MSEC
},
785 { "m", NSEC_PER_MINUTE
},
786 { "hours", NSEC_PER_HOUR
},
787 { "hour", NSEC_PER_HOUR
},
788 { "hr", NSEC_PER_HOUR
},
789 { "h", NSEC_PER_HOUR
},
790 { "days", NSEC_PER_DAY
},
791 { "day", NSEC_PER_DAY
},
792 { "d", NSEC_PER_DAY
},
793 { "weeks", NSEC_PER_WEEK
},
794 { "week", NSEC_PER_WEEK
},
795 { "w", NSEC_PER_WEEK
},
796 { "years", NSEC_PER_YEAR
},
797 { "year", NSEC_PER_YEAR
},
798 { "y", NSEC_PER_YEAR
},
799 { "usec", NSEC_PER_USEC
},
800 { "us", NSEC_PER_USEC
},
803 { "", 1ULL }, /* default is nsec */
808 bool something
= false;
815 p
+= strspn(p
, WHITESPACE
);
816 s
= startswith(p
, "infinity");
818 s
+= strspn(s
, WHITESPACE
);
822 *nsec
= NSEC_INFINITY
;
831 p
+= strspn(p
, WHITESPACE
);
841 l
= strtoll(p
, &e
, 10);
853 z
= strtoll(b
, &e
, 10);
868 e
+= strspn(e
, WHITESPACE
);
870 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
871 if (startswith(e
, table
[i
].suffix
)) {
872 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
877 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
878 p
= e
+ strlen(table
[i
].suffix
);
884 if (i
>= ELEMENTSOF(table
))
894 bool ntp_synced(void) {
895 struct timex txc
= {};
897 if (adjtimex(&txc
) < 0)
900 if (txc
.status
& STA_UNSYNC
)
906 int get_timezones(char ***ret
) {
907 _cleanup_fclose_
FILE *f
= NULL
;
908 _cleanup_strv_free_
char **zones
= NULL
;
909 size_t n_zones
= 0, n_allocated
= 0;
913 zones
= strv_new("UTC", NULL
);
920 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
924 FOREACH_LINE(l
, f
, return -errno
) {
930 if (isempty(p
) || *p
== '#')
933 /* Skip over country code */
934 p
+= strcspn(p
, WHITESPACE
);
935 p
+= strspn(p
, WHITESPACE
);
937 /* Skip over coordinates */
938 p
+= strcspn(p
, WHITESPACE
);
939 p
+= strspn(p
, WHITESPACE
);
941 /* Found timezone name */
942 k
= strcspn(p
, WHITESPACE
);
950 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
955 zones
[n_zones
++] = w
;
956 zones
[n_zones
] = NULL
;
961 } else if (errno
!= ENOENT
)
970 bool timezone_is_valid(const char *name
) {
981 for (p
= name
; *p
; p
++) {
982 if (!(*p
>= '0' && *p
<= '9') &&
983 !(*p
>= 'a' && *p
<= 'z') &&
984 !(*p
>= 'A' && *p
<= 'Z') &&
985 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1001 t
= strjoina("/usr/share/zoneinfo/", name
);
1002 if (stat(t
, &st
) < 0)
1005 if (!S_ISREG(st
.st_mode
))
1011 clockid_t
clock_boottime_or_monotonic(void) {
1012 static clockid_t clock
= -1;
1018 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1020 clock
= CLOCK_MONOTONIC
;
1023 clock
= CLOCK_BOOTTIME
;
1029 int get_timezone(char **timezone
) {
1030 _cleanup_free_
char *t
= NULL
;
1035 r
= readlink_malloc("/etc/localtime", &t
);
1037 return r
; /* returns EINVAL if not a symlink */
1039 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1041 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1045 if (!timezone_is_valid(e
))