]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/time-util.c
0c6deb66f4242367192a8da54a9894be7c4e4b0c
[thirdparty/systemd.git] / src / shared / time-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <time.h>
23 #include <string.h>
24
25 #include "util.h"
26 #include "time-util.h"
27
28 usec_t now(clockid_t clock_id) {
29 struct timespec ts;
30
31 assert_se(clock_gettime(clock_id, &ts) == 0);
32
33 return timespec_load(&ts);
34 }
35
36 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
37 assert(ts);
38
39 ts->realtime = now(CLOCK_REALTIME);
40 ts->monotonic = now(CLOCK_MONOTONIC);
41
42 return ts;
43 }
44
45 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
46 int64_t delta;
47 assert(ts);
48
49 if (u == (usec_t) -1) {
50 ts->realtime = ts->monotonic = (usec_t) -1;
51 return ts;
52 }
53
54 ts->realtime = u;
55
56 if (u == 0)
57 ts->monotonic = 0;
58 else {
59 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
60
61 ts->monotonic = now(CLOCK_MONOTONIC);
62
63 if ((int64_t) ts->monotonic > delta)
64 ts->monotonic -= delta;
65 else
66 ts->monotonic = 0;
67 }
68
69 return ts;
70 }
71
72 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
73 int64_t delta;
74 assert(ts);
75
76 if (u == (usec_t) -1) {
77 ts->realtime = ts->monotonic = (usec_t) -1;
78 return ts;
79 }
80
81 ts->monotonic = u;
82 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
83
84 ts->realtime = now(CLOCK_REALTIME);
85 if ((int64_t) ts->realtime > delta)
86 ts->realtime -= delta;
87 else
88 ts->realtime = 0;
89
90 return ts;
91 }
92
93 usec_t timespec_load(const struct timespec *ts) {
94 assert(ts);
95
96 if (ts->tv_sec == (time_t) -1 &&
97 ts->tv_nsec == (long) -1)
98 return (usec_t) -1;
99
100 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
101 return (usec_t) -1;
102
103 return
104 (usec_t) ts->tv_sec * USEC_PER_SEC +
105 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
106 }
107
108 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
109 assert(ts);
110
111 if (u == (usec_t) -1) {
112 ts->tv_sec = (time_t) -1;
113 ts->tv_nsec = (long) -1;
114 return ts;
115 }
116
117 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
118 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
119
120 return ts;
121 }
122
123 usec_t timeval_load(const struct timeval *tv) {
124 assert(tv);
125
126 if (tv->tv_sec == (time_t) -1 &&
127 tv->tv_usec == (suseconds_t) -1)
128 return (usec_t) -1;
129
130 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
131 return (usec_t) -1;
132
133 return
134 (usec_t) tv->tv_sec * USEC_PER_SEC +
135 (usec_t) tv->tv_usec;
136 }
137
138 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
139 assert(tv);
140
141 if (u == (usec_t) -1) {
142 tv->tv_sec = (time_t) -1;
143 tv->tv_usec = (suseconds_t) -1;
144 return tv;
145 }
146
147 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
148 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
149
150 return tv;
151 }
152
153 char *format_timestamp(char *buf, size_t l, usec_t t) {
154 struct tm tm;
155 time_t sec;
156
157 assert(buf);
158 assert(l > 0);
159
160 if (t <= 0)
161 return NULL;
162
163 sec = (time_t) (t / USEC_PER_SEC);
164
165 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
166 return NULL;
167
168 return buf;
169 }
170
171 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
172 usec_t n, d;
173
174 n = now(CLOCK_REALTIME);
175
176 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
177 return NULL;
178
179 d = n - t;
180
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));
218 else if (d > 0)
219 snprintf(buf, l, "%lluus ago",
220 (unsigned long long) d);
221 else
222 snprintf(buf, l, "now");
223
224 buf[l-1] = 0;
225 return buf;
226 }
227
228 char *format_timespan(char *buf, size_t l, usec_t t) {
229 static const struct {
230 const char *suffix;
231 usec_t usec;
232 } table[] = {
233 { "y", USEC_PER_YEAR },
234 { "month", USEC_PER_MONTH },
235 { "w", USEC_PER_WEEK },
236 { "d", USEC_PER_DAY },
237 { "h", USEC_PER_HOUR },
238 { "min", USEC_PER_MINUTE },
239 { "s", USEC_PER_SEC },
240 { "ms", USEC_PER_MSEC },
241 { "us", 1 },
242 };
243
244 unsigned i;
245 char *p = buf;
246
247 assert(buf);
248 assert(l > 0);
249
250 if (t == (usec_t) -1)
251 return NULL;
252
253 if (t == 0) {
254 snprintf(p, l, "0");
255 p[l-1] = 0;
256 return p;
257 }
258
259 /* The result of this function can be parsed with parse_usec */
260
261 for (i = 0; i < ELEMENTSOF(table); i++) {
262 int k;
263 size_t n;
264
265 if (t < table[i].usec)
266 continue;
267
268 if (l <= 1)
269 break;
270
271 k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
272 n = MIN((size_t) k, l);
273
274 l -= n;
275 p += n;
276
277 t %= table[i].usec;
278 }
279
280 *p = 0;
281
282 return buf;
283 }
284
285 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
286
287 assert(f);
288 assert(name);
289 assert(t);
290
291 if (!dual_timestamp_is_set(t))
292 return;
293
294 fprintf(f, "%s=%llu %llu\n",
295 name,
296 (unsigned long long) t->realtime,
297 (unsigned long long) t->monotonic);
298 }
299
300 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
301 unsigned long long a, b;
302
303 assert(value);
304 assert(t);
305
306 if (sscanf(value, "%lli %llu", &a, &b) != 2)
307 log_debug("Failed to parse finish timestamp value %s", value);
308 else {
309 t->realtime = a;
310 t->monotonic = b;
311 }
312 }
313
314 int parse_timestamp(const char *t, usec_t *usec) {
315 static const struct {
316 const char *name;
317 const int nr;
318 } day_nr[] = {
319 { "Sunday", 0 },
320 { "Sun", 0 },
321 { "Monday", 1 },
322 { "Mon", 1 },
323 { "Tuesday", 2 },
324 { "Tue", 2 },
325 { "Wednesday", 3 },
326 { "Wed", 3 },
327 { "Thursday", 4 },
328 { "Thu", 4 },
329 { "Friday", 5 },
330 { "Fri", 5 },
331 { "Saturday", 6 },
332 { "Sat", 6 },
333 };
334
335 const char *k;
336 struct tm tm, copy;
337 time_t x;
338 usec_t plus = 0, minus = 0, ret;
339 int r, weekday = -1;
340 unsigned i;
341
342 /*
343 * Allowed syntaxes:
344 *
345 * 2012-09-22 16:34:22
346 * 2012-09-22 16:34 (seconds will be set to 0)
347 * 2012-09-22 (time will be set to 00:00:00)
348 * 16:34:22 (date will be set to today)
349 * 16:34 (date will be set to today, seconds to 0)
350 * now
351 * yesterday (time is set to 00:00:00)
352 * today (time is set to 00:00:00)
353 * tomorrow (time is set to 00:00:00)
354 * +5min
355 * -5days
356 *
357 */
358
359 assert(t);
360 assert(usec);
361
362 x = time(NULL);
363 assert_se(localtime_r(&x, &tm));
364 tm.tm_isdst = -1;
365
366 if (streq(t, "now"))
367 goto finish;
368
369 else if (streq(t, "today")) {
370 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
371 goto finish;
372
373 } else if (streq(t, "yesterday")) {
374 tm.tm_mday --;
375 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
376 goto finish;
377
378 } else if (streq(t, "tomorrow")) {
379 tm.tm_mday ++;
380 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
381 goto finish;
382
383 } else if (t[0] == '+') {
384
385 r = parse_usec(t+1, &plus);
386 if (r < 0)
387 return r;
388
389 goto finish;
390 } else if (t[0] == '-') {
391
392 r = parse_usec(t+1, &minus);
393 if (r < 0)
394 return r;
395
396 goto finish;
397
398 } else if (endswith(t, " ago")) {
399 _cleanup_free_ char *z;
400
401 z = strndup(t, strlen(t) - 4);
402 if (!z)
403 return -ENOMEM;
404
405 r = parse_usec(z, &minus);
406 if (r < 0)
407 return r;
408
409 goto finish;
410 }
411
412 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
413 size_t skip;
414
415 if (!startswith_no_case(t, day_nr[i].name))
416 continue;
417
418 skip = strlen(day_nr[i].name);
419 if (t[skip] != ' ')
420 continue;
421
422 weekday = day_nr[i].nr;
423 t += skip + 1;
424 break;
425 }
426
427 copy = tm;
428 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
429 if (k && *k == 0)
430 goto finish;
431
432 tm = copy;
433 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
434 if (k && *k == 0)
435 goto finish;
436
437 tm = copy;
438 k = strptime(t, "%y-%m-%d %H:%M", &tm);
439 if (k && *k == 0) {
440 tm.tm_sec = 0;
441 goto finish;
442 }
443
444 tm = copy;
445 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
446 if (k && *k == 0) {
447 tm.tm_sec = 0;
448 goto finish;
449 }
450
451 tm = copy;
452 k = strptime(t, "%y-%m-%d", &tm);
453 if (k && *k == 0) {
454 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
455 goto finish;
456 }
457
458 tm = copy;
459 k = strptime(t, "%Y-%m-%d", &tm);
460 if (k && *k == 0) {
461 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
462 goto finish;
463 }
464
465 tm = copy;
466 k = strptime(t, "%H:%M:%S", &tm);
467 if (k && *k == 0)
468 goto finish;
469
470 tm = copy;
471 k = strptime(t, "%H:%M", &tm);
472 if (k && *k == 0) {
473 tm.tm_sec = 0;
474 goto finish;
475 }
476
477 return -EINVAL;
478
479 finish:
480 x = mktime(&tm);
481 if (x == (time_t) -1)
482 return -EINVAL;
483
484 if (weekday >= 0 && tm.tm_wday != weekday)
485 return -EINVAL;
486
487 ret = (usec_t) x * USEC_PER_SEC;
488
489 ret += plus;
490 if (ret > minus)
491 ret -= minus;
492 else
493 ret = 0;
494
495 *usec = ret;
496
497 return 0;
498 }
499
500 int parse_usec(const char *t, usec_t *usec) {
501 static const struct {
502 const char *suffix;
503 usec_t usec;
504 } table[] = {
505 { "seconds", USEC_PER_SEC },
506 { "second", USEC_PER_SEC },
507 { "sec", USEC_PER_SEC },
508 { "s", USEC_PER_SEC },
509 { "minutes", USEC_PER_MINUTE },
510 { "minute", USEC_PER_MINUTE },
511 { "min", USEC_PER_MINUTE },
512 { "months", USEC_PER_MONTH },
513 { "month", USEC_PER_MONTH },
514 { "msec", USEC_PER_MSEC },
515 { "ms", USEC_PER_MSEC },
516 { "m", USEC_PER_MINUTE },
517 { "hours", USEC_PER_HOUR },
518 { "hour", USEC_PER_HOUR },
519 { "hr", USEC_PER_HOUR },
520 { "h", USEC_PER_HOUR },
521 { "days", USEC_PER_DAY },
522 { "day", USEC_PER_DAY },
523 { "d", USEC_PER_DAY },
524 { "weeks", USEC_PER_WEEK },
525 { "week", USEC_PER_WEEK },
526 { "w", USEC_PER_WEEK },
527 { "years", USEC_PER_YEAR },
528 { "year", USEC_PER_YEAR },
529 { "y", USEC_PER_YEAR },
530 { "usec", 1ULL },
531 { "us", 1ULL },
532 { "", USEC_PER_SEC }, /* default is sec */
533 };
534
535 const char *p;
536 usec_t r = 0;
537
538 assert(t);
539 assert(usec);
540
541 p = t;
542 do {
543 long long l;
544 char *e;
545 unsigned i;
546
547 errno = 0;
548 l = strtoll(p, &e, 10);
549
550 if (errno > 0)
551 return -errno;
552
553 if (l < 0)
554 return -ERANGE;
555
556 if (e == p)
557 return -EINVAL;
558
559 e += strspn(e, WHITESPACE);
560
561 for (i = 0; i < ELEMENTSOF(table); i++)
562 if (startswith(e, table[i].suffix)) {
563 r += (usec_t) l * table[i].usec;
564 p = e + strlen(table[i].suffix);
565 break;
566 }
567
568 if (i >= ELEMENTSOF(table))
569 return -EINVAL;
570
571 } while (*p != 0);
572
573 *usec = r;
574
575 return 0;
576 }
577
578 int parse_nsec(const char *t, nsec_t *nsec) {
579 static const struct {
580 const char *suffix;
581 nsec_t nsec;
582 } table[] = {
583 { "seconds", NSEC_PER_SEC },
584 { "second", NSEC_PER_SEC },
585 { "sec", NSEC_PER_SEC },
586 { "s", NSEC_PER_SEC },
587 { "minutes", NSEC_PER_MINUTE },
588 { "minute", NSEC_PER_MINUTE },
589 { "min", NSEC_PER_MINUTE },
590 { "months", NSEC_PER_MONTH },
591 { "month", NSEC_PER_MONTH },
592 { "msec", NSEC_PER_MSEC },
593 { "ms", NSEC_PER_MSEC },
594 { "m", NSEC_PER_MINUTE },
595 { "hours", NSEC_PER_HOUR },
596 { "hour", NSEC_PER_HOUR },
597 { "hr", NSEC_PER_HOUR },
598 { "h", NSEC_PER_HOUR },
599 { "days", NSEC_PER_DAY },
600 { "day", NSEC_PER_DAY },
601 { "d", NSEC_PER_DAY },
602 { "weeks", NSEC_PER_WEEK },
603 { "week", NSEC_PER_WEEK },
604 { "w", NSEC_PER_WEEK },
605 { "years", NSEC_PER_YEAR },
606 { "year", NSEC_PER_YEAR },
607 { "y", NSEC_PER_YEAR },
608 { "usec", NSEC_PER_USEC },
609 { "us", NSEC_PER_USEC },
610 { "nsec", 1ULL },
611 { "ns", 1ULL },
612 { "", 1ULL }, /* default is nsec */
613 };
614
615 const char *p;
616 nsec_t r = 0;
617
618 assert(t);
619 assert(nsec);
620
621 p = t;
622 do {
623 long long l;
624 char *e;
625 unsigned i;
626
627 errno = 0;
628 l = strtoll(p, &e, 10);
629
630 if (errno > 0)
631 return -errno;
632
633 if (l < 0)
634 return -ERANGE;
635
636 if (e == p)
637 return -EINVAL;
638
639 e += strspn(e, WHITESPACE);
640
641 for (i = 0; i < ELEMENTSOF(table); i++)
642 if (startswith(e, table[i].suffix)) {
643 r += (nsec_t) l * table[i].nsec;
644 p = e + strlen(table[i].suffix);
645 break;
646 }
647
648 if (i >= ELEMENTSOF(table))
649 return -EINVAL;
650
651 } while (*p != 0);
652
653 *nsec = r;
654
655 return 0;
656 }