chiark / gitweb /
43448e0c6305ec5449596e8274fe14ea2c6d9704
[elogind.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         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
67 usec_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
82 struct 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
97 usec_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
112 struct 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
127 char *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
145 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
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
202 char *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
257 void 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
272 void 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
286 int 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         tm.tm_isdst = -1;
316
317         if (streq(t, "now"))
318                 goto finish;
319
320         else if (streq(t, "today")) {
321                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
322                 goto finish;
323
324         } else if (streq(t, "yesterday")) {
325                 tm.tm_mday --;
326                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
327                 goto finish;
328
329         } else if (streq(t, "tomorrow")) {
330                 tm.tm_mday ++;
331                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
332                 goto finish;
333
334         } else if (t[0] == '+') {
335
336                 r = parse_usec(t+1, &plus);
337                 if (r < 0)
338                         return r;
339
340                 goto finish;
341         } else if (t[0] == '-') {
342
343                 r = parse_usec(t+1, &minus);
344                 if (r < 0)
345                         return r;
346
347                 goto finish;
348
349         } else if (endswith(t, " ago")) {
350                 _cleanup_free_ char *z;
351
352                 z = strndup(t, strlen(t) - 4);
353                 if (!z)
354                         return -ENOMEM;
355
356                 r = parse_usec(z, &minus);
357                 if (r < 0)
358                         return r;
359
360                 goto finish;
361         }
362
363         copy = tm;
364         k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
365         if (k && *k == 0)
366                 goto finish;
367
368         tm = copy;
369         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
370         if (k && *k == 0)
371                 goto finish;
372
373         tm = copy;
374         k = strptime(t, "%y-%m-%d %H:%M", &tm);
375         if (k && *k == 0) {
376                 tm.tm_sec = 0;
377                 goto finish;
378         }
379
380         tm = copy;
381         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
382         if (k && *k == 0) {
383                 tm.tm_sec = 0;
384                 goto finish;
385         }
386
387         tm = copy;
388         k = strptime(t, "%y-%m-%d", &tm);
389         if (k && *k == 0) {
390                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
391                 goto finish;
392         }
393
394         tm = copy;
395         k = strptime(t, "%Y-%m-%d", &tm);
396         if (k && *k == 0) {
397                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
398                 goto finish;
399         }
400
401         tm = copy;
402         k = strptime(t, "%H:%M:%S", &tm);
403         if (k && *k == 0)
404                 goto finish;
405
406         tm = copy;
407         k = strptime(t, "%H:%M", &tm);
408         if (k && *k == 0) {
409                 tm.tm_sec = 0;
410                 goto finish;
411         }
412
413         return -EINVAL;
414
415 finish:
416         x = mktime(&tm);
417         if (x == (time_t) -1)
418                 return -EINVAL;
419
420         ret = (usec_t) x * USEC_PER_SEC;
421
422         ret += plus;
423         if (ret > minus)
424                 ret -= minus;
425         else
426                 ret = 0;
427
428         *usec = ret;
429
430         return 0;
431 }
432
433 int parse_usec(const char *t, usec_t *usec) {
434         static const struct {
435                 const char *suffix;
436                 usec_t usec;
437         } table[] = {
438                 { "seconds", USEC_PER_SEC },
439                 { "second", USEC_PER_SEC },
440                 { "sec", USEC_PER_SEC },
441                 { "s", USEC_PER_SEC },
442                 { "minutes", USEC_PER_MINUTE },
443                 { "minute", USEC_PER_MINUTE },
444                 { "min", USEC_PER_MINUTE },
445                 { "months", USEC_PER_MONTH },
446                 { "month", USEC_PER_MONTH },
447                 { "msec", USEC_PER_MSEC },
448                 { "ms", USEC_PER_MSEC },
449                 { "m", USEC_PER_MINUTE },
450                 { "hours", USEC_PER_HOUR },
451                 { "hour", USEC_PER_HOUR },
452                 { "hr", USEC_PER_HOUR },
453                 { "h", USEC_PER_HOUR },
454                 { "days", USEC_PER_DAY },
455                 { "day", USEC_PER_DAY },
456                 { "d", USEC_PER_DAY },
457                 { "weeks", USEC_PER_WEEK },
458                 { "week", USEC_PER_WEEK },
459                 { "w", USEC_PER_WEEK },
460                 { "years", USEC_PER_YEAR },
461                 { "year", USEC_PER_YEAR },
462                 { "y", USEC_PER_YEAR },
463                 { "usec", 1ULL },
464                 { "us", 1ULL },
465                 { "", USEC_PER_SEC }, /* default is sec */
466         };
467
468         const char *p;
469         usec_t r = 0;
470
471         assert(t);
472         assert(usec);
473
474         p = t;
475         do {
476                 long long l;
477                 char *e;
478                 unsigned i;
479
480                 errno = 0;
481                 l = strtoll(p, &e, 10);
482
483                 if (errno != 0)
484                         return -errno;
485
486                 if (l < 0)
487                         return -ERANGE;
488
489                 if (e == p)
490                         return -EINVAL;
491
492                 e += strspn(e, WHITESPACE);
493
494                 for (i = 0; i < ELEMENTSOF(table); i++)
495                         if (startswith(e, table[i].suffix)) {
496                                 r += (usec_t) l * table[i].usec;
497                                 p = e + strlen(table[i].suffix);
498                                 break;
499                         }
500
501                 if (i >= ELEMENTSOF(table))
502                         return -EINVAL;
503
504         } while (*p != 0);
505
506         *usec = r;
507
508         return 0;
509 }
510
511 int parse_nsec(const char *t, nsec_t *nsec) {
512         static const struct {
513                 const char *suffix;
514                 nsec_t nsec;
515         } table[] = {
516                 { "seconds", NSEC_PER_SEC },
517                 { "second", NSEC_PER_SEC },
518                 { "sec", NSEC_PER_SEC },
519                 { "s", NSEC_PER_SEC },
520                 { "minutes", NSEC_PER_MINUTE },
521                 { "minute", NSEC_PER_MINUTE },
522                 { "min", NSEC_PER_MINUTE },
523                 { "months", NSEC_PER_MONTH },
524                 { "month", NSEC_PER_MONTH },
525                 { "msec", NSEC_PER_MSEC },
526                 { "ms", NSEC_PER_MSEC },
527                 { "m", NSEC_PER_MINUTE },
528                 { "hours", NSEC_PER_HOUR },
529                 { "hour", NSEC_PER_HOUR },
530                 { "hr", NSEC_PER_HOUR },
531                 { "h", NSEC_PER_HOUR },
532                 { "days", NSEC_PER_DAY },
533                 { "day", NSEC_PER_DAY },
534                 { "d", NSEC_PER_DAY },
535                 { "weeks", NSEC_PER_WEEK },
536                 { "week", NSEC_PER_WEEK },
537                 { "w", NSEC_PER_WEEK },
538                 { "years", NSEC_PER_YEAR },
539                 { "year", NSEC_PER_YEAR },
540                 { "y", NSEC_PER_YEAR },
541                 { "usec", NSEC_PER_USEC },
542                 { "us", NSEC_PER_USEC },
543                 { "nsec", 1ULL },
544                 { "ns", 1ULL },
545                 { "", 1ULL }, /* default is nsec */
546         };
547
548         const char *p;
549         nsec_t r = 0;
550
551         assert(t);
552         assert(nsec);
553
554         p = t;
555         do {
556                 long long l;
557                 char *e;
558                 unsigned i;
559
560                 errno = 0;
561                 l = strtoll(p, &e, 10);
562
563                 if (errno != 0)
564                         return -errno;
565
566                 if (l < 0)
567                         return -ERANGE;
568
569                 if (e == p)
570                         return -EINVAL;
571
572                 e += strspn(e, WHITESPACE);
573
574                 for (i = 0; i < ELEMENTSOF(table); i++)
575                         if (startswith(e, table[i].suffix)) {
576                                 r += (nsec_t) l * table[i].nsec;
577                                 p = e + strlen(table[i].suffix);
578                                 break;
579                         }
580
581                 if (i >= ELEMENTSOF(table))
582                         return -EINVAL;
583
584         } while (*p != 0);
585
586         *nsec = r;
587
588         return 0;
589 }