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