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) {
54 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
56 ts->monotonic = now(CLOCK_MONOTONIC);
58 if ((int64_t) ts->monotonic > delta)
59 ts->monotonic -= delta;
67 usec_t timespec_load(const struct timespec *ts) {
70 if (ts->tv_sec == (time_t) -1 &&
71 ts->tv_nsec == (long) -1)
74 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
78 (usec_t) ts->tv_sec * USEC_PER_SEC +
79 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
82 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
85 if (u == (usec_t) -1) {
86 ts->tv_sec = (time_t) -1;
87 ts->tv_nsec = (long) -1;
91 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
92 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
97 usec_t timeval_load(const struct timeval *tv) {
100 if (tv->tv_sec == (time_t) -1 &&
101 tv->tv_usec == (suseconds_t) -1)
104 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
108 (usec_t) tv->tv_sec * USEC_PER_SEC +
109 (usec_t) tv->tv_usec;
112 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
115 if (u == (usec_t) -1) {
116 tv->tv_sec = (time_t) -1;
117 tv->tv_usec = (suseconds_t) -1;
121 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
122 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
127 char *format_timestamp(char *buf, size_t l, usec_t t) {
137 sec = (time_t) (t / USEC_PER_SEC);
139 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
145 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
148 n = now(CLOCK_REALTIME);
150 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
155 if (d >= USEC_PER_YEAR)
156 snprintf(buf, l, "%llu years %llu months ago",
157 (unsigned long long) (d / USEC_PER_YEAR),
158 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
159 else if (d >= USEC_PER_MONTH)
160 snprintf(buf, l, "%llu months %llu days ago",
161 (unsigned long long) (d / USEC_PER_MONTH),
162 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
163 else if (d >= USEC_PER_WEEK)
164 snprintf(buf, l, "%llu weeks %llu days ago",
165 (unsigned long long) (d / USEC_PER_WEEK),
166 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
167 else if (d >= 2*USEC_PER_DAY)
168 snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
169 else if (d >= 25*USEC_PER_HOUR)
170 snprintf(buf, l, "1 day %lluh ago",
171 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
172 else if (d >= 6*USEC_PER_HOUR)
173 snprintf(buf, l, "%lluh ago",
174 (unsigned long long) (d / USEC_PER_HOUR));
175 else if (d >= USEC_PER_HOUR)
176 snprintf(buf, l, "%lluh %llumin ago",
177 (unsigned long long) (d / USEC_PER_HOUR),
178 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
179 else if (d >= 5*USEC_PER_MINUTE)
180 snprintf(buf, l, "%llumin ago",
181 (unsigned long long) (d / USEC_PER_MINUTE));
182 else if (d >= USEC_PER_MINUTE)
183 snprintf(buf, l, "%llumin %llus ago",
184 (unsigned long long) (d / USEC_PER_MINUTE),
185 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
186 else if (d >= USEC_PER_SEC)
187 snprintf(buf, l, "%llus ago",
188 (unsigned long long) (d / USEC_PER_SEC));
189 else if (d >= USEC_PER_MSEC)
190 snprintf(buf, l, "%llums ago",
191 (unsigned long long) (d / USEC_PER_MSEC));
193 snprintf(buf, l, "%lluus ago",
194 (unsigned long long) d);
196 snprintf(buf, l, "now");
202 char *format_timespan(char *buf, size_t l, usec_t t) {
203 static const struct {
207 { "w", USEC_PER_WEEK },
208 { "d", USEC_PER_DAY },
209 { "h", USEC_PER_HOUR },
210 { "min", USEC_PER_MINUTE },
211 { "s", USEC_PER_SEC },
212 { "ms", USEC_PER_MSEC },
222 if (t == (usec_t) -1)
231 /* The result of this function can be parsed with parse_usec */
233 for (i = 0; i < ELEMENTSOF(table); i++) {
237 if (t < table[i].usec)
243 k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
244 n = MIN((size_t) k, l);
257 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
263 if (!dual_timestamp_is_set(t))
266 fprintf(f, "%s=%llu %llu\n",
268 (unsigned long long) t->realtime,
269 (unsigned long long) t->monotonic);
272 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
273 unsigned long long a, b;
278 if (sscanf(value, "%lli %llu", &a, &b) != 2)
279 log_debug("Failed to parse finish timestamp value %s", value);
286 int parse_timestamp(const char *t, usec_t *usec) {
287 static const struct {
310 usec_t plus = 0, minus = 0, ret;
317 * 2012-09-22 16:34:22
318 * 2012-09-22 16:34 (seconds will be set to 0)
319 * 2012-09-22 (time will be set to 00:00:00)
320 * 16:34:22 (date will be set to today)
321 * 16:34 (date will be set to today, seconds to 0)
323 * yesterday (time is set to 00:00:00)
324 * today (time is set to 00:00:00)
325 * tomorrow (time is set to 00:00:00)
335 assert_se(localtime_r(&x, &tm));
341 else if (streq(t, "today")) {
342 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
345 } else if (streq(t, "yesterday")) {
347 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
350 } else if (streq(t, "tomorrow")) {
352 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
355 } else if (t[0] == '+') {
357 r = parse_usec(t+1, &plus);
362 } else if (t[0] == '-') {
364 r = parse_usec(t+1, &minus);
370 } else if (endswith(t, " ago")) {
371 _cleanup_free_ char *z;
373 z = strndup(t, strlen(t) - 4);
377 r = parse_usec(z, &minus);
384 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
387 if (!startswith_no_case(t, day_nr[i].name))
390 skip = strlen(day_nr[i].name);
394 weekday = day_nr[i].nr;
400 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
405 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
410 k = strptime(t, "%y-%m-%d %H:%M", &tm);
417 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
424 k = strptime(t, "%y-%m-%d", &tm);
426 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
431 k = strptime(t, "%Y-%m-%d", &tm);
433 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
438 k = strptime(t, "%H:%M:%S", &tm);
443 k = strptime(t, "%H:%M", &tm);
453 if (x == (time_t) -1)
456 if (weekday >= 0 && tm.tm_wday != weekday)
459 ret = (usec_t) x * USEC_PER_SEC;
472 int parse_usec(const char *t, usec_t *usec) {
473 static const struct {
477 { "seconds", USEC_PER_SEC },
478 { "second", USEC_PER_SEC },
479 { "sec", USEC_PER_SEC },
480 { "s", USEC_PER_SEC },
481 { "minutes", USEC_PER_MINUTE },
482 { "minute", USEC_PER_MINUTE },
483 { "min", USEC_PER_MINUTE },
484 { "months", USEC_PER_MONTH },
485 { "month", USEC_PER_MONTH },
486 { "msec", USEC_PER_MSEC },
487 { "ms", USEC_PER_MSEC },
488 { "m", USEC_PER_MINUTE },
489 { "hours", USEC_PER_HOUR },
490 { "hour", USEC_PER_HOUR },
491 { "hr", USEC_PER_HOUR },
492 { "h", USEC_PER_HOUR },
493 { "days", USEC_PER_DAY },
494 { "day", USEC_PER_DAY },
495 { "d", USEC_PER_DAY },
496 { "weeks", USEC_PER_WEEK },
497 { "week", USEC_PER_WEEK },
498 { "w", USEC_PER_WEEK },
499 { "years", USEC_PER_YEAR },
500 { "year", USEC_PER_YEAR },
501 { "y", USEC_PER_YEAR },
504 { "", USEC_PER_SEC }, /* default is sec */
520 l = strtoll(p, &e, 10);
531 e += strspn(e, WHITESPACE);
533 for (i = 0; i < ELEMENTSOF(table); i++)
534 if (startswith(e, table[i].suffix)) {
535 r += (usec_t) l * table[i].usec;
536 p = e + strlen(table[i].suffix);
540 if (i >= ELEMENTSOF(table))
550 int parse_nsec(const char *t, nsec_t *nsec) {
551 static const struct {
555 { "seconds", NSEC_PER_SEC },
556 { "second", NSEC_PER_SEC },
557 { "sec", NSEC_PER_SEC },
558 { "s", NSEC_PER_SEC },
559 { "minutes", NSEC_PER_MINUTE },
560 { "minute", NSEC_PER_MINUTE },
561 { "min", NSEC_PER_MINUTE },
562 { "months", NSEC_PER_MONTH },
563 { "month", NSEC_PER_MONTH },
564 { "msec", NSEC_PER_MSEC },
565 { "ms", NSEC_PER_MSEC },
566 { "m", NSEC_PER_MINUTE },
567 { "hours", NSEC_PER_HOUR },
568 { "hour", NSEC_PER_HOUR },
569 { "hr", NSEC_PER_HOUR },
570 { "h", NSEC_PER_HOUR },
571 { "days", NSEC_PER_DAY },
572 { "day", NSEC_PER_DAY },
573 { "d", NSEC_PER_DAY },
574 { "weeks", NSEC_PER_WEEK },
575 { "week", NSEC_PER_WEEK },
576 { "w", NSEC_PER_WEEK },
577 { "years", NSEC_PER_YEAR },
578 { "year", NSEC_PER_YEAR },
579 { "y", NSEC_PER_YEAR },
580 { "usec", NSEC_PER_USEC },
581 { "us", NSEC_PER_USEC },
584 { "", 1ULL }, /* default is nsec */
600 l = strtoll(p, &e, 10);
611 e += strspn(e, WHITESPACE);
613 for (i = 0; i < ELEMENTSOF(table); i++)
614 if (startswith(e, table[i].suffix)) {
615 r += (nsec_t) l * table[i].nsec;
616 p = e + strlen(table[i].suffix);
620 if (i >= ELEMENTSOF(table))