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"
29 usec_t now(clockid_t clock_id) {
32 assert_se(clock_gettime(clock_id, &ts) == 0);
34 return timespec_load(&ts);
37 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
40 ts->realtime = now(CLOCK_REALTIME);
41 ts->monotonic = now(CLOCK_MONOTONIC);
46 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
50 if (u == (usec_t) -1) {
51 ts->realtime = ts->monotonic = (usec_t) -1;
60 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
62 ts->monotonic = now(CLOCK_MONOTONIC);
64 if ((int64_t) ts->monotonic > delta)
65 ts->monotonic -= delta;
73 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
77 if (u == (usec_t) -1) {
78 ts->realtime = ts->monotonic = (usec_t) -1;
83 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
85 ts->realtime = now(CLOCK_REALTIME);
86 if ((int64_t) ts->realtime > delta)
87 ts->realtime -= delta;
94 usec_t timespec_load(const struct timespec *ts) {
97 if (ts->tv_sec == (time_t) -1 &&
98 ts->tv_nsec == (long) -1)
101 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
105 (usec_t) ts->tv_sec * USEC_PER_SEC +
106 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
109 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
112 if (u == (usec_t) -1) {
113 ts->tv_sec = (time_t) -1;
114 ts->tv_nsec = (long) -1;
118 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
119 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
124 usec_t timeval_load(const struct timeval *tv) {
127 if (tv->tv_sec == (time_t) -1 &&
128 tv->tv_usec == (suseconds_t) -1)
131 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
135 (usec_t) tv->tv_sec * USEC_PER_SEC +
136 (usec_t) tv->tv_usec;
139 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
142 if (u == (usec_t) -1) {
143 tv->tv_sec = (time_t) -1;
144 tv->tv_usec = (suseconds_t) -1;
148 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
149 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
154 char *format_timestamp(char *buf, size_t l, usec_t t) {
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) {
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", 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) {
197 n = now(CLOCK_REALTIME);
199 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
204 if (d >= USEC_PER_YEAR)
205 snprintf(buf, l, "%llu years %llu months ago",
206 (unsigned long long) (d / USEC_PER_YEAR),
207 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
208 else if (d >= USEC_PER_MONTH)
209 snprintf(buf, l, "%llu months %llu days ago",
210 (unsigned long long) (d / USEC_PER_MONTH),
211 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
212 else if (d >= USEC_PER_WEEK)
213 snprintf(buf, l, "%llu weeks %llu days ago",
214 (unsigned long long) (d / USEC_PER_WEEK),
215 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
216 else if (d >= 2*USEC_PER_DAY)
217 snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
218 else if (d >= 25*USEC_PER_HOUR)
219 snprintf(buf, l, "1 day %lluh ago",
220 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
221 else if (d >= 6*USEC_PER_HOUR)
222 snprintf(buf, l, "%lluh ago",
223 (unsigned long long) (d / USEC_PER_HOUR));
224 else if (d >= USEC_PER_HOUR)
225 snprintf(buf, l, "%lluh %llumin ago",
226 (unsigned long long) (d / USEC_PER_HOUR),
227 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
228 else if (d >= 5*USEC_PER_MINUTE)
229 snprintf(buf, l, "%llumin ago",
230 (unsigned long long) (d / USEC_PER_MINUTE));
231 else if (d >= USEC_PER_MINUTE)
232 snprintf(buf, l, "%llumin %llus ago",
233 (unsigned long long) (d / USEC_PER_MINUTE),
234 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
235 else if (d >= USEC_PER_SEC)
236 snprintf(buf, l, "%llus ago",
237 (unsigned long long) (d / USEC_PER_SEC));
238 else if (d >= USEC_PER_MSEC)
239 snprintf(buf, l, "%llums ago",
240 (unsigned long long) (d / USEC_PER_MSEC));
242 snprintf(buf, l, "%lluus ago",
243 (unsigned long long) d);
245 snprintf(buf, l, "now");
251 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
252 static const struct {
256 { "y", USEC_PER_YEAR },
257 { "month", USEC_PER_MONTH },
258 { "w", USEC_PER_WEEK },
259 { "d", USEC_PER_DAY },
260 { "h", USEC_PER_HOUR },
261 { "min", USEC_PER_MINUTE },
262 { "s", USEC_PER_SEC },
263 { "ms", USEC_PER_MSEC },
269 bool something = false;
274 if (t == (usec_t) -1)
283 /* The result of this function can be parsed with parse_sec */
285 for (i = 0; i < ELEMENTSOF(table); i++) {
294 if (t < accuracy && something)
297 if (t < table[i].usec)
303 a = t / table[i].usec;
304 b = t % table[i].usec;
306 /* Let's see if we should shows this in dot notation */
307 if (t < USEC_PER_MINUTE && b > 0) {
312 for (cc = table[i].usec; cc > 1; cc /= 10)
315 for (cc = accuracy; cc > 1; cc /= 10) {
324 (unsigned long long) a,
326 (unsigned long long) b,
334 /* No? Then let's show it normally */
339 (unsigned long long) a,
345 n = MIN((size_t) k, l);
358 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
364 if (!dual_timestamp_is_set(t))
367 fprintf(f, "%s=%llu %llu\n",
369 (unsigned long long) t->realtime,
370 (unsigned long long) t->monotonic);
373 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
374 unsigned long long a, b;
379 if (sscanf(value, "%lli %llu", &a, &b) != 2)
380 log_debug("Failed to parse finish timestamp value %s", value);
387 int parse_timestamp(const char *t, usec_t *usec) {
388 static const struct {
411 usec_t plus = 0, minus = 0, ret;
418 * 2012-09-22 16:34:22
419 * 2012-09-22 16:34 (seconds will be set to 0)
420 * 2012-09-22 (time will be set to 00:00:00)
421 * 16:34:22 (date will be set to today)
422 * 16:34 (date will be set to today, seconds to 0)
424 * yesterday (time is set to 00:00:00)
425 * today (time is set to 00:00:00)
426 * tomorrow (time is set to 00:00:00)
436 assert_se(localtime_r(&x, &tm));
442 else if (streq(t, "today")) {
443 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
446 } else if (streq(t, "yesterday")) {
448 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
451 } else if (streq(t, "tomorrow")) {
453 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
456 } else if (t[0] == '+') {
458 r = parse_sec(t+1, &plus);
463 } else if (t[0] == '-') {
465 r = parse_sec(t+1, &minus);
471 } else if (endswith(t, " ago")) {
472 _cleanup_free_ char *z;
474 z = strndup(t, strlen(t) - 4);
478 r = parse_sec(z, &minus);
485 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
488 if (!startswith_no_case(t, day_nr[i].name))
491 skip = strlen(day_nr[i].name);
495 weekday = day_nr[i].nr;
501 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
506 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
511 k = strptime(t, "%y-%m-%d %H:%M", &tm);
518 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
525 k = strptime(t, "%y-%m-%d", &tm);
527 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
532 k = strptime(t, "%Y-%m-%d", &tm);
534 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
539 k = strptime(t, "%H:%M:%S", &tm);
544 k = strptime(t, "%H:%M", &tm);
554 if (x == (time_t) -1)
557 if (weekday >= 0 && tm.tm_wday != weekday)
560 ret = (usec_t) x * USEC_PER_SEC;
573 int parse_sec(const char *t, usec_t *usec) {
574 static const struct {
578 { "seconds", USEC_PER_SEC },
579 { "second", USEC_PER_SEC },
580 { "sec", USEC_PER_SEC },
581 { "s", USEC_PER_SEC },
582 { "minutes", USEC_PER_MINUTE },
583 { "minute", USEC_PER_MINUTE },
584 { "min", USEC_PER_MINUTE },
585 { "months", USEC_PER_MONTH },
586 { "month", USEC_PER_MONTH },
587 { "msec", USEC_PER_MSEC },
588 { "ms", USEC_PER_MSEC },
589 { "m", USEC_PER_MINUTE },
590 { "hours", USEC_PER_HOUR },
591 { "hour", USEC_PER_HOUR },
592 { "hr", USEC_PER_HOUR },
593 { "h", USEC_PER_HOUR },
594 { "days", USEC_PER_DAY },
595 { "day", USEC_PER_DAY },
596 { "d", USEC_PER_DAY },
597 { "weeks", USEC_PER_WEEK },
598 { "week", USEC_PER_WEEK },
599 { "w", USEC_PER_WEEK },
600 { "years", USEC_PER_YEAR },
601 { "year", USEC_PER_YEAR },
602 { "y", USEC_PER_YEAR },
605 { "", USEC_PER_SEC }, /* default is sec */
610 bool something = false;
621 p += strspn(p, WHITESPACE);
631 l = strtoll(p, &e, 10);
643 z = strtoll(b, &e, 10);
658 e += strspn(e, WHITESPACE);
660 for (i = 0; i < ELEMENTSOF(table); i++)
661 if (startswith(e, table[i].suffix)) {
662 usec_t k = (usec_t) z * table[i].usec;
667 r += (usec_t) l * table[i].usec + k;
668 p = e + strlen(table[i].suffix);
674 if (i >= ELEMENTSOF(table))
684 int parse_nsec(const char *t, nsec_t *nsec) {
685 static const struct {
689 { "seconds", NSEC_PER_SEC },
690 { "second", NSEC_PER_SEC },
691 { "sec", NSEC_PER_SEC },
692 { "s", NSEC_PER_SEC },
693 { "minutes", NSEC_PER_MINUTE },
694 { "minute", NSEC_PER_MINUTE },
695 { "min", NSEC_PER_MINUTE },
696 { "months", NSEC_PER_MONTH },
697 { "month", NSEC_PER_MONTH },
698 { "msec", NSEC_PER_MSEC },
699 { "ms", NSEC_PER_MSEC },
700 { "m", NSEC_PER_MINUTE },
701 { "hours", NSEC_PER_HOUR },
702 { "hour", NSEC_PER_HOUR },
703 { "hr", NSEC_PER_HOUR },
704 { "h", NSEC_PER_HOUR },
705 { "days", NSEC_PER_DAY },
706 { "day", NSEC_PER_DAY },
707 { "d", NSEC_PER_DAY },
708 { "weeks", NSEC_PER_WEEK },
709 { "week", NSEC_PER_WEEK },
710 { "w", NSEC_PER_WEEK },
711 { "years", NSEC_PER_YEAR },
712 { "year", NSEC_PER_YEAR },
713 { "y", NSEC_PER_YEAR },
714 { "usec", NSEC_PER_USEC },
715 { "us", NSEC_PER_USEC },
718 { "", 1ULL }, /* default is nsec */
723 bool something = false;
734 p += strspn(p, WHITESPACE);
744 l = strtoll(p, &e, 10);
756 z = strtoll(b, &e, 10);
771 e += strspn(e, WHITESPACE);
773 for (i = 0; i < ELEMENTSOF(table); i++)
774 if (startswith(e, table[i].suffix)) {
775 nsec_t k = (nsec_t) z * table[i].nsec;
780 r += (nsec_t) l * table[i].nsec + k;
781 p = e + strlen(table[i].suffix);
787 if (i >= ELEMENTSOF(table))
797 bool ntp_synced(void) {
798 struct timex txc = {};
800 if (adjtimex(&txc) < 0)
803 if (txc.status & STA_UNSYNC)