chiark / gitweb /
util: split off time related calls from util.[ch] into time-util.[ch]
[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_pretty(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
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
401 finish:
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
419 int 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
497 int 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 }