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_relative(char *buf, size_t l, usec_t t) {
174 n = now(CLOCK_REALTIME);
176 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
181 if (d >= USEC_PER_YEAR)
182 snprintf(buf, l, "%llu years %llu months ago",
183 (unsigned long long) (d / USEC_PER_YEAR),
184 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
185 else if (d >= USEC_PER_MONTH)
186 snprintf(buf, l, "%llu months %llu days ago",
187 (unsigned long long) (d / USEC_PER_MONTH),
188 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
189 else if (d >= USEC_PER_WEEK)
190 snprintf(buf, l, "%llu weeks %llu days ago",
191 (unsigned long long) (d / USEC_PER_WEEK),
192 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
193 else if (d >= 2*USEC_PER_DAY)
194 snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
195 else if (d >= 25*USEC_PER_HOUR)
196 snprintf(buf, l, "1 day %lluh ago",
197 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
198 else if (d >= 6*USEC_PER_HOUR)
199 snprintf(buf, l, "%lluh ago",
200 (unsigned long long) (d / USEC_PER_HOUR));
201 else if (d >= USEC_PER_HOUR)
202 snprintf(buf, l, "%lluh %llumin ago",
203 (unsigned long long) (d / USEC_PER_HOUR),
204 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
205 else if (d >= 5*USEC_PER_MINUTE)
206 snprintf(buf, l, "%llumin ago",
207 (unsigned long long) (d / USEC_PER_MINUTE));
208 else if (d >= USEC_PER_MINUTE)
209 snprintf(buf, l, "%llumin %llus ago",
210 (unsigned long long) (d / USEC_PER_MINUTE),
211 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
212 else if (d >= USEC_PER_SEC)
213 snprintf(buf, l, "%llus ago",
214 (unsigned long long) (d / USEC_PER_SEC));
215 else if (d >= USEC_PER_MSEC)
216 snprintf(buf, l, "%llums ago",
217 (unsigned long long) (d / USEC_PER_MSEC));
219 snprintf(buf, l, "%lluus ago",
220 (unsigned long long) d);
222 snprintf(buf, l, "now");
228 char *format_timespan(char *buf, size_t l, usec_t t) {
229 static const struct {
233 { "w", USEC_PER_WEEK },
234 { "d", USEC_PER_DAY },
235 { "h", USEC_PER_HOUR },
236 { "min", USEC_PER_MINUTE },
237 { "s", USEC_PER_SEC },
238 { "ms", USEC_PER_MSEC },
248 if (t == (usec_t) -1)
257 /* The result of this function can be parsed with parse_usec */
259 for (i = 0; i < ELEMENTSOF(table); i++) {
263 if (t < table[i].usec)
269 k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
270 n = MIN((size_t) k, l);
283 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
289 if (!dual_timestamp_is_set(t))
292 fprintf(f, "%s=%llu %llu\n",
294 (unsigned long long) t->realtime,
295 (unsigned long long) t->monotonic);
298 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
299 unsigned long long a, b;
304 if (sscanf(value, "%lli %llu", &a, &b) != 2)
305 log_debug("Failed to parse finish timestamp value %s", value);
312 int parse_timestamp(const char *t, usec_t *usec) {
313 static const struct {
336 usec_t plus = 0, minus = 0, ret;
343 * 2012-09-22 16:34:22
344 * 2012-09-22 16:34 (seconds will be set to 0)
345 * 2012-09-22 (time will be set to 00:00:00)
346 * 16:34:22 (date will be set to today)
347 * 16:34 (date will be set to today, seconds to 0)
349 * yesterday (time is set to 00:00:00)
350 * today (time is set to 00:00:00)
351 * tomorrow (time is set to 00:00:00)
361 assert_se(localtime_r(&x, &tm));
367 else if (streq(t, "today")) {
368 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
371 } else if (streq(t, "yesterday")) {
373 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
376 } else if (streq(t, "tomorrow")) {
378 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
381 } else if (t[0] == '+') {
383 r = parse_usec(t+1, &plus);
388 } else if (t[0] == '-') {
390 r = parse_usec(t+1, &minus);
396 } else if (endswith(t, " ago")) {
397 _cleanup_free_ char *z;
399 z = strndup(t, strlen(t) - 4);
403 r = parse_usec(z, &minus);
410 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
413 if (!startswith_no_case(t, day_nr[i].name))
416 skip = strlen(day_nr[i].name);
420 weekday = day_nr[i].nr;
426 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
431 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
436 k = strptime(t, "%y-%m-%d %H:%M", &tm);
443 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
450 k = strptime(t, "%y-%m-%d", &tm);
452 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
457 k = strptime(t, "%Y-%m-%d", &tm);
459 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
464 k = strptime(t, "%H:%M:%S", &tm);
469 k = strptime(t, "%H:%M", &tm);
479 if (x == (time_t) -1)
482 if (weekday >= 0 && tm.tm_wday != weekday)
485 ret = (usec_t) x * USEC_PER_SEC;
498 int parse_usec(const char *t, usec_t *usec) {
499 static const struct {
503 { "seconds", USEC_PER_SEC },
504 { "second", USEC_PER_SEC },
505 { "sec", USEC_PER_SEC },
506 { "s", USEC_PER_SEC },
507 { "minutes", USEC_PER_MINUTE },
508 { "minute", USEC_PER_MINUTE },
509 { "min", USEC_PER_MINUTE },
510 { "months", USEC_PER_MONTH },
511 { "month", USEC_PER_MONTH },
512 { "msec", USEC_PER_MSEC },
513 { "ms", USEC_PER_MSEC },
514 { "m", USEC_PER_MINUTE },
515 { "hours", USEC_PER_HOUR },
516 { "hour", USEC_PER_HOUR },
517 { "hr", USEC_PER_HOUR },
518 { "h", USEC_PER_HOUR },
519 { "days", USEC_PER_DAY },
520 { "day", USEC_PER_DAY },
521 { "d", USEC_PER_DAY },
522 { "weeks", USEC_PER_WEEK },
523 { "week", USEC_PER_WEEK },
524 { "w", USEC_PER_WEEK },
525 { "years", USEC_PER_YEAR },
526 { "year", USEC_PER_YEAR },
527 { "y", USEC_PER_YEAR },
530 { "", USEC_PER_SEC }, /* default is sec */
546 l = strtoll(p, &e, 10);
557 e += strspn(e, WHITESPACE);
559 for (i = 0; i < ELEMENTSOF(table); i++)
560 if (startswith(e, table[i].suffix)) {
561 r += (usec_t) l * table[i].usec;
562 p = e + strlen(table[i].suffix);
566 if (i >= ELEMENTSOF(table))
576 int parse_nsec(const char *t, nsec_t *nsec) {
577 static const struct {
581 { "seconds", NSEC_PER_SEC },
582 { "second", NSEC_PER_SEC },
583 { "sec", NSEC_PER_SEC },
584 { "s", NSEC_PER_SEC },
585 { "minutes", NSEC_PER_MINUTE },
586 { "minute", NSEC_PER_MINUTE },
587 { "min", NSEC_PER_MINUTE },
588 { "months", NSEC_PER_MONTH },
589 { "month", NSEC_PER_MONTH },
590 { "msec", NSEC_PER_MSEC },
591 { "ms", NSEC_PER_MSEC },
592 { "m", NSEC_PER_MINUTE },
593 { "hours", NSEC_PER_HOUR },
594 { "hour", NSEC_PER_HOUR },
595 { "hr", NSEC_PER_HOUR },
596 { "h", NSEC_PER_HOUR },
597 { "days", NSEC_PER_DAY },
598 { "day", NSEC_PER_DAY },
599 { "d", NSEC_PER_DAY },
600 { "weeks", NSEC_PER_WEEK },
601 { "week", NSEC_PER_WEEK },
602 { "w", NSEC_PER_WEEK },
603 { "years", NSEC_PER_YEAR },
604 { "year", NSEC_PER_YEAR },
605 { "y", NSEC_PER_YEAR },
606 { "usec", NSEC_PER_USEC },
607 { "us", NSEC_PER_USEC },
610 { "", 1ULL }, /* default is nsec */
626 l = strtoll(p, &e, 10);
637 e += strspn(e, WHITESPACE);
639 for (i = 0; i < ELEMENTSOF(table); i++)
640 if (startswith(e, table[i].suffix)) {
641 r += (nsec_t) l * table[i].nsec;
642 p = e + strlen(table[i].suffix);
646 if (i >= ELEMENTSOF(table))