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/>.
26 #include "time-util.h"
28 usec_t now(clockid_t clock_id) {
31 assert_se(clock_gettime(clock_id, &ts) == 0);
33 return timespec_load(&ts);
36 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
39 ts->realtime = now(CLOCK_REALTIME);
40 ts->monotonic = now(CLOCK_MONOTONIC);
45 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
49 if (u == (usec_t) -1) {
50 ts->realtime = ts->monotonic = (usec_t) -1;
59 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
61 ts->monotonic = now(CLOCK_MONOTONIC);
63 if ((int64_t) ts->monotonic > delta)
64 ts->monotonic -= delta;
72 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
76 if (u == (usec_t) -1) {
77 ts->realtime = ts->monotonic = (usec_t) -1;
82 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
84 ts->realtime = now(CLOCK_REALTIME);
85 if ((int64_t) ts->realtime > delta)
86 ts->realtime -= delta;
93 usec_t timespec_load(const struct timespec *ts) {
96 if (ts->tv_sec == (time_t) -1 &&
97 ts->tv_nsec == (long) -1)
100 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
104 (usec_t) ts->tv_sec * USEC_PER_SEC +
105 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
108 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
111 if (u == (usec_t) -1) {
112 ts->tv_sec = (time_t) -1;
113 ts->tv_nsec = (long) -1;
117 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
118 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
123 usec_t timeval_load(const struct timeval *tv) {
126 if (tv->tv_sec == (time_t) -1 &&
127 tv->tv_usec == (suseconds_t) -1)
130 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
134 (usec_t) tv->tv_sec * USEC_PER_SEC +
135 (usec_t) tv->tv_usec;
138 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
141 if (u == (usec_t) -1) {
142 tv->tv_sec = (time_t) -1;
143 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);
153 char *format_timestamp(char *buf, size_t l, usec_t t) {
163 sec = (time_t) (t / USEC_PER_SEC);
165 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
171 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
181 sec = (time_t) (t / USEC_PER_SEC);
182 localtime_r(&sec, &tm);
184 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
186 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", t % USEC_PER_SEC);
187 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
193 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
196 n = now(CLOCK_REALTIME);
198 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
203 if (d >= USEC_PER_YEAR)
204 snprintf(buf, l, "%llu years %llu months ago",
205 (unsigned long long) (d / USEC_PER_YEAR),
206 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
207 else if (d >= USEC_PER_MONTH)
208 snprintf(buf, l, "%llu months %llu days ago",
209 (unsigned long long) (d / USEC_PER_MONTH),
210 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
211 else if (d >= USEC_PER_WEEK)
212 snprintf(buf, l, "%llu weeks %llu days ago",
213 (unsigned long long) (d / USEC_PER_WEEK),
214 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
215 else if (d >= 2*USEC_PER_DAY)
216 snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
217 else if (d >= 25*USEC_PER_HOUR)
218 snprintf(buf, l, "1 day %lluh ago",
219 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
220 else if (d >= 6*USEC_PER_HOUR)
221 snprintf(buf, l, "%lluh ago",
222 (unsigned long long) (d / USEC_PER_HOUR));
223 else if (d >= USEC_PER_HOUR)
224 snprintf(buf, l, "%lluh %llumin ago",
225 (unsigned long long) (d / USEC_PER_HOUR),
226 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
227 else if (d >= 5*USEC_PER_MINUTE)
228 snprintf(buf, l, "%llumin ago",
229 (unsigned long long) (d / USEC_PER_MINUTE));
230 else if (d >= USEC_PER_MINUTE)
231 snprintf(buf, l, "%llumin %llus ago",
232 (unsigned long long) (d / USEC_PER_MINUTE),
233 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
234 else if (d >= USEC_PER_SEC)
235 snprintf(buf, l, "%llus ago",
236 (unsigned long long) (d / USEC_PER_SEC));
237 else if (d >= USEC_PER_MSEC)
238 snprintf(buf, l, "%llums ago",
239 (unsigned long long) (d / USEC_PER_MSEC));
241 snprintf(buf, l, "%lluus ago",
242 (unsigned long long) d);
244 snprintf(buf, l, "now");
250 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
251 static const struct {
255 { "y", USEC_PER_YEAR },
256 { "month", USEC_PER_MONTH },
257 { "w", USEC_PER_WEEK },
258 { "d", USEC_PER_DAY },
259 { "h", USEC_PER_HOUR },
260 { "min", USEC_PER_MINUTE },
261 { "s", USEC_PER_SEC },
262 { "ms", USEC_PER_MSEC },
268 bool something = false;
273 if (t == (usec_t) -1)
282 /* The result of this function can be parsed with parse_sec */
284 for (i = 0; i < ELEMENTSOF(table); i++) {
293 if (t < accuracy && something)
296 if (t < table[i].usec)
302 a = t / table[i].usec;
303 b = t % table[i].usec;
305 /* Let's see if we should shows this in dot notation */
306 if (t < USEC_PER_MINUTE && b > 0) {
311 for (cc = table[i].usec; cc > 1; cc /= 10)
314 for (cc = accuracy; cc > 1; cc /= 10) {
323 (unsigned long long) a,
325 (unsigned long long) b,
333 /* No? Then let's show it normally */
338 (unsigned long long) a,
344 n = MIN((size_t) k, l);
357 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
363 if (!dual_timestamp_is_set(t))
366 fprintf(f, "%s=%llu %llu\n",
368 (unsigned long long) t->realtime,
369 (unsigned long long) t->monotonic);
372 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
373 unsigned long long a, b;
378 if (sscanf(value, "%lli %llu", &a, &b) != 2)
379 log_debug("Failed to parse finish timestamp value %s", value);
386 int parse_timestamp(const char *t, usec_t *usec) {
387 static const struct {
410 usec_t plus = 0, minus = 0, ret;
417 * 2012-09-22 16:34:22
418 * 2012-09-22 16:34 (seconds will be set to 0)
419 * 2012-09-22 (time will be set to 00:00:00)
420 * 16:34:22 (date will be set to today)
421 * 16:34 (date will be set to today, seconds to 0)
423 * yesterday (time is set to 00:00:00)
424 * today (time is set to 00:00:00)
425 * tomorrow (time is set to 00:00:00)
435 assert_se(localtime_r(&x, &tm));
441 else if (streq(t, "today")) {
442 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
445 } else if (streq(t, "yesterday")) {
447 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
450 } else if (streq(t, "tomorrow")) {
452 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
455 } else if (t[0] == '+') {
457 r = parse_sec(t+1, &plus);
462 } else if (t[0] == '-') {
464 r = parse_sec(t+1, &minus);
470 } else if (endswith(t, " ago")) {
471 _cleanup_free_ char *z;
473 z = strndup(t, strlen(t) - 4);
477 r = parse_sec(z, &minus);
484 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
487 if (!startswith_no_case(t, day_nr[i].name))
490 skip = strlen(day_nr[i].name);
494 weekday = day_nr[i].nr;
500 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
505 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
510 k = strptime(t, "%y-%m-%d %H:%M", &tm);
517 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
524 k = strptime(t, "%y-%m-%d", &tm);
526 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
531 k = strptime(t, "%Y-%m-%d", &tm);
533 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
538 k = strptime(t, "%H:%M:%S", &tm);
543 k = strptime(t, "%H:%M", &tm);
553 if (x == (time_t) -1)
556 if (weekday >= 0 && tm.tm_wday != weekday)
559 ret = (usec_t) x * USEC_PER_SEC;
572 int parse_sec(const char *t, usec_t *usec) {
573 static const struct {
577 { "seconds", USEC_PER_SEC },
578 { "second", USEC_PER_SEC },
579 { "sec", USEC_PER_SEC },
580 { "s", USEC_PER_SEC },
581 { "minutes", USEC_PER_MINUTE },
582 { "minute", USEC_PER_MINUTE },
583 { "min", USEC_PER_MINUTE },
584 { "months", USEC_PER_MONTH },
585 { "month", USEC_PER_MONTH },
586 { "msec", USEC_PER_MSEC },
587 { "ms", USEC_PER_MSEC },
588 { "m", USEC_PER_MINUTE },
589 { "hours", USEC_PER_HOUR },
590 { "hour", USEC_PER_HOUR },
591 { "hr", USEC_PER_HOUR },
592 { "h", USEC_PER_HOUR },
593 { "days", USEC_PER_DAY },
594 { "day", USEC_PER_DAY },
595 { "d", USEC_PER_DAY },
596 { "weeks", USEC_PER_WEEK },
597 { "week", USEC_PER_WEEK },
598 { "w", USEC_PER_WEEK },
599 { "years", USEC_PER_YEAR },
600 { "year", USEC_PER_YEAR },
601 { "y", USEC_PER_YEAR },
604 { "", USEC_PER_SEC }, /* default is sec */
609 bool something = false;
620 p += strspn(p, WHITESPACE);
630 l = strtoll(p, &e, 10);
642 z = strtoll(b, &e, 10);
657 e += strspn(e, WHITESPACE);
659 for (i = 0; i < ELEMENTSOF(table); i++)
660 if (startswith(e, table[i].suffix)) {
661 usec_t k = (usec_t) z * table[i].usec;
666 r += (usec_t) l * table[i].usec + k;
667 p = e + strlen(table[i].suffix);
673 if (i >= ELEMENTSOF(table))
683 int parse_nsec(const char *t, nsec_t *nsec) {
684 static const struct {
688 { "seconds", NSEC_PER_SEC },
689 { "second", NSEC_PER_SEC },
690 { "sec", NSEC_PER_SEC },
691 { "s", NSEC_PER_SEC },
692 { "minutes", NSEC_PER_MINUTE },
693 { "minute", NSEC_PER_MINUTE },
694 { "min", NSEC_PER_MINUTE },
695 { "months", NSEC_PER_MONTH },
696 { "month", NSEC_PER_MONTH },
697 { "msec", NSEC_PER_MSEC },
698 { "ms", NSEC_PER_MSEC },
699 { "m", NSEC_PER_MINUTE },
700 { "hours", NSEC_PER_HOUR },
701 { "hour", NSEC_PER_HOUR },
702 { "hr", NSEC_PER_HOUR },
703 { "h", NSEC_PER_HOUR },
704 { "days", NSEC_PER_DAY },
705 { "day", NSEC_PER_DAY },
706 { "d", NSEC_PER_DAY },
707 { "weeks", NSEC_PER_WEEK },
708 { "week", NSEC_PER_WEEK },
709 { "w", NSEC_PER_WEEK },
710 { "years", NSEC_PER_YEAR },
711 { "year", NSEC_PER_YEAR },
712 { "y", NSEC_PER_YEAR },
713 { "usec", NSEC_PER_USEC },
714 { "us", NSEC_PER_USEC },
717 { "", 1ULL }, /* default is nsec */
722 bool something = false;
733 p += strspn(p, WHITESPACE);
743 l = strtoll(p, &e, 10);
755 z = strtoll(b, &e, 10);
770 e += strspn(e, WHITESPACE);
772 for (i = 0; i < ELEMENTSOF(table); i++)
773 if (startswith(e, table[i].suffix)) {
774 nsec_t k = (nsec_t) z * table[i].nsec;
779 r += (nsec_t) l * table[i].nsec + k;
780 p = e + strlen(table[i].suffix);
786 if (i >= ELEMENTSOF(table))