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 /// UNNEEDED by elogind
42 nsec_t now_nsec(clockid_t clock_id) {
45 assert_se(clock_gettime(clock_id, &ts) == 0);
47 return timespec_load_nsec(&ts);
51 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
54 ts->realtime = now(CLOCK_REALTIME);
55 ts->monotonic = now(CLOCK_MONOTONIC);
60 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
64 if (u == USEC_INFINITY || u <= 0) {
65 ts->realtime = ts->monotonic = u;
71 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
72 ts->monotonic = now(CLOCK_MONOTONIC);
74 if ((int64_t) ts->monotonic > delta)
75 ts->monotonic -= delta;
82 /// UNNEEDED by elogind
84 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
88 if (u == USEC_INFINITY) {
89 ts->realtime = ts->monotonic = USEC_INFINITY;
94 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
96 ts->realtime = now(CLOCK_REALTIME);
97 if ((int64_t) ts->realtime > delta)
98 ts->realtime -= delta;
105 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
108 if (u == USEC_INFINITY) {
109 ts->realtime = ts->monotonic = USEC_INFINITY;
112 ts->realtime = now(CLOCK_REALTIME);
113 ts->monotonic = now(CLOCK_MONOTONIC);
115 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
117 if ((int64_t) ts->realtime > delta)
118 ts->realtime -= delta;
122 if ((int64_t) ts->monotonic > delta)
123 ts->monotonic -= delta;
131 usec_t timespec_load(const struct timespec *ts) {
134 if (ts->tv_sec == (time_t) -1 &&
135 ts->tv_nsec == (long) -1)
136 return USEC_INFINITY;
138 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
139 return USEC_INFINITY;
142 (usec_t) ts->tv_sec * USEC_PER_SEC +
143 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
146 nsec_t timespec_load_nsec(const struct timespec *ts) {
149 if (ts->tv_sec == (time_t) -1 &&
150 ts->tv_nsec == (long) -1)
151 return NSEC_INFINITY;
154 (nsec_t) ts->tv_sec * NSEC_PER_SEC +
155 (nsec_t) ts->tv_nsec;
158 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
161 if (u == USEC_INFINITY) {
162 ts->tv_sec = (time_t) -1;
163 ts->tv_nsec = (long) -1;
167 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
168 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
173 usec_t timeval_load(const struct timeval *tv) {
176 if (tv->tv_sec == (time_t) -1 &&
177 tv->tv_usec == (suseconds_t) -1)
178 return USEC_INFINITY;
180 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
181 return USEC_INFINITY;
184 (usec_t) tv->tv_sec * USEC_PER_SEC +
185 (usec_t) tv->tv_usec;
188 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
191 if (u == USEC_INFINITY) {
192 tv->tv_sec = (time_t) -1;
193 tv->tv_usec = (suseconds_t) -1;
195 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
196 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
202 static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
209 if (t <= 0 || t == USEC_INFINITY)
212 sec = (time_t) (t / USEC_PER_SEC);
217 localtime_r(&sec, &tm);
218 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
224 char *format_timestamp(char *buf, size_t l, usec_t t) {
225 return format_timestamp_internal(buf, l, t, false);
228 /// UNNEEDED by elogind
230 char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
231 return format_timestamp_internal(buf, l, t, true);
235 static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
242 if (t <= 0 || t == USEC_INFINITY)
245 sec = (time_t) (t / USEC_PER_SEC);
249 localtime_r(&sec, &tm);
251 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
253 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
254 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
260 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
261 return format_timestamp_internal_us(buf, l, t, false);
264 /// UNNEEDED by elogind
266 char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
267 return format_timestamp_internal_us(buf, l, t, true);
271 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
275 if (t <= 0 || t == USEC_INFINITY)
278 n = now(CLOCK_REALTIME);
287 if (d >= USEC_PER_YEAR)
288 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
290 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
291 else if (d >= USEC_PER_MONTH)
292 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
294 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
295 else if (d >= USEC_PER_WEEK)
296 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
298 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
299 else if (d >= 2*USEC_PER_DAY)
300 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
301 else if (d >= 25*USEC_PER_HOUR)
302 snprintf(buf, l, "1 day " USEC_FMT "h %s",
303 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
304 else if (d >= 6*USEC_PER_HOUR)
305 snprintf(buf, l, USEC_FMT "h %s",
306 d / USEC_PER_HOUR, s);
307 else if (d >= USEC_PER_HOUR)
308 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
310 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
311 else if (d >= 5*USEC_PER_MINUTE)
312 snprintf(buf, l, USEC_FMT "min %s",
313 d / USEC_PER_MINUTE, s);
314 else if (d >= USEC_PER_MINUTE)
315 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
317 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
318 else if (d >= USEC_PER_SEC)
319 snprintf(buf, l, USEC_FMT "s %s",
320 d / USEC_PER_SEC, s);
321 else if (d >= USEC_PER_MSEC)
322 snprintf(buf, l, USEC_FMT "ms %s",
323 d / USEC_PER_MSEC, s);
325 snprintf(buf, l, USEC_FMT"us %s",
328 snprintf(buf, l, "now");
334 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
335 static const struct {
339 { "y", USEC_PER_YEAR },
340 { "month", USEC_PER_MONTH },
341 { "w", USEC_PER_WEEK },
342 { "d", USEC_PER_DAY },
343 { "h", USEC_PER_HOUR },
344 { "min", USEC_PER_MINUTE },
345 { "s", USEC_PER_SEC },
346 { "ms", USEC_PER_MSEC },
352 bool something = false;
357 if (t == USEC_INFINITY) {
358 strncpy(p, "infinity", l-1);
364 strncpy(p, "0", l-1);
369 /* The result of this function can be parsed with parse_sec */
371 for (i = 0; i < ELEMENTSOF(table); i++) {
380 if (t < accuracy && something)
383 if (t < table[i].usec)
389 a = t / table[i].usec;
390 b = t % table[i].usec;
392 /* Let's see if we should shows this in dot notation */
393 if (t < USEC_PER_MINUTE && b > 0) {
398 for (cc = table[i].usec; cc > 1; cc /= 10)
401 for (cc = accuracy; cc > 1; cc /= 10) {
408 "%s"USEC_FMT".%0*llu%s",
412 (unsigned long long) b,
420 /* No? Then let's show it normally */
431 n = MIN((size_t) k, l);
444 /// UNNEEDED by elogind
446 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
452 if (!dual_timestamp_is_set(t))
455 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
461 int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
462 unsigned long long a, b;
467 if (sscanf(value, "%llu %llu", &a, &b) != 2) {
468 log_debug("Failed to parse finish timestamp value %s.", value);
478 int parse_timestamp(const char *t, usec_t *usec) {
479 static const struct {
502 usec_t plus = 0, minus = 0, ret;
509 * 2012-09-22 16:34:22
510 * 2012-09-22 16:34 (seconds will be set to 0)
511 * 2012-09-22 (time will be set to 00:00:00)
512 * 16:34:22 (date will be set to today)
513 * 16:34 (date will be set to today, seconds to 0)
515 * yesterday (time is set to 00:00:00)
516 * today (time is set to 00:00:00)
517 * tomorrow (time is set to 00:00:00)
520 * @2147483647 (seconds since epoch)
528 assert_se(localtime_r(&x, &tm));
534 else if (streq(t, "today")) {
535 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
538 } else if (streq(t, "yesterday")) {
540 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
543 } else if (streq(t, "tomorrow")) {
545 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
548 } else if (t[0] == '+') {
549 r = parse_sec(t+1, &plus);
555 } else if (t[0] == '-') {
556 r = parse_sec(t+1, &minus);
562 } else if (t[0] == '@')
563 return parse_sec(t + 1, usec);
565 else if (endswith(t, " ago")) {
566 _cleanup_free_ char *z;
568 z = strndup(t, strlen(t) - 4);
572 r = parse_sec(z, &minus);
577 } else if (endswith(t, " left")) {
578 _cleanup_free_ char *z;
580 z = strndup(t, strlen(t) - 4);
584 r = parse_sec(z, &plus);
591 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
594 if (!startswith_no_case(t, day_nr[i].name))
597 skip = strlen(day_nr[i].name);
601 weekday = day_nr[i].nr;
607 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
612 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
617 k = strptime(t, "%y-%m-%d %H:%M", &tm);
624 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
631 k = strptime(t, "%y-%m-%d", &tm);
633 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
638 k = strptime(t, "%Y-%m-%d", &tm);
640 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
645 k = strptime(t, "%H:%M:%S", &tm);
650 k = strptime(t, "%H:%M", &tm);
660 if (x == (time_t) -1)
663 if (weekday >= 0 && tm.tm_wday != weekday)
666 ret = (usec_t) x * USEC_PER_SEC;
680 int parse_sec(const char *t, usec_t *usec) {
681 static const struct {
685 { "seconds", USEC_PER_SEC },
686 { "second", USEC_PER_SEC },
687 { "sec", USEC_PER_SEC },
688 { "s", USEC_PER_SEC },
689 { "minutes", USEC_PER_MINUTE },
690 { "minute", USEC_PER_MINUTE },
691 { "min", USEC_PER_MINUTE },
692 { "months", USEC_PER_MONTH },
693 { "month", USEC_PER_MONTH },
694 { "msec", USEC_PER_MSEC },
695 { "ms", USEC_PER_MSEC },
696 { "m", USEC_PER_MINUTE },
697 { "hours", USEC_PER_HOUR },
698 { "hour", USEC_PER_HOUR },
699 { "hr", USEC_PER_HOUR },
700 { "h", USEC_PER_HOUR },
701 { "days", USEC_PER_DAY },
702 { "day", USEC_PER_DAY },
703 { "d", USEC_PER_DAY },
704 { "weeks", USEC_PER_WEEK },
705 { "week", USEC_PER_WEEK },
706 { "w", USEC_PER_WEEK },
707 { "years", USEC_PER_YEAR },
708 { "year", USEC_PER_YEAR },
709 { "y", USEC_PER_YEAR },
712 { "", USEC_PER_SEC }, /* default is sec */
717 bool something = false;
724 p += strspn(p, WHITESPACE);
725 s = startswith(p, "infinity");
727 s += strspn(s, WHITESPACE);
731 *usec = USEC_INFINITY;
740 p += strspn(p, WHITESPACE);
750 l = strtoll(p, &e, 10);
762 z = strtoll(b, &e, 10);
777 e += strspn(e, WHITESPACE);
779 for (i = 0; i < ELEMENTSOF(table); i++)
780 if (startswith(e, table[i].suffix)) {
781 usec_t k = (usec_t) z * table[i].usec;
786 r += (usec_t) l * table[i].usec + k;
787 p = e + strlen(table[i].suffix);
793 if (i >= ELEMENTSOF(table))
803 /// UNNEEDED by elogind
805 int parse_nsec(const char *t, nsec_t *nsec) {
806 static const struct {
810 { "seconds", NSEC_PER_SEC },
811 { "second", NSEC_PER_SEC },
812 { "sec", NSEC_PER_SEC },
813 { "s", NSEC_PER_SEC },
814 { "minutes", NSEC_PER_MINUTE },
815 { "minute", NSEC_PER_MINUTE },
816 { "min", NSEC_PER_MINUTE },
817 { "months", NSEC_PER_MONTH },
818 { "month", NSEC_PER_MONTH },
819 { "msec", NSEC_PER_MSEC },
820 { "ms", NSEC_PER_MSEC },
821 { "m", NSEC_PER_MINUTE },
822 { "hours", NSEC_PER_HOUR },
823 { "hour", NSEC_PER_HOUR },
824 { "hr", NSEC_PER_HOUR },
825 { "h", NSEC_PER_HOUR },
826 { "days", NSEC_PER_DAY },
827 { "day", NSEC_PER_DAY },
828 { "d", NSEC_PER_DAY },
829 { "weeks", NSEC_PER_WEEK },
830 { "week", NSEC_PER_WEEK },
831 { "w", NSEC_PER_WEEK },
832 { "years", NSEC_PER_YEAR },
833 { "year", NSEC_PER_YEAR },
834 { "y", NSEC_PER_YEAR },
835 { "usec", NSEC_PER_USEC },
836 { "us", NSEC_PER_USEC },
839 { "", 1ULL }, /* default is nsec */
844 bool something = false;
851 p += strspn(p, WHITESPACE);
852 s = startswith(p, "infinity");
854 s += strspn(s, WHITESPACE);
858 *nsec = NSEC_INFINITY;
867 p += strspn(p, WHITESPACE);
877 l = strtoll(p, &e, 10);
889 z = strtoll(b, &e, 10);
904 e += strspn(e, WHITESPACE);
906 for (i = 0; i < ELEMENTSOF(table); i++)
907 if (startswith(e, table[i].suffix)) {
908 nsec_t k = (nsec_t) z * table[i].nsec;
913 r += (nsec_t) l * table[i].nsec + k;
914 p = e + strlen(table[i].suffix);
920 if (i >= ELEMENTSOF(table))
930 bool ntp_synced(void) {
931 struct timex txc = {};
933 if (adjtimex(&txc) < 0)
936 if (txc.status & STA_UNSYNC)
942 int get_timezones(char ***ret) {
943 _cleanup_fclose_ FILE *f = NULL;
944 _cleanup_strv_free_ char **zones = NULL;
945 size_t n_zones = 0, n_allocated = 0;
949 zones = strv_new("UTC", NULL);
956 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
960 FOREACH_LINE(l, f, return -errno) {
966 if (isempty(p) || *p == '#')
969 /* Skip over country code */
970 p += strcspn(p, WHITESPACE);
971 p += strspn(p, WHITESPACE);
973 /* Skip over coordinates */
974 p += strcspn(p, WHITESPACE);
975 p += strspn(p, WHITESPACE);
977 /* Found timezone name */
978 k = strcspn(p, WHITESPACE);
986 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
991 zones[n_zones++] = w;
992 zones[n_zones] = NULL;
997 } else if (errno != ENOENT)
1006 bool timezone_is_valid(const char *name) {
1017 for (p = name; *p; p++) {
1018 if (!(*p >= '0' && *p <= '9') &&
1019 !(*p >= 'a' && *p <= 'z') &&
1020 !(*p >= 'A' && *p <= 'Z') &&
1021 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
1037 t = strjoina("/usr/share/zoneinfo/", name);
1038 if (stat(t, &st) < 0)
1041 if (!S_ISREG(st.st_mode))
1047 clockid_t clock_boottime_or_monotonic(void) {
1048 static clockid_t clock = -1;
1054 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1056 clock = CLOCK_MONOTONIC;
1059 clock = CLOCK_BOOTTIME;
1065 int get_timezone(char **tz) {
1066 _cleanup_free_ char *t = NULL;
1071 r = readlink_malloc("/etc/localtime", &t);
1073 return r; /* returns EINVAL if not a symlink */
1075 e = path_startswith(t, "/usr/share/zoneinfo/");
1077 e = path_startswith(t, "../usr/share/zoneinfo/");
1081 if (!timezone_is_valid(e))