]>
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 "path-util.h"
27 #include "string-util.h"
30 #include "time-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
);
208 localtime_or_gmtime_r(&sec
, &tm
, utc
);
210 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", &tm
) <= 0)
216 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
217 return format_timestamp_internal(buf
, l
, t
, false);
220 char *format_timestamp_utc(char *buf
, size_t l
, usec_t t
) {
221 return format_timestamp_internal(buf
, l
, t
, true);
224 static char *format_timestamp_internal_us(char *buf
, size_t l
, usec_t t
, bool utc
) {
231 if (t
<= 0 || t
== USEC_INFINITY
)
234 sec
= (time_t) (t
/ USEC_PER_SEC
);
235 localtime_or_gmtime_r(&sec
, &tm
, utc
);
237 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
239 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
240 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
246 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
247 return format_timestamp_internal_us(buf
, l
, t
, false);
250 char *format_timestamp_us_utc(char *buf
, size_t l
, usec_t t
) {
251 return format_timestamp_internal_us(buf
, l
, t
, true);
254 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
258 if (t
<= 0 || t
== USEC_INFINITY
)
261 n
= now(CLOCK_REALTIME
);
270 if (d
>= USEC_PER_YEAR
)
271 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
273 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
274 else if (d
>= USEC_PER_MONTH
)
275 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
277 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
278 else if (d
>= USEC_PER_WEEK
)
279 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
281 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
282 else if (d
>= 2*USEC_PER_DAY
)
283 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
284 else if (d
>= 25*USEC_PER_HOUR
)
285 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
286 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
287 else if (d
>= 6*USEC_PER_HOUR
)
288 snprintf(buf
, l
, USEC_FMT
"h %s",
289 d
/ USEC_PER_HOUR
, s
);
290 else if (d
>= USEC_PER_HOUR
)
291 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
293 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
294 else if (d
>= 5*USEC_PER_MINUTE
)
295 snprintf(buf
, l
, USEC_FMT
"min %s",
296 d
/ USEC_PER_MINUTE
, s
);
297 else if (d
>= USEC_PER_MINUTE
)
298 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
300 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
301 else if (d
>= USEC_PER_SEC
)
302 snprintf(buf
, l
, USEC_FMT
"s %s",
303 d
/ USEC_PER_SEC
, s
);
304 else if (d
>= USEC_PER_MSEC
)
305 snprintf(buf
, l
, USEC_FMT
"ms %s",
306 d
/ USEC_PER_MSEC
, s
);
308 snprintf(buf
, l
, USEC_FMT
"us %s",
311 snprintf(buf
, l
, "now");
317 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
318 static const struct {
322 { "y", USEC_PER_YEAR
},
323 { "month", USEC_PER_MONTH
},
324 { "w", USEC_PER_WEEK
},
325 { "d", USEC_PER_DAY
},
326 { "h", USEC_PER_HOUR
},
327 { "min", USEC_PER_MINUTE
},
328 { "s", USEC_PER_SEC
},
329 { "ms", USEC_PER_MSEC
},
335 bool something
= false;
340 if (t
== USEC_INFINITY
) {
341 strncpy(p
, "infinity", l
-1);
347 strncpy(p
, "0", l
-1);
352 /* The result of this function can be parsed with parse_sec */
354 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
363 if (t
< accuracy
&& something
)
366 if (t
< table
[i
].usec
)
372 a
= t
/ table
[i
].usec
;
373 b
= t
% table
[i
].usec
;
375 /* Let's see if we should shows this in dot notation */
376 if (t
< USEC_PER_MINUTE
&& b
> 0) {
381 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
384 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
391 "%s"USEC_FMT
".%0*llu%s",
395 (unsigned long long) b
,
403 /* No? Then let's show it normally */
414 n
= MIN((size_t) k
, l
);
427 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
433 if (!dual_timestamp_is_set(t
))
436 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
442 int dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
443 unsigned long long a
, b
;
448 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2) {
449 log_debug("Failed to parse finish timestamp value %s.", value
);
459 int parse_timestamp(const char *t
, usec_t
*usec
) {
460 static const struct {
484 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
491 * 2012-09-22 16:34:22
492 * 2012-09-22 16:34 (seconds will be set to 0)
493 * 2012-09-22 (time will be set to 00:00:00)
494 * 16:34:22 (date will be set to today)
495 * 16:34 (date will be set to today, seconds to 0)
497 * yesterday (time is set to 00:00:00)
498 * today (time is set to 00:00:00)
499 * tomorrow (time is set to 00:00:00)
502 * @2147483647 (seconds since epoch)
510 return parse_sec(t
+ 1, usec
);
512 ret
= now(CLOCK_REALTIME
);
517 else if (t
[0] == '+') {
518 r
= parse_sec(t
+1, &plus
);
524 } else if (t
[0] == '-') {
525 r
= parse_sec(t
+1, &minus
);
531 } else if (endswith(t
, " ago")) {
532 t
= strndupa(t
, strlen(t
) - strlen(" ago"));
534 r
= parse_sec(t
, &minus
);
540 } else if (endswith(t
, " left")) {
541 t
= strndupa(t
, strlen(t
) - strlen(" left"));
543 r
= parse_sec(t
, &plus
);
550 utc
= endswith_no_case(t
, " UTC");
552 t
= strndupa(t
, strlen(t
) - strlen(" UTC"));
554 x
= ret
/ USEC_PER_SEC
;
557 assert_se(localtime_or_gmtime_r(&x
, &tm
, utc
));
560 if (streq(t
, "today")) {
561 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
564 } else if (streq(t
, "yesterday")) {
566 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
569 } else if (streq(t
, "tomorrow")) {
571 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
576 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
579 if (!startswith_no_case(t
, day_nr
[i
].name
))
582 skip
= strlen(day_nr
[i
].name
);
586 weekday
= day_nr
[i
].nr
;
592 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
601 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
610 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
617 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
624 k
= strptime(t
, "%y-%m-%d", &tm
);
626 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
631 k
= strptime(t
, "%Y-%m-%d", &tm
);
633 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
638 k
= strptime(t
, "%H:%M:%S", &tm
);
647 k
= strptime(t
, "%H:%M", &tm
);
658 unsigned long long val
;
662 if (*k
< '0' || *k
> '9')
665 /* base 10 instead of base 0, .09 is not base 8 */
667 val
= strtoull(k
, &end
, 10);
673 /* val has l digits, make them 6 */
683 x
= mktime_or_timegm(&tm
, utc
);
684 if (x
== (time_t) -1)
687 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
690 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
704 int parse_sec(const char *t
, usec_t
*usec
) {
705 static const struct {
709 { "seconds", USEC_PER_SEC
},
710 { "second", USEC_PER_SEC
},
711 { "sec", USEC_PER_SEC
},
712 { "s", USEC_PER_SEC
},
713 { "minutes", USEC_PER_MINUTE
},
714 { "minute", USEC_PER_MINUTE
},
715 { "min", USEC_PER_MINUTE
},
716 { "months", USEC_PER_MONTH
},
717 { "month", USEC_PER_MONTH
},
718 { "msec", USEC_PER_MSEC
},
719 { "ms", USEC_PER_MSEC
},
720 { "m", USEC_PER_MINUTE
},
721 { "hours", USEC_PER_HOUR
},
722 { "hour", USEC_PER_HOUR
},
723 { "hr", USEC_PER_HOUR
},
724 { "h", USEC_PER_HOUR
},
725 { "days", USEC_PER_DAY
},
726 { "day", USEC_PER_DAY
},
727 { "d", USEC_PER_DAY
},
728 { "weeks", USEC_PER_WEEK
},
729 { "week", USEC_PER_WEEK
},
730 { "w", USEC_PER_WEEK
},
731 { "years", USEC_PER_YEAR
},
732 { "year", USEC_PER_YEAR
},
733 { "y", USEC_PER_YEAR
},
736 { "", USEC_PER_SEC
}, /* default is sec */
741 bool something
= false;
748 p
+= strspn(p
, WHITESPACE
);
749 s
= startswith(p
, "infinity");
751 s
+= strspn(s
, WHITESPACE
);
755 *usec
= USEC_INFINITY
;
764 p
+= strspn(p
, WHITESPACE
);
774 l
= strtoll(p
, &e
, 10);
786 z
= strtoll(b
, &e
, 10);
801 e
+= strspn(e
, WHITESPACE
);
803 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
804 if (startswith(e
, table
[i
].suffix
)) {
805 usec_t k
= (usec_t
) z
* table
[i
].usec
;
810 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
811 p
= e
+ strlen(table
[i
].suffix
);
817 if (i
>= ELEMENTSOF(table
))
827 int parse_nsec(const char *t
, nsec_t
*nsec
) {
828 static const struct {
832 { "seconds", NSEC_PER_SEC
},
833 { "second", NSEC_PER_SEC
},
834 { "sec", NSEC_PER_SEC
},
835 { "s", NSEC_PER_SEC
},
836 { "minutes", NSEC_PER_MINUTE
},
837 { "minute", NSEC_PER_MINUTE
},
838 { "min", NSEC_PER_MINUTE
},
839 { "months", NSEC_PER_MONTH
},
840 { "month", NSEC_PER_MONTH
},
841 { "msec", NSEC_PER_MSEC
},
842 { "ms", NSEC_PER_MSEC
},
843 { "m", NSEC_PER_MINUTE
},
844 { "hours", NSEC_PER_HOUR
},
845 { "hour", NSEC_PER_HOUR
},
846 { "hr", NSEC_PER_HOUR
},
847 { "h", NSEC_PER_HOUR
},
848 { "days", NSEC_PER_DAY
},
849 { "day", NSEC_PER_DAY
},
850 { "d", NSEC_PER_DAY
},
851 { "weeks", NSEC_PER_WEEK
},
852 { "week", NSEC_PER_WEEK
},
853 { "w", NSEC_PER_WEEK
},
854 { "years", NSEC_PER_YEAR
},
855 { "year", NSEC_PER_YEAR
},
856 { "y", NSEC_PER_YEAR
},
857 { "usec", NSEC_PER_USEC
},
858 { "us", NSEC_PER_USEC
},
861 { "", 1ULL }, /* default is nsec */
866 bool something
= false;
873 p
+= strspn(p
, WHITESPACE
);
874 s
= startswith(p
, "infinity");
876 s
+= strspn(s
, WHITESPACE
);
880 *nsec
= NSEC_INFINITY
;
889 p
+= strspn(p
, WHITESPACE
);
899 l
= strtoll(p
, &e
, 10);
911 z
= strtoll(b
, &e
, 10);
926 e
+= strspn(e
, WHITESPACE
);
928 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
929 if (startswith(e
, table
[i
].suffix
)) {
930 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
935 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
936 p
= e
+ strlen(table
[i
].suffix
);
942 if (i
>= ELEMENTSOF(table
))
952 bool ntp_synced(void) {
953 struct timex txc
= {};
955 if (adjtimex(&txc
) < 0)
958 if (txc
.status
& STA_UNSYNC
)
964 int get_timezones(char ***ret
) {
965 _cleanup_fclose_
FILE *f
= NULL
;
966 _cleanup_strv_free_
char **zones
= NULL
;
967 size_t n_zones
= 0, n_allocated
= 0;
971 zones
= strv_new("UTC", NULL
);
978 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
982 FOREACH_LINE(l
, f
, return -errno
) {
988 if (isempty(p
) || *p
== '#')
991 /* Skip over country code */
992 p
+= strcspn(p
, WHITESPACE
);
993 p
+= strspn(p
, WHITESPACE
);
995 /* Skip over coordinates */
996 p
+= strcspn(p
, WHITESPACE
);
997 p
+= strspn(p
, WHITESPACE
);
999 /* Found timezone name */
1000 k
= strcspn(p
, WHITESPACE
);
1008 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1013 zones
[n_zones
++] = w
;
1014 zones
[n_zones
] = NULL
;
1019 } else if (errno
!= ENOENT
)
1028 bool timezone_is_valid(const char *name
) {
1039 for (p
= name
; *p
; p
++) {
1040 if (!(*p
>= '0' && *p
<= '9') &&
1041 !(*p
>= 'a' && *p
<= 'z') &&
1042 !(*p
>= 'A' && *p
<= 'Z') &&
1043 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
1059 t
= strjoina("/usr/share/zoneinfo/", name
);
1060 if (stat(t
, &st
) < 0)
1063 if (!S_ISREG(st
.st_mode
))
1069 clockid_t
clock_boottime_or_monotonic(void) {
1070 static clockid_t clock
= -1;
1076 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1078 clock
= CLOCK_MONOTONIC
;
1081 clock
= CLOCK_BOOTTIME
;
1087 int get_timezone(char **tz
) {
1088 _cleanup_free_
char *t
= NULL
;
1093 r
= readlink_malloc("/etc/localtime", &t
);
1095 return r
; /* returns EINVAL if not a symlink */
1097 e
= path_startswith(t
, "/usr/share/zoneinfo/");
1099 e
= path_startswith(t
, "../usr/share/zoneinfo/");
1103 if (!timezone_is_valid(e
))
1114 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1115 return utc
? timegm(tm
) : mktime(tm
);
1118 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1119 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);