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 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 /// UNNEEDED by elogind
81 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
85 if (u == USEC_INFINITY) {
86 ts->realtime = ts->monotonic = USEC_INFINITY;
91 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
93 ts->realtime = now(CLOCK_REALTIME);
94 if ((int64_t) ts->realtime > delta)
95 ts->realtime -= delta;
103 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
106 if (u == USEC_INFINITY) {
107 ts->realtime = ts->monotonic = USEC_INFINITY;
110 ts->realtime = now(CLOCK_REALTIME);
111 ts->monotonic = now(CLOCK_MONOTONIC);
113 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
115 if ((int64_t) ts->realtime > delta)
116 ts->realtime -= delta;
120 if ((int64_t) ts->monotonic > delta)
121 ts->monotonic -= delta;
129 usec_t timespec_load(const struct timespec *ts) {
132 if (ts->tv_sec == (time_t) -1 &&
133 ts->tv_nsec == (long) -1)
134 return USEC_INFINITY;
136 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
137 return USEC_INFINITY;
140 (usec_t) ts->tv_sec * USEC_PER_SEC +
141 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
144 nsec_t timespec_load_nsec(const struct timespec *ts) {
147 if (ts->tv_sec == (time_t) -1 &&
148 ts->tv_nsec == (long) -1)
149 return NSEC_INFINITY;
152 (nsec_t) ts->tv_sec * NSEC_PER_SEC +
153 (nsec_t) ts->tv_nsec;
156 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
159 if (u == USEC_INFINITY) {
160 ts->tv_sec = (time_t) -1;
161 ts->tv_nsec = (long) -1;
165 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
166 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
171 usec_t timeval_load(const struct timeval *tv) {
174 if (tv->tv_sec == (time_t) -1 &&
175 tv->tv_usec == (suseconds_t) -1)
176 return USEC_INFINITY;
178 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
179 return USEC_INFINITY;
182 (usec_t) tv->tv_sec * USEC_PER_SEC +
183 (usec_t) tv->tv_usec;
186 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
189 if (u == USEC_INFINITY) {
190 tv->tv_sec = (time_t) -1;
191 tv->tv_usec = (suseconds_t) -1;
193 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
194 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
200 static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
207 if (t <= 0 || t == USEC_INFINITY)
210 sec = (time_t) (t / USEC_PER_SEC);
215 localtime_r(&sec, &tm);
216 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
222 char *format_timestamp(char *buf, size_t l, usec_t t) {
223 return format_timestamp_internal(buf, l, t, false);
226 /// UNNEEDED by elogind
228 char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
229 return format_timestamp_internal(buf, l, t, true);
233 static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
240 if (t <= 0 || t == USEC_INFINITY)
243 sec = (time_t) (t / USEC_PER_SEC);
247 localtime_r(&sec, &tm);
249 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
251 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
252 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
258 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
259 return format_timestamp_internal_us(buf, l, t, false);
262 /// UNNEEDED by elogind
264 char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
265 return format_timestamp_internal_us(buf, l, t, true);
269 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
273 if (t <= 0 || t == USEC_INFINITY)
276 n = now(CLOCK_REALTIME);
285 if (d >= USEC_PER_YEAR)
286 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
288 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
289 else if (d >= USEC_PER_MONTH)
290 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
292 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
293 else if (d >= USEC_PER_WEEK)
294 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
296 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
297 else if (d >= 2*USEC_PER_DAY)
298 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
299 else if (d >= 25*USEC_PER_HOUR)
300 snprintf(buf, l, "1 day " USEC_FMT "h %s",
301 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
302 else if (d >= 6*USEC_PER_HOUR)
303 snprintf(buf, l, USEC_FMT "h %s",
304 d / USEC_PER_HOUR, s);
305 else if (d >= USEC_PER_HOUR)
306 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
308 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
309 else if (d >= 5*USEC_PER_MINUTE)
310 snprintf(buf, l, USEC_FMT "min %s",
311 d / USEC_PER_MINUTE, s);
312 else if (d >= USEC_PER_MINUTE)
313 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
315 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
316 else if (d >= USEC_PER_SEC)
317 snprintf(buf, l, USEC_FMT "s %s",
318 d / USEC_PER_SEC, s);
319 else if (d >= USEC_PER_MSEC)
320 snprintf(buf, l, USEC_FMT "ms %s",
321 d / USEC_PER_MSEC, s);
323 snprintf(buf, l, USEC_FMT"us %s",
326 snprintf(buf, l, "now");
332 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
333 static const struct {
337 { "y", USEC_PER_YEAR },
338 { "month", USEC_PER_MONTH },
339 { "w", USEC_PER_WEEK },
340 { "d", USEC_PER_DAY },
341 { "h", USEC_PER_HOUR },
342 { "min", USEC_PER_MINUTE },
343 { "s", USEC_PER_SEC },
344 { "ms", USEC_PER_MSEC },
350 bool something = false;
355 if (t == USEC_INFINITY) {
356 strncpy(p, "infinity", l-1);
362 strncpy(p, "0", l-1);
367 /* The result of this function can be parsed with parse_sec */
369 for (i = 0; i < ELEMENTSOF(table); i++) {
378 if (t < accuracy && something)
381 if (t < table[i].usec)
387 a = t / table[i].usec;
388 b = t % table[i].usec;
390 /* Let's see if we should shows this in dot notation */
391 if (t < USEC_PER_MINUTE && b > 0) {
396 for (cc = table[i].usec; cc > 1; cc /= 10)
399 for (cc = accuracy; cc > 1; cc /= 10) {
406 "%s"USEC_FMT".%0*llu%s",
410 (unsigned long long) b,
418 /* No? Then let's show it normally */
429 n = MIN((size_t) k, l);
442 /// UNNEEDED by elogind
444 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
450 if (!dual_timestamp_is_set(t))
453 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
459 int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
460 unsigned long long a, b;
465 if (sscanf(value, "%llu %llu", &a, &b) != 2) {
466 log_debug("Failed to parse finish timestamp value %s.", value);
476 int parse_timestamp(const char *t, usec_t *usec) {
477 static const struct {
500 usec_t plus = 0, minus = 0, ret;
507 * 2012-09-22 16:34:22
508 * 2012-09-22 16:34 (seconds will be set to 0)
509 * 2012-09-22 (time will be set to 00:00:00)
510 * 16:34:22 (date will be set to today)
511 * 16:34 (date will be set to today, seconds to 0)
513 * yesterday (time is set to 00:00:00)
514 * today (time is set to 00:00:00)
515 * tomorrow (time is set to 00:00:00)
518 * @2147483647 (seconds since epoch)
526 assert_se(localtime_r(&x, &tm));
532 else if (streq(t, "today")) {
533 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
536 } else if (streq(t, "yesterday")) {
538 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
541 } else if (streq(t, "tomorrow")) {
543 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
546 } else if (t[0] == '+') {
547 r = parse_sec(t+1, &plus);
553 } else if (t[0] == '-') {
554 r = parse_sec(t+1, &minus);
560 } else if (t[0] == '@')
561 return parse_sec(t + 1, usec);
563 else if (endswith(t, " ago")) {
564 _cleanup_free_ char *z;
566 z = strndup(t, strlen(t) - 4);
570 r = parse_sec(z, &minus);
575 } else if (endswith(t, " left")) {
576 _cleanup_free_ char *z;
578 z = strndup(t, strlen(t) - 4);
582 r = parse_sec(z, &plus);
589 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
592 if (!startswith_no_case(t, day_nr[i].name))
595 skip = strlen(day_nr[i].name);
599 weekday = day_nr[i].nr;
605 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
610 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
615 k = strptime(t, "%y-%m-%d %H:%M", &tm);
622 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
629 k = strptime(t, "%y-%m-%d", &tm);
631 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
636 k = strptime(t, "%Y-%m-%d", &tm);
638 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
643 k = strptime(t, "%H:%M:%S", &tm);
648 k = strptime(t, "%H:%M", &tm);
658 if (x == (time_t) -1)
661 if (weekday >= 0 && tm.tm_wday != weekday)
664 ret = (usec_t) x * USEC_PER_SEC;
678 int parse_sec(const char *t, usec_t *usec) {
679 static const struct {
683 { "seconds", USEC_PER_SEC },
684 { "second", USEC_PER_SEC },
685 { "sec", USEC_PER_SEC },
686 { "s", USEC_PER_SEC },
687 { "minutes", USEC_PER_MINUTE },
688 { "minute", USEC_PER_MINUTE },
689 { "min", USEC_PER_MINUTE },
690 { "months", USEC_PER_MONTH },
691 { "month", USEC_PER_MONTH },
692 { "msec", USEC_PER_MSEC },
693 { "ms", USEC_PER_MSEC },
694 { "m", USEC_PER_MINUTE },
695 { "hours", USEC_PER_HOUR },
696 { "hour", USEC_PER_HOUR },
697 { "hr", USEC_PER_HOUR },
698 { "h", USEC_PER_HOUR },
699 { "days", USEC_PER_DAY },
700 { "day", USEC_PER_DAY },
701 { "d", USEC_PER_DAY },
702 { "weeks", USEC_PER_WEEK },
703 { "week", USEC_PER_WEEK },
704 { "w", USEC_PER_WEEK },
705 { "years", USEC_PER_YEAR },
706 { "year", USEC_PER_YEAR },
707 { "y", USEC_PER_YEAR },
710 { "", USEC_PER_SEC }, /* default is sec */
715 bool something = false;
722 p += strspn(p, WHITESPACE);
723 s = startswith(p, "infinity");
725 s += strspn(s, WHITESPACE);
729 *usec = USEC_INFINITY;
738 p += strspn(p, WHITESPACE);
748 l = strtoll(p, &e, 10);
760 z = strtoll(b, &e, 10);
775 e += strspn(e, WHITESPACE);
777 for (i = 0; i < ELEMENTSOF(table); i++)
778 if (startswith(e, table[i].suffix)) {
779 usec_t k = (usec_t) z * table[i].usec;
784 r += (usec_t) l * table[i].usec + k;
785 p = e + strlen(table[i].suffix);
791 if (i >= ELEMENTSOF(table))
801 int parse_nsec(const char *t, nsec_t *nsec) {
802 static const struct {
806 { "seconds", NSEC_PER_SEC },
807 { "second", NSEC_PER_SEC },
808 { "sec", NSEC_PER_SEC },
809 { "s", NSEC_PER_SEC },
810 { "minutes", NSEC_PER_MINUTE },
811 { "minute", NSEC_PER_MINUTE },
812 { "min", NSEC_PER_MINUTE },
813 { "months", NSEC_PER_MONTH },
814 { "month", NSEC_PER_MONTH },
815 { "msec", NSEC_PER_MSEC },
816 { "ms", NSEC_PER_MSEC },
817 { "m", NSEC_PER_MINUTE },
818 { "hours", NSEC_PER_HOUR },
819 { "hour", NSEC_PER_HOUR },
820 { "hr", NSEC_PER_HOUR },
821 { "h", NSEC_PER_HOUR },
822 { "days", NSEC_PER_DAY },
823 { "day", NSEC_PER_DAY },
824 { "d", NSEC_PER_DAY },
825 { "weeks", NSEC_PER_WEEK },
826 { "week", NSEC_PER_WEEK },
827 { "w", NSEC_PER_WEEK },
828 { "years", NSEC_PER_YEAR },
829 { "year", NSEC_PER_YEAR },
830 { "y", NSEC_PER_YEAR },
831 { "usec", NSEC_PER_USEC },
832 { "us", NSEC_PER_USEC },
835 { "", 1ULL }, /* default is nsec */
840 bool something = false;
847 p += strspn(p, WHITESPACE);
848 s = startswith(p, "infinity");
850 s += strspn(s, WHITESPACE);
854 *nsec = NSEC_INFINITY;
863 p += strspn(p, WHITESPACE);
873 l = strtoll(p, &e, 10);
885 z = strtoll(b, &e, 10);
900 e += strspn(e, WHITESPACE);
902 for (i = 0; i < ELEMENTSOF(table); i++)
903 if (startswith(e, table[i].suffix)) {
904 nsec_t k = (nsec_t) z * table[i].nsec;
909 r += (nsec_t) l * table[i].nsec + k;
910 p = e + strlen(table[i].suffix);
916 if (i >= ELEMENTSOF(table))
926 /// UNNEEDED by elogind
928 bool ntp_synced(void) {
929 struct timex txc = {};
931 if (adjtimex(&txc) < 0)
934 if (txc.status & STA_UNSYNC)
940 int get_timezones(char ***ret) {
941 _cleanup_fclose_ FILE *f = NULL;
942 _cleanup_strv_free_ char **zones = NULL;
943 size_t n_zones = 0, n_allocated = 0;
947 zones = strv_new("UTC", NULL);
954 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
958 FOREACH_LINE(l, f, return -errno) {
964 if (isempty(p) || *p == '#')
967 /* Skip over country code */
968 p += strcspn(p, WHITESPACE);
969 p += strspn(p, WHITESPACE);
971 /* Skip over coordinates */
972 p += strcspn(p, WHITESPACE);
973 p += strspn(p, WHITESPACE);
975 /* Found timezone name */
976 k = strcspn(p, WHITESPACE);
984 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
989 zones[n_zones++] = w;
990 zones[n_zones] = NULL;
995 } else if (errno != ENOENT)
1004 bool timezone_is_valid(const char *name) {
1015 for (p = name; *p; p++) {
1016 if (!(*p >= '0' && *p <= '9') &&
1017 !(*p >= 'a' && *p <= 'z') &&
1018 !(*p >= 'A' && *p <= 'Z') &&
1019 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
1035 t = strjoina("/usr/share/zoneinfo/", name);
1036 if (stat(t, &st) < 0)
1039 if (!S_ISREG(st.st_mode))
1046 clockid_t clock_boottime_or_monotonic(void) {
1047 static clockid_t clock = -1;
1053 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1055 clock = CLOCK_MONOTONIC;
1058 clock = CLOCK_BOOTTIME;
1064 int get_timezone(char **tz) {
1065 _cleanup_free_ char *t = NULL;
1070 r = readlink_malloc("/etc/localtime", &t);
1072 return r; /* returns EINVAL if not a symlink */
1074 e = path_startswith(t, "/usr/share/zoneinfo/");
1076 e = path_startswith(t, "../usr/share/zoneinfo/");
1080 if (!timezone_is_valid(e))