]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/time-util.c
fc79c569f4bceb52450a4a7be17ea09d6b8c817b
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>
27 #include "time-util.h"
30 usec_t
now(clockid_t clock_id
) {
33 assert_se(clock_gettime(clock_id
, &ts
) == 0);
35 return timespec_load(&ts
);
38 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
41 ts
->realtime
= now(CLOCK_REALTIME
);
42 ts
->monotonic
= now(CLOCK_MONOTONIC
);
47 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
51 if (u
== (usec_t
) -1) {
52 ts
->realtime
= ts
->monotonic
= (usec_t
) -1;
61 delta
= (int64_t) now(CLOCK_REALTIME
) - (int64_t) u
;
63 ts
->monotonic
= now(CLOCK_MONOTONIC
);
65 if ((int64_t) ts
->monotonic
> delta
)
66 ts
->monotonic
-= delta
;
74 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
78 if (u
== (usec_t
) -1) {
79 ts
->realtime
= ts
->monotonic
= (usec_t
) -1;
84 delta
= (int64_t) now(CLOCK_MONOTONIC
) - (int64_t) u
;
86 ts
->realtime
= now(CLOCK_REALTIME
);
87 if ((int64_t) ts
->realtime
> delta
)
88 ts
->realtime
-= delta
;
95 usec_t
timespec_load(const struct timespec
*ts
) {
98 if (ts
->tv_sec
== (time_t) -1 &&
99 ts
->tv_nsec
== (long) -1)
102 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
106 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
107 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
110 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
113 if (u
== (usec_t
) -1) {
114 ts
->tv_sec
= (time_t) -1;
115 ts
->tv_nsec
= (long) -1;
119 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
120 ts
->tv_nsec
= (long int) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
125 usec_t
timeval_load(const struct timeval
*tv
) {
128 if (tv
->tv_sec
== (time_t) -1 &&
129 tv
->tv_usec
== (suseconds_t
) -1)
132 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
136 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
137 (usec_t
) tv
->tv_usec
;
140 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
143 if (u
== (usec_t
) -1) {
144 tv
->tv_sec
= (time_t) -1;
145 tv
->tv_usec
= (suseconds_t
) -1;
147 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
148 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
154 char *format_timestamp(char *buf
, size_t l
, usec_t t
) {
161 if (t
<= 0 || t
== (usec_t
) -1)
164 sec
= (time_t) (t
/ USEC_PER_SEC
);
166 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec
, &tm
)) <= 0)
172 char *format_timestamp_us(char *buf
, size_t l
, usec_t t
) {
179 if (t
<= 0 || t
== (usec_t
) -1)
182 sec
= (time_t) (t
/ USEC_PER_SEC
);
183 localtime_r(&sec
, &tm
);
185 if (strftime(buf
, l
, "%a %Y-%m-%d %H:%M:%S", &tm
) <= 0)
187 snprintf(buf
+ strlen(buf
), l
- strlen(buf
), ".%06llu", (unsigned long long) (t
% USEC_PER_SEC
));
188 if (strftime(buf
+ strlen(buf
), l
- strlen(buf
), " %Z", &tm
) <= 0)
194 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
198 n
= now(CLOCK_REALTIME
);
200 if (t
<= 0 || (t
== (usec_t
) -1))
211 if (d
>= USEC_PER_YEAR
)
212 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
214 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
215 else if (d
>= USEC_PER_MONTH
)
216 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
218 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
219 else if (d
>= USEC_PER_WEEK
)
220 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
222 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
223 else if (d
>= 2*USEC_PER_DAY
)
224 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
225 else if (d
>= 25*USEC_PER_HOUR
)
226 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
227 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
228 else if (d
>= 6*USEC_PER_HOUR
)
229 snprintf(buf
, l
, USEC_FMT
"h %s",
230 d
/ USEC_PER_HOUR
, s
);
231 else if (d
>= USEC_PER_HOUR
)
232 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
234 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
235 else if (d
>= 5*USEC_PER_MINUTE
)
236 snprintf(buf
, l
, USEC_FMT
"min %s",
237 d
/ USEC_PER_MINUTE
, s
);
238 else if (d
>= USEC_PER_MINUTE
)
239 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
241 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
242 else if (d
>= USEC_PER_SEC
)
243 snprintf(buf
, l
, USEC_FMT
"s %s",
244 d
/ USEC_PER_SEC
, s
);
245 else if (d
>= USEC_PER_MSEC
)
246 snprintf(buf
, l
, USEC_FMT
"ms %s",
247 d
/ USEC_PER_MSEC
, s
);
249 snprintf(buf
, l
, USEC_FMT
"us %s",
252 snprintf(buf
, l
, "now");
258 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
259 static const struct {
263 { "y", USEC_PER_YEAR
},
264 { "month", USEC_PER_MONTH
},
265 { "w", USEC_PER_WEEK
},
266 { "d", USEC_PER_DAY
},
267 { "h", USEC_PER_HOUR
},
268 { "min", USEC_PER_MINUTE
},
269 { "s", USEC_PER_SEC
},
270 { "ms", USEC_PER_MSEC
},
276 bool something
= false;
281 if (t
== (usec_t
) -1)
290 /* The result of this function can be parsed with parse_sec */
292 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
301 if (t
< accuracy
&& something
)
304 if (t
< table
[i
].usec
)
310 a
= t
/ table
[i
].usec
;
311 b
= t
% table
[i
].usec
;
313 /* Let's see if we should shows this in dot notation */
314 if (t
< USEC_PER_MINUTE
&& b
> 0) {
319 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
322 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
329 "%s"USEC_FMT
".%0*llu%s",
333 (unsigned long long) b
,
341 /* No? Then let's show it normally */
352 n
= MIN((size_t) k
, l
);
365 void dual_timestamp_serialize(FILE *f
, const char *name
, dual_timestamp
*t
) {
371 if (!dual_timestamp_is_set(t
))
374 fprintf(f
, "%s="USEC_FMT
" "USEC_FMT
"\n",
380 void dual_timestamp_deserialize(const char *value
, dual_timestamp
*t
) {
381 unsigned long long a
, b
;
386 if (sscanf(value
, "%llu %llu", &a
, &b
) != 2)
387 log_debug("Failed to parse finish timestamp value %s", value
);
394 int parse_timestamp(const char *t
, usec_t
*usec
) {
395 static const struct {
418 usec_t plus
= 0, minus
= 0, ret
;
425 * 2012-09-22 16:34:22
426 * 2012-09-22 16:34 (seconds will be set to 0)
427 * 2012-09-22 (time will be set to 00:00:00)
428 * 16:34:22 (date will be set to today)
429 * 16:34 (date will be set to today, seconds to 0)
431 * yesterday (time is set to 00:00:00)
432 * today (time is set to 00:00:00)
433 * tomorrow (time is set to 00:00:00)
436 * @2147483647 (seconds since epoch)
444 assert_se(localtime_r(&x
, &tm
));
450 else if (streq(t
, "today")) {
451 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
454 } else if (streq(t
, "yesterday")) {
456 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
459 } else if (streq(t
, "tomorrow")) {
461 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
464 } else if (t
[0] == '+') {
465 r
= parse_sec(t
+1, &plus
);
471 } else if (t
[0] == '-') {
472 r
= parse_sec(t
+1, &minus
);
478 } else if (t
[0] == '@')
479 return parse_sec(t
+ 1, usec
);
481 else if (endswith(t
, " ago")) {
482 _cleanup_free_
char *z
;
484 z
= strndup(t
, strlen(t
) - 4);
488 r
= parse_sec(z
, &minus
);
493 } else if (endswith(t
, " left")) {
494 _cleanup_free_
char *z
;
496 z
= strndup(t
, strlen(t
) - 4);
500 r
= parse_sec(z
, &plus
);
507 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
510 if (!startswith_no_case(t
, day_nr
[i
].name
))
513 skip
= strlen(day_nr
[i
].name
);
517 weekday
= day_nr
[i
].nr
;
523 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
528 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
533 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
540 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
547 k
= strptime(t
, "%y-%m-%d", &tm
);
549 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
554 k
= strptime(t
, "%Y-%m-%d", &tm
);
556 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
561 k
= strptime(t
, "%H:%M:%S", &tm
);
566 k
= strptime(t
, "%H:%M", &tm
);
576 if (x
== (time_t) -1)
579 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
582 ret
= (usec_t
) x
* USEC_PER_SEC
;
595 int parse_sec(const char *t
, usec_t
*usec
) {
596 static const struct {
600 { "seconds", USEC_PER_SEC
},
601 { "second", USEC_PER_SEC
},
602 { "sec", USEC_PER_SEC
},
603 { "s", USEC_PER_SEC
},
604 { "minutes", USEC_PER_MINUTE
},
605 { "minute", USEC_PER_MINUTE
},
606 { "min", USEC_PER_MINUTE
},
607 { "months", USEC_PER_MONTH
},
608 { "month", USEC_PER_MONTH
},
609 { "msec", USEC_PER_MSEC
},
610 { "ms", USEC_PER_MSEC
},
611 { "m", USEC_PER_MINUTE
},
612 { "hours", USEC_PER_HOUR
},
613 { "hour", USEC_PER_HOUR
},
614 { "hr", USEC_PER_HOUR
},
615 { "h", USEC_PER_HOUR
},
616 { "days", USEC_PER_DAY
},
617 { "day", USEC_PER_DAY
},
618 { "d", USEC_PER_DAY
},
619 { "weeks", USEC_PER_WEEK
},
620 { "week", USEC_PER_WEEK
},
621 { "w", USEC_PER_WEEK
},
622 { "years", USEC_PER_YEAR
},
623 { "year", USEC_PER_YEAR
},
624 { "y", USEC_PER_YEAR
},
627 { "", USEC_PER_SEC
}, /* default is sec */
632 bool something
= false;
643 p
+= strspn(p
, WHITESPACE
);
653 l
= strtoll(p
, &e
, 10);
665 z
= strtoll(b
, &e
, 10);
680 e
+= strspn(e
, WHITESPACE
);
682 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
683 if (startswith(e
, table
[i
].suffix
)) {
684 usec_t k
= (usec_t
) z
* table
[i
].usec
;
689 r
+= (usec_t
) l
* table
[i
].usec
+ k
;
690 p
= e
+ strlen(table
[i
].suffix
);
696 if (i
>= ELEMENTSOF(table
))
706 int parse_nsec(const char *t
, nsec_t
*nsec
) {
707 static const struct {
711 { "seconds", NSEC_PER_SEC
},
712 { "second", NSEC_PER_SEC
},
713 { "sec", NSEC_PER_SEC
},
714 { "s", NSEC_PER_SEC
},
715 { "minutes", NSEC_PER_MINUTE
},
716 { "minute", NSEC_PER_MINUTE
},
717 { "min", NSEC_PER_MINUTE
},
718 { "months", NSEC_PER_MONTH
},
719 { "month", NSEC_PER_MONTH
},
720 { "msec", NSEC_PER_MSEC
},
721 { "ms", NSEC_PER_MSEC
},
722 { "m", NSEC_PER_MINUTE
},
723 { "hours", NSEC_PER_HOUR
},
724 { "hour", NSEC_PER_HOUR
},
725 { "hr", NSEC_PER_HOUR
},
726 { "h", NSEC_PER_HOUR
},
727 { "days", NSEC_PER_DAY
},
728 { "day", NSEC_PER_DAY
},
729 { "d", NSEC_PER_DAY
},
730 { "weeks", NSEC_PER_WEEK
},
731 { "week", NSEC_PER_WEEK
},
732 { "w", NSEC_PER_WEEK
},
733 { "years", NSEC_PER_YEAR
},
734 { "year", NSEC_PER_YEAR
},
735 { "y", NSEC_PER_YEAR
},
736 { "usec", NSEC_PER_USEC
},
737 { "us", NSEC_PER_USEC
},
740 { "", 1ULL }, /* default is nsec */
745 bool something
= false;
756 p
+= strspn(p
, WHITESPACE
);
766 l
= strtoll(p
, &e
, 10);
778 z
= strtoll(b
, &e
, 10);
793 e
+= strspn(e
, WHITESPACE
);
795 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
796 if (startswith(e
, table
[i
].suffix
)) {
797 nsec_t k
= (nsec_t
) z
* table
[i
].nsec
;
802 r
+= (nsec_t
) l
* table
[i
].nsec
+ k
;
803 p
= e
+ strlen(table
[i
].suffix
);
809 if (i
>= ELEMENTSOF(table
))
819 bool ntp_synced(void) {
820 struct timex txc
= {};
822 if (adjtimex(&txc
) < 0)
825 if (txc
.status
& STA_UNSYNC
)
831 int get_timezones(char ***ret
) {
832 _cleanup_fclose_
FILE *f
= NULL
;
833 _cleanup_strv_free_
char **zones
= NULL
;
834 size_t n_zones
= 0, n_allocated
= 0;
838 zones
= strv_new("UTC", NULL
);
845 f
= fopen("/usr/share/zoneinfo/zone.tab", "re");
849 FOREACH_LINE(l
, f
, return -errno
) {
855 if (isempty(p
) || *p
== '#')
858 /* Skip over country code */
859 p
+= strcspn(p
, WHITESPACE
);
860 p
+= strspn(p
, WHITESPACE
);
862 /* Skip over coordinates */
863 p
+= strcspn(p
, WHITESPACE
);
864 p
+= strspn(p
, WHITESPACE
);
866 /* Found timezone name */
867 k
= strcspn(p
, WHITESPACE
);
875 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
880 zones
[n_zones
++] = w
;
881 zones
[n_zones
] = NULL
;
886 } else if (errno
!= ENOENT
)
895 bool timezone_is_valid(const char *name
) {
900 if (!name
|| *name
== 0 || *name
== '/')
903 for (p
= name
; *p
; p
++) {
904 if (!(*p
>= '0' && *p
<= '9') &&
905 !(*p
>= 'a' && *p
<= 'z') &&
906 !(*p
>= 'A' && *p
<= 'Z') &&
907 !(*p
== '-' || *p
== '_' || *p
== '+' || *p
== '/'))
923 t
= strappenda("/usr/share/zoneinfo/", name
);
924 if (stat(t
, &st
) < 0)
927 if (!S_ISREG(st
.st_mode
))