]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
analyze: add emacs mode header
[thirdparty/systemd.git] / src / shared / time-util.c
CommitLineData
9a98c7a1
LP
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
28usec_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
36dual_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
45dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
46 int64_t delta;
47 assert(ts);
48
49 ts->realtime = u;
50
51 if (u == 0)
52 ts->monotonic = 0;
53 else {
54 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
55
56 ts->monotonic = now(CLOCK_MONOTONIC);
57
58 if ((int64_t) ts->monotonic > delta)
59 ts->monotonic -= delta;
60 else
61 ts->monotonic = 0;
62 }
63
64 return ts;
65}
66
67usec_t timespec_load(const struct timespec *ts) {
68 assert(ts);
69
70 if (ts->tv_sec == (time_t) -1 &&
71 ts->tv_nsec == (long) -1)
72 return (usec_t) -1;
73
74 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
75 return (usec_t) -1;
76
77 return
78 (usec_t) ts->tv_sec * USEC_PER_SEC +
79 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
80}
81
82struct timespec *timespec_store(struct timespec *ts, usec_t u) {
83 assert(ts);
84
85 if (u == (usec_t) -1) {
86 ts->tv_sec = (time_t) -1;
87 ts->tv_nsec = (long) -1;
88 return ts;
89 }
90
91 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
92 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
93
94 return ts;
95}
96
97usec_t timeval_load(const struct timeval *tv) {
98 assert(tv);
99
100 if (tv->tv_sec == (time_t) -1 &&
101 tv->tv_usec == (suseconds_t) -1)
102 return (usec_t) -1;
103
104 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
105 return (usec_t) -1;
106
107 return
108 (usec_t) tv->tv_sec * USEC_PER_SEC +
109 (usec_t) tv->tv_usec;
110}
111
112struct timeval *timeval_store(struct timeval *tv, usec_t u) {
113 assert(tv);
114
115 if (u == (usec_t) -1) {
116 tv->tv_sec = (time_t) -1;
117 tv->tv_usec = (suseconds_t) -1;
118 return tv;
119 }
120
121 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
122 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
123
124 return tv;
125}
126
127char *format_timestamp(char *buf, size_t l, usec_t t) {
128 struct tm tm;
129 time_t sec;
130
131 assert(buf);
132 assert(l > 0);
133
134 if (t <= 0)
135 return NULL;
136
137 sec = (time_t) (t / USEC_PER_SEC);
138
139 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
140 return NULL;
141
142 return buf;
143}
144
bbb8486e 145char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
9a98c7a1
LP
146 usec_t n, d;
147
148 n = now(CLOCK_REALTIME);
149
150 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
151 return NULL;
152
153 d = n - t;
154
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));
192 else if (d > 0)
193 snprintf(buf, l, "%lluus ago",
194 (unsigned long long) d);
195 else
196 snprintf(buf, l, "now");
197
198 buf[l-1] = 0;
199 return buf;
200}
201
202char *format_timespan(char *buf, size_t l, usec_t t) {
203 static const struct {
204 const char *suffix;
205 usec_t usec;
206 } table[] = {
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 },
213 { "us", 1 },
214 };
215
216 unsigned i;
217 char *p = buf;
218
219 assert(buf);
220 assert(l > 0);
221
222 if (t == (usec_t) -1)
223 return NULL;
224
225 if (t == 0) {
226 snprintf(p, l, "0");
227 p[l-1] = 0;
228 return p;
229 }
230
231 /* The result of this function can be parsed with parse_usec */
232
233 for (i = 0; i < ELEMENTSOF(table); i++) {
234 int k;
235 size_t n;
236
237 if (t < table[i].usec)
238 continue;
239
240 if (l <= 1)
241 break;
242
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);
245
246 l -= n;
247 p += n;
248
249 t %= table[i].usec;
250 }
251
252 *p = 0;
253
254 return buf;
255}
256
257void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
258
259 assert(f);
260 assert(name);
261 assert(t);
262
263 if (!dual_timestamp_is_set(t))
264 return;
265
266 fprintf(f, "%s=%llu %llu\n",
267 name,
268 (unsigned long long) t->realtime,
269 (unsigned long long) t->monotonic);
270}
271
272void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
273 unsigned long long a, b;
274
275 assert(value);
276 assert(t);
277
278 if (sscanf(value, "%lli %llu", &a, &b) != 2)
279 log_debug("Failed to parse finish timestamp value %s", value);
280 else {
281 t->realtime = a;
282 t->monotonic = b;
283 }
284}
285
286int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
287 static const struct {
288 const char *name;
289 const int nr;
290 } day_nr[] = {
291 { "Sunday", 0 },
292 { "Sun", 0 },
293 { "Monday", 1 },
294 { "Mon", 1 },
295 { "Tuesday", 2 },
296 { "Tue", 2 },
297 { "Wednesday", 3 },
298 { "Wed", 3 },
299 { "Thursday", 4 },
300 { "Thu", 4 },
301 { "Friday", 5 },
302 { "Fri", 5 },
303 { "Saturday", 6 },
304 { "Sat", 6 },
305 };
306
9a98c7a1
LP
307 const char *k;
308 struct tm tm, copy;
309 time_t x;
310 usec_t plus = 0, minus = 0, ret;
92134489
LP
311 int r, weekday = -1;
312 unsigned i;
9a98c7a1
LP
313
314 /*
315 * Allowed syntaxes:
316 *
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)
322 * now
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)
326 * +5min
327 * -5days
328 *
329 */
330
331 assert(t);
332 assert(usec);
333
334 x = time(NULL);
335 assert_se(localtime_r(&x, &tm));
6a741b4a 336 tm.tm_isdst = -1;
9a98c7a1
LP
337
338 if (streq(t, "now"))
339 goto finish;
340
341 else if (streq(t, "today")) {
342 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
343 goto finish;
344
345 } else if (streq(t, "yesterday")) {
346 tm.tm_mday --;
347 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
348 goto finish;
349
350 } else if (streq(t, "tomorrow")) {
351 tm.tm_mday ++;
352 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
353 goto finish;
354
355 } else if (t[0] == '+') {
356
357 r = parse_usec(t+1, &plus);
358 if (r < 0)
359 return r;
360
361 goto finish;
362 } else if (t[0] == '-') {
363
364 r = parse_usec(t+1, &minus);
365 if (r < 0)
366 return r;
367
368 goto finish;
decad910
LP
369
370 } else if (endswith(t, " ago")) {
371 _cleanup_free_ char *z;
372
373 z = strndup(t, strlen(t) - 4);
374 if (!z)
375 return -ENOMEM;
376
377 r = parse_usec(z, &minus);
378 if (r < 0)
379 return r;
380
381 goto finish;
9a98c7a1
LP
382 }
383
92134489
LP
384 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
385 size_t skip;
386
387 if (!startswith_no_case(t, day_nr[i].name))
388 continue;
389
390 skip = strlen(day_nr[i].name);
391 if (t[skip] != ' ')
392 continue;
393
394 weekday = day_nr[i].nr;
395 t += skip + 1;
396 break;
397 }
398
9a98c7a1
LP
399 copy = tm;
400 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
401 if (k && *k == 0)
402 goto finish;
403
404 tm = copy;
405 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
406 if (k && *k == 0)
407 goto finish;
408
409 tm = copy;
410 k = strptime(t, "%y-%m-%d %H:%M", &tm);
411 if (k && *k == 0) {
412 tm.tm_sec = 0;
413 goto finish;
414 }
415
416 tm = copy;
417 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
418 if (k && *k == 0) {
419 tm.tm_sec = 0;
420 goto finish;
421 }
422
423 tm = copy;
424 k = strptime(t, "%y-%m-%d", &tm);
425 if (k && *k == 0) {
426 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
427 goto finish;
428 }
429
430 tm = copy;
431 k = strptime(t, "%Y-%m-%d", &tm);
432 if (k && *k == 0) {
433 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
434 goto finish;
435 }
436
437 tm = copy;
438 k = strptime(t, "%H:%M:%S", &tm);
439 if (k && *k == 0)
440 goto finish;
441
442 tm = copy;
443 k = strptime(t, "%H:%M", &tm);
444 if (k && *k == 0) {
445 tm.tm_sec = 0;
446 goto finish;
447 }
448
449 return -EINVAL;
450
451finish:
452 x = mktime(&tm);
453 if (x == (time_t) -1)
454 return -EINVAL;
455
92134489
LP
456 if (weekday >= 0 && tm.tm_wday != weekday)
457 return -EINVAL;
458
9a98c7a1
LP
459 ret = (usec_t) x * USEC_PER_SEC;
460
461 ret += plus;
462 if (ret > minus)
463 ret -= minus;
464 else
465 ret = 0;
466
467 *usec = ret;
468
469 return 0;
470}
471
472int parse_usec(const char *t, usec_t *usec) {
473 static const struct {
474 const char *suffix;
475 usec_t usec;
476 } table[] = {
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 },
502 { "usec", 1ULL },
503 { "us", 1ULL },
504 { "", USEC_PER_SEC }, /* default is sec */
505 };
506
507 const char *p;
508 usec_t r = 0;
509
510 assert(t);
511 assert(usec);
512
513 p = t;
514 do {
515 long long l;
516 char *e;
517 unsigned i;
518
519 errno = 0;
520 l = strtoll(p, &e, 10);
521
522 if (errno != 0)
523 return -errno;
524
525 if (l < 0)
526 return -ERANGE;
527
528 if (e == p)
529 return -EINVAL;
530
531 e += strspn(e, WHITESPACE);
532
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);
537 break;
538 }
539
540 if (i >= ELEMENTSOF(table))
541 return -EINVAL;
542
543 } while (*p != 0);
544
545 *usec = r;
546
547 return 0;
548}
549
550int parse_nsec(const char *t, nsec_t *nsec) {
551 static const struct {
552 const char *suffix;
553 nsec_t nsec;
554 } table[] = {
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 },
582 { "nsec", 1ULL },
583 { "ns", 1ULL },
584 { "", 1ULL }, /* default is nsec */
585 };
586
587 const char *p;
588 nsec_t r = 0;
589
590 assert(t);
591 assert(nsec);
592
593 p = t;
594 do {
595 long long l;
596 char *e;
597 unsigned i;
598
599 errno = 0;
600 l = strtoll(p, &e, 10);
601
602 if (errno != 0)
603 return -errno;
604
605 if (l < 0)
606 return -ERANGE;
607
608 if (e == p)
609 return -EINVAL;
610
611 e += strspn(e, WHITESPACE);
612
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);
617 break;
618 }
619
620 if (i >= ELEMENTSOF(table))
621 return -EINVAL;
622
623 } while (*p != 0);
624
625 *nsec = r;
626
627 return 0;
628}