]>
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"
31 usec_t
now(clockid_t clock_id
) {
34 assert_se(clock_gettime(clock_id
, &ts
) == 0);
36 return timespec_load(&ts
);
39 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
42 ts
->realtime
= now(CLOCK_REALTIME
);
43 ts
->monotonic
= now(CLOCK_MONOTONIC
);
48 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
52 if (u
== USEC_INFINITY
|| u
<= 0) {
53 ts
->realtime
= ts
->monotonic
= u
;
59 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
60 ts
->monotonic
= now(CLOCK_MONOTONIC
);
62 if ((int64_t) ts
->monotonic
> delta
)
63 ts
->monotonic
-= delta
;
70 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
74 if (u
== USEC_INFINITY
) {
75 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
80 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
82 ts
->realtime
= now(CLOCK_REALTIME
);
83 if ((int64_t) ts
->realtime
> delta
)
84 ts
->realtime
-= delta
;
91 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
94 if (u
== USEC_INFINITY
) {
95 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
98 ts
->realtime
= now(CLOCK_REALTIME
);
99 ts
->monotonic
= now(CLOCK_MONOTONIC
);
101 delta
= (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u
;
103 if ((int64_t) ts
->realtime
> delta
)
104 ts
->realtime
-= delta
;
108 if ((int64_t) ts
->monotonic
> delta
)
109 ts
->monotonic
-= delta
;
117 usec_t
timespec_load(const struct timespec
*ts
) {
120 if (ts
->tv_sec
== (time_t) -1 &&
121 ts
->tv_nsec
== (long) -1)
122 return USEC_INFINITY
;
124 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
125 return USEC_INFINITY
;
128 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
129 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
132 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
135 if (u
== USEC_INFINITY
) {
136 ts
->tv_sec
= (time_t) -1;
137 ts
->tv_nsec
= (long) -1;
141 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
142 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
147 usec_t
timeval_load(const struct timeval
*tv
) {
150 if (tv
->tv_sec
== (time_t) -1 &&
151 tv
->tv_usec
== (suseconds_t
) -1)
152 return USEC_INFINITY
;
154 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
155 return USEC_INFINITY
;
158 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
159 (usec_t
) tv
->tv_usec
;
162 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
165 if (u
== USEC_INFINITY
) {
166 tv
->tv_sec
= (time_t) -1;
167 tv
->tv_usec
= (suseconds_t
) -1;
169 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
170 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
176 static char *format_timestamp_internal(char *buf
, size_t l
, usec_t t
, bool utc
) {
183 if (t
<= 0 || t
== USEC_INFINITY
)
186 sec
= (time_t) (t
/ USEC_PER_SEC
);
191 localtime_r(&sec
, &tm
);
192 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", &tm
) <= 0)
198 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
199 return format_timestamp_internal(buf
, l
, t
, false);
202 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
203 return format_timestamp_internal(buf
, l
, t
, true);
206 static char *format_timestamp_internal_us(char *buf
, size_t l
, usec_t t
, bool utc
) {
213 if (t
<= 0 || t
== USEC_INFINITY
)
216 sec
= (time_t) (t
/ USEC_PER_SEC
);
220 localtime_r(&sec
, &tm
);
222 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
224 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
225 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
231 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
232 return format_timestamp_internal_us(buf
, l
, t
, false);
235 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
236 return format_timestamp_internal_us(buf
, l
, t
, true);
239 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
243 if (t
<= 0 || t
== USEC_INFINITY
)
246 n
= now(CLOCK_REALTIME
);
255 if (d
>= USEC_PER_YEAR
)
256 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
258 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
259 else if (d
>= USEC_PER_MONTH
)
260 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
262 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
263 else if (d
>= USEC_PER_WEEK
)
264 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
266 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
267 else if (d
>= 2*USEC_PER_DAY
)
268 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
269 else if (d
>= 25*USEC_PER_HOUR
)
270 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
271 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
272 else if (d
>= 6*USEC_PER_HOUR
)
273 snprintf(buf
, l
, USEC_FMT
"h %s",
274 d
/ USEC_PER_HOUR
, s
);
275 else if (d
>= USEC_PER_HOUR
)
276 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
278 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
279 else if (d
>= 5*USEC_PER_MINUTE
)
280 snprintf(buf
, l
, USEC_FMT
"min %s",
281 d
/ USEC_PER_MINUTE
, s
);
282 else if (d
>= USEC_PER_MINUTE
)
283 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
285 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
286 else if (d
>= USEC_PER_SEC
)
287 snprintf(buf
, l
, USEC_FMT
"s %s",
288 d
/ USEC_PER_SEC
, s
);
289 else if (d
>= USEC_PER_MSEC
)
290 snprintf(buf
, l
, USEC_FMT
"ms %s",
291 d
/ USEC_PER_MSEC
, s
);
293 snprintf(buf
, l
, USEC_FMT
"us %s",
296 snprintf(buf
, l
, "now");
302 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
303 static const struct {
307 { "y", USEC_PER_YEAR
},
308 { "month", USEC_PER_MONTH
},
309 { "w", USEC_PER_WEEK
},
310 { "d", USEC_PER_DAY
},
311 { "h", USEC_PER_HOUR
},
312 { "min", USEC_PER_MINUTE
},
313 { "s", USEC_PER_SEC
},
314 { "ms", USEC_PER_MSEC
},
320 bool something
= false;
325 if (t
== USEC_INFINITY
) {
326 strncpy(p
, "infinity", l
-1);
332 strncpy(p
, "0", l
-1);
337 /* The result of this function can be parsed with parse_sec */
339 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
348 if (t
< accuracy
&& something
)
351 if (t
< table
[i
].usec
)
357 a
= t
/ table
[i
].usec
;
358 b
= t
% table
[i
].usec
;
360 /* Let's see if we should shows this in dot notation */
361 if (t
< USEC_PER_MINUTE
&& b
> 0) {
366 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
369 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
376 "%s"USEC_FMT
".%0*llu%s",
380 (unsigned long long) b
,
388 /* No? Then let's show it normally */
399 n
= MIN((size_t) k
, l
);
412 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
418 if (!dual_timestamp_is_set(t
))
421 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
427 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
428 unsigned long long a
, b
;
433 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
434 log_debug("Failed to parse finish timestamp value %s.", value
);
444 int parse_timestamp(const char *t
, usec_t
*usec
) {
445 static const struct {
468 usec_t plus
= 0, minus
= 0, ret
;
475 * 2012-09-22 16:34:22
476 * 2012-09-22 16:34 (seconds will be set to 0)
477 * 2012-09-22 (time will be set to 00:00:00)
478 * 16:34:22 (date will be set to today)
479 * 16:34 (date will be set to today, seconds to 0)
481 * yesterday (time is set to 00:00:00)
482 * today (time is set to 00:00:00)
483 * tomorrow (time is set to 00:00:00)
486 * @2147483647 (seconds since epoch)
494 assert_se(localtime_r(&x
, &tm
));
500 else if (streq(t
, "today")) {
501 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
504 } else if (streq(t
, "yesterday")) {
506 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
509 } else if (streq(t
, "tomorrow")) {
511 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
514 } else if (t
[0] == '+') {
515 r
= parse_sec(t
+1, &plus
);
521 } else if (t
[0] == '-') {
522 r
= parse_sec(t
+1, &minus
);
528 } else if (t
[0] == '@')
529 return parse_sec(t
+ 1, usec
);
531 else if (endswith(t
, " ago")) {
532 _cleanup_free_
char *z
;
534 z
= strndup(t
, strlen(t
) - 4);
538 r
= parse_sec(z
, &minus
);
543 } else if (endswith(t
, " left")) {
544 _cleanup_free_
char *z
;
546 z
= strndup(t
, strlen(t
) - 4);
550 r
= parse_sec(z
, &plus
);
557 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
560 if (!startswith_no_case(t
, day_nr
[i
].name
))
563 skip
= strlen(day_nr
[i
].name
);
567 weekday
= day_nr
[i
].nr
;
573 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
578 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
583 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
590 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
597 k
= strptime(t
, "%y-%m-%d", &tm
);
599 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
604 k
= strptime(t
, "%Y-%m-%d", &tm
);
606 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
611 k
= strptime(t
, "%H:%M:%S", &tm
);
616 k
= strptime(t
, "%H:%M", &tm
);
626 if (x
== (time_t) -1)
629 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
632 ret
= (usec_t
) x
* USEC_PER_SEC
;
645 int parse_sec(const char *t
, usec_t
*usec
) {
646 static const struct {
650 { "seconds", USEC_PER_SEC
},
651 { "second", USEC_PER_SEC
},
652 { "sec", USEC_PER_SEC
},
653 { "s", USEC_PER_SEC
},
654 { "minutes", USEC_PER_MINUTE
},
655 { "minute", USEC_PER_MINUTE
},
656 { "min", USEC_PER_MINUTE
},
657 { "months", USEC_PER_MONTH
},
658 { "month", USEC_PER_MONTH
},
659 { "msec", USEC_PER_MSEC
},
660 { "ms", USEC_PER_MSEC
},
661 { "m", USEC_PER_MINUTE
},
662 { "hours", USEC_PER_HOUR
},
663 { "hour", USEC_PER_HOUR
},
664 { "hr", USEC_PER_HOUR
},
665 { "h", USEC_PER_HOUR
},
666 { "days", USEC_PER_DAY
},
667 { "day", USEC_PER_DAY
},
668 { "d", USEC_PER_DAY
},
669 { "weeks", USEC_PER_WEEK
},
670 { "week", USEC_PER_WEEK
},
671 { "w", USEC_PER_WEEK
},
672 { "years", USEC_PER_YEAR
},
673 { "year", USEC_PER_YEAR
},
674 { "y", USEC_PER_YEAR
},
677 { "", USEC_PER_SEC
}, /* default is sec */
682 bool something
= false;
689 p
+= strspn(p
, WHITESPACE
);
690 s
= startswith(p
, "infinity");
692 s
+= strspn(s
, WHITESPACE
);
696 *usec
= USEC_INFINITY
;
705 p
+= strspn(p
, WHITESPACE
);
715 l
= strtoll(p
, &e
, 10);
727 z
= strtoll(b
, &e
, 10);
742 e
+= strspn(e
, WHITESPACE
);
744 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
745 if (startswith(e
, table
[i
].suffix
)) {
746 usec_t k
= (usec_t
) z
* table
[i
].usec
;
751 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
752 p
= e
+ strlen(table
[i
].suffix
);
758 if (i
>= ELEMENTSOF(table
))
768 int parse_nsec(const char *t
, nsec_t
*nsec
) {
769 static const struct {
773 { "seconds", NSEC_PER_SEC
},
774 { "second", NSEC_PER_SEC
},
775 { "sec", NSEC_PER_SEC
},
776 { "s", NSEC_PER_SEC
},
777 { "minutes", NSEC_PER_MINUTE
},
778 { "minute", NSEC_PER_MINUTE
},
779 { "min", NSEC_PER_MINUTE
},
780 { "months", NSEC_PER_MONTH
},
781 { "month", NSEC_PER_MONTH
},
782 { "msec", NSEC_PER_MSEC
},
783 { "ms", NSEC_PER_MSEC
},
784 { "m", NSEC_PER_MINUTE
},
785 { "hours", NSEC_PER_HOUR
},
786 { "hour", NSEC_PER_HOUR
},
787 { "hr", NSEC_PER_HOUR
},
788 { "h", NSEC_PER_HOUR
},
789 { "days", NSEC_PER_DAY
},
790 { "day", NSEC_PER_DAY
},
791 { "d", NSEC_PER_DAY
},
792 { "weeks", NSEC_PER_WEEK
},
793 { "week", NSEC_PER_WEEK
},
794 { "w", NSEC_PER_WEEK
},
795 { "years", NSEC_PER_YEAR
},
796 { "year", NSEC_PER_YEAR
},
797 { "y", NSEC_PER_YEAR
},
798 { "usec", NSEC_PER_USEC
},
799 { "us", NSEC_PER_USEC
},
802 { "", 1ULL }, /* default is nsec */
807 bool something
= false;
814 p
+= strspn(p
, WHITESPACE
);
815 s
= startswith(p
, "infinity");
817 s
+= strspn(s
, WHITESPACE
);
821 *nsec
= NSEC_INFINITY
;
830 p
+= strspn(p
, WHITESPACE
);
840 l
= strtoll(p
, &e
, 10);
852 z
= strtoll(b
, &e
, 10);
867 e
+= strspn(e
, WHITESPACE
);
869 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
870 if (startswith(e
, table
[i
].suffix
)) {
871 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
876 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
877 p
= e
+ strlen(table
[i
].suffix
);
883 if (i
>= ELEMENTSOF(table
))
893 bool ntp_synced(void) {
894 struct timex txc
= {};
896 if (adjtimex(&txc
) < 0)
899 if (txc
.status
& STA_UNSYNC
)
905 int get_timezones(char ***ret
) {
906 _cleanup_fclose_
FILE *f
= NULL
;
907 _cleanup_strv_free_
char **zones
= NULL
;
908 size_t n_zones
= 0, n_allocated
= 0;
912 zones
= strv_new("UTC", NULL
);
919 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
923 FOREACH_LINE(l
, f
, return -errno
) {
929 if (isempty(p
) || *p
== '#')
932 /* Skip over country code */
933 p
+= strcspn(p
, WHITESPACE
);
934 p
+= strspn(p
, WHITESPACE
);
936 /* Skip over coordinates */
937 p
+= strcspn(p
, WHITESPACE
);
938 p
+= strspn(p
, WHITESPACE
);
940 /* Found timezone name */
941 k
= strcspn(p
, WHITESPACE
);
949 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
954 zones
[n_zones
++] = w
;
955 zones
[n_zones
] = NULL
;
960 } else if (errno
!= ENOENT
)
969 bool timezone_is_valid(const char *name
) {
974 if (!name
|| *name
== 0 || *name
== '/')
977 for (p
= name
; *p
; p
++) {
978 if (!(*p
>= '0' && *p
<= '9') &&
979 !(*p
>= 'a' && *p
<= 'z') &&
980 !(*p
>= 'A' && *p
<= 'Z') &&
981 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
997 t
= strjoina("/usr/share/zoneinfo/", name
);
998 if (stat(t
, &st
) < 0)
1001 if (!S_ISREG(st
.st_mode
))
1007 clockid_t
clock_boottime_or_monotonic(void) {
1008 static clockid_t clock
= -1;
1014 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1016 clock
= CLOCK_MONOTONIC
;
1019 clock
= CLOCK_BOOTTIME
;