]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
util: rename format_timestamp_pretty() to format_timestamp_relative() because that...
[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) {
287 const char *k;
288 struct tm tm, copy;
289 time_t x;
290 usec_t plus = 0, minus = 0, ret;
291 int r;
292
293 /*
294 * Allowed syntaxes:
295 *
296 * 2012-09-22 16:34:22
297 * 2012-09-22 16:34 (seconds will be set to 0)
298 * 2012-09-22 (time will be set to 00:00:00)
299 * 16:34:22 (date will be set to today)
300 * 16:34 (date will be set to today, seconds to 0)
301 * now
302 * yesterday (time is set to 00:00:00)
303 * today (time is set to 00:00:00)
304 * tomorrow (time is set to 00:00:00)
305 * +5min
306 * -5days
307 *
308 */
309
310 assert(t);
311 assert(usec);
312
313 x = time(NULL);
314 assert_se(localtime_r(&x, &tm));
315
316 if (streq(t, "now"))
317 goto finish;
318
319 else if (streq(t, "today")) {
320 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
321 goto finish;
322
323 } else if (streq(t, "yesterday")) {
324 tm.tm_mday --;
325 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
326 goto finish;
327
328 } else if (streq(t, "tomorrow")) {
329 tm.tm_mday ++;
330 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
331 goto finish;
332
333 } else if (t[0] == '+') {
334
335 r = parse_usec(t+1, &plus);
336 if (r < 0)
337 return r;
338
339 goto finish;
340 } else if (t[0] == '-') {
341
342 r = parse_usec(t+1, &minus);
343 if (r < 0)
344 return r;
345
346 goto finish;
347 }
348
349 copy = tm;
350 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
351 if (k && *k == 0)
352 goto finish;
353
354 tm = copy;
355 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
356 if (k && *k == 0)
357 goto finish;
358
359 tm = copy;
360 k = strptime(t, "%y-%m-%d %H:%M", &tm);
361 if (k && *k == 0) {
362 tm.tm_sec = 0;
363 goto finish;
364 }
365
366 tm = copy;
367 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
368 if (k && *k == 0) {
369 tm.tm_sec = 0;
370 goto finish;
371 }
372
373 tm = copy;
374 k = strptime(t, "%y-%m-%d", &tm);
375 if (k && *k == 0) {
376 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
377 goto finish;
378 }
379
380 tm = copy;
381 k = strptime(t, "%Y-%m-%d", &tm);
382 if (k && *k == 0) {
383 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
384 goto finish;
385 }
386
387 tm = copy;
388 k = strptime(t, "%H:%M:%S", &tm);
389 if (k && *k == 0)
390 goto finish;
391
392 tm = copy;
393 k = strptime(t, "%H:%M", &tm);
394 if (k && *k == 0) {
395 tm.tm_sec = 0;
396 goto finish;
397 }
398
399 return -EINVAL;
400
401finish:
402 x = mktime(&tm);
403 if (x == (time_t) -1)
404 return -EINVAL;
405
406 ret = (usec_t) x * USEC_PER_SEC;
407
408 ret += plus;
409 if (ret > minus)
410 ret -= minus;
411 else
412 ret = 0;
413
414 *usec = ret;
415
416 return 0;
417}
418
419int parse_usec(const char *t, usec_t *usec) {
420 static const struct {
421 const char *suffix;
422 usec_t usec;
423 } table[] = {
424 { "seconds", USEC_PER_SEC },
425 { "second", USEC_PER_SEC },
426 { "sec", USEC_PER_SEC },
427 { "s", USEC_PER_SEC },
428 { "minutes", USEC_PER_MINUTE },
429 { "minute", USEC_PER_MINUTE },
430 { "min", USEC_PER_MINUTE },
431 { "months", USEC_PER_MONTH },
432 { "month", USEC_PER_MONTH },
433 { "msec", USEC_PER_MSEC },
434 { "ms", USEC_PER_MSEC },
435 { "m", USEC_PER_MINUTE },
436 { "hours", USEC_PER_HOUR },
437 { "hour", USEC_PER_HOUR },
438 { "hr", USEC_PER_HOUR },
439 { "h", USEC_PER_HOUR },
440 { "days", USEC_PER_DAY },
441 { "day", USEC_PER_DAY },
442 { "d", USEC_PER_DAY },
443 { "weeks", USEC_PER_WEEK },
444 { "week", USEC_PER_WEEK },
445 { "w", USEC_PER_WEEK },
446 { "years", USEC_PER_YEAR },
447 { "year", USEC_PER_YEAR },
448 { "y", USEC_PER_YEAR },
449 { "usec", 1ULL },
450 { "us", 1ULL },
451 { "", USEC_PER_SEC }, /* default is sec */
452 };
453
454 const char *p;
455 usec_t r = 0;
456
457 assert(t);
458 assert(usec);
459
460 p = t;
461 do {
462 long long l;
463 char *e;
464 unsigned i;
465
466 errno = 0;
467 l = strtoll(p, &e, 10);
468
469 if (errno != 0)
470 return -errno;
471
472 if (l < 0)
473 return -ERANGE;
474
475 if (e == p)
476 return -EINVAL;
477
478 e += strspn(e, WHITESPACE);
479
480 for (i = 0; i < ELEMENTSOF(table); i++)
481 if (startswith(e, table[i].suffix)) {
482 r += (usec_t) l * table[i].usec;
483 p = e + strlen(table[i].suffix);
484 break;
485 }
486
487 if (i >= ELEMENTSOF(table))
488 return -EINVAL;
489
490 } while (*p != 0);
491
492 *usec = r;
493
494 return 0;
495}
496
497int parse_nsec(const char *t, nsec_t *nsec) {
498 static const struct {
499 const char *suffix;
500 nsec_t nsec;
501 } table[] = {
502 { "seconds", NSEC_PER_SEC },
503 { "second", NSEC_PER_SEC },
504 { "sec", NSEC_PER_SEC },
505 { "s", NSEC_PER_SEC },
506 { "minutes", NSEC_PER_MINUTE },
507 { "minute", NSEC_PER_MINUTE },
508 { "min", NSEC_PER_MINUTE },
509 { "months", NSEC_PER_MONTH },
510 { "month", NSEC_PER_MONTH },
511 { "msec", NSEC_PER_MSEC },
512 { "ms", NSEC_PER_MSEC },
513 { "m", NSEC_PER_MINUTE },
514 { "hours", NSEC_PER_HOUR },
515 { "hour", NSEC_PER_HOUR },
516 { "hr", NSEC_PER_HOUR },
517 { "h", NSEC_PER_HOUR },
518 { "days", NSEC_PER_DAY },
519 { "day", NSEC_PER_DAY },
520 { "d", NSEC_PER_DAY },
521 { "weeks", NSEC_PER_WEEK },
522 { "week", NSEC_PER_WEEK },
523 { "w", NSEC_PER_WEEK },
524 { "years", NSEC_PER_YEAR },
525 { "year", NSEC_PER_YEAR },
526 { "y", NSEC_PER_YEAR },
527 { "usec", NSEC_PER_USEC },
528 { "us", NSEC_PER_USEC },
529 { "nsec", 1ULL },
530 { "ns", 1ULL },
531 { "", 1ULL }, /* default is nsec */
532 };
533
534 const char *p;
535 nsec_t r = 0;
536
537 assert(t);
538 assert(nsec);
539
540 p = t;
541 do {
542 long long l;
543 char *e;
544 unsigned i;
545
546 errno = 0;
547 l = strtoll(p, &e, 10);
548
549 if (errno != 0)
550 return -errno;
551
552 if (l < 0)
553 return -ERANGE;
554
555 if (e == p)
556 return -EINVAL;
557
558 e += strspn(e, WHITESPACE);
559
560 for (i = 0; i < ELEMENTSOF(table); i++)
561 if (startswith(e, table[i].suffix)) {
562 r += (nsec_t) l * table[i].nsec;
563 p = e + strlen(table[i].suffix);
564 break;
565 }
566
567 if (i >= ELEMENTSOF(table))
568 return -EINVAL;
569
570 } while (*p != 0);
571
572 *nsec = r;
573
574 return 0;
575}