chiark / gitweb /
shared/util: Fix glob_extend() argument
[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         if (u == (usec_t) -1) {
50                 ts->realtime = ts->monotonic = (usec_t) -1;
51                 return ts;
52         }
53
54         ts->realtime = u;
55
56         if (u == 0)
57                 ts->monotonic = 0;
58         else {
59                 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
60
61                 ts->monotonic = now(CLOCK_MONOTONIC);
62
63                 if ((int64_t) ts->monotonic > delta)
64                         ts->monotonic -= delta;
65                 else
66                         ts->monotonic = 0;
67         }
68
69         return ts;
70 }
71
72 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
73         int64_t delta;
74         assert(ts);
75
76         if (u == (usec_t) -1) {
77                 ts->realtime = ts->monotonic = (usec_t) -1;
78                 return ts;
79         }
80
81         ts->monotonic = u;
82         delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
83
84         ts->realtime = now(CLOCK_REALTIME);
85         if ((int64_t) ts->realtime > delta)
86                 ts->realtime -= delta;
87         else
88                 ts->realtime = 0;
89
90         return ts;
91 }
92
93 usec_t timespec_load(const struct timespec *ts) {
94         assert(ts);
95
96         if (ts->tv_sec == (time_t) -1 &&
97             ts->tv_nsec == (long) -1)
98                 return (usec_t) -1;
99
100         if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
101                 return (usec_t) -1;
102
103         return
104                 (usec_t) ts->tv_sec * USEC_PER_SEC +
105                 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
106 }
107
108 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
109         assert(ts);
110
111         if (u == (usec_t) -1) {
112                 ts->tv_sec = (time_t) -1;
113                 ts->tv_nsec = (long) -1;
114                 return ts;
115         }
116
117         ts->tv_sec = (time_t) (u / USEC_PER_SEC);
118         ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
119
120         return ts;
121 }
122
123 usec_t timeval_load(const struct timeval *tv) {
124         assert(tv);
125
126         if (tv->tv_sec == (time_t) -1 &&
127             tv->tv_usec == (suseconds_t) -1)
128                 return (usec_t) -1;
129
130         if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
131                 return (usec_t) -1;
132
133         return
134                 (usec_t) tv->tv_sec * USEC_PER_SEC +
135                 (usec_t) tv->tv_usec;
136 }
137
138 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
139         assert(tv);
140
141         if (u == (usec_t) -1) {
142                 tv->tv_sec = (time_t) -1;
143                 tv->tv_usec = (suseconds_t) -1;
144                 return tv;
145         }
146
147         tv->tv_sec = (time_t) (u / USEC_PER_SEC);
148         tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
149
150         return tv;
151 }
152
153 char *format_timestamp(char *buf, size_t l, usec_t t) {
154         struct tm tm;
155         time_t sec;
156
157         assert(buf);
158         assert(l > 0);
159
160         if (t <= 0)
161                 return NULL;
162
163         sec = (time_t) (t / USEC_PER_SEC);
164
165         if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
166                 return NULL;
167
168         return buf;
169 }
170
171 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
172         struct tm tm;
173         time_t sec;
174
175         assert(buf);
176         assert(l > 0);
177
178         if (t <= 0)
179                 return NULL;
180
181         sec = (time_t) (t / USEC_PER_SEC);
182         localtime_r(&sec, &tm);
183
184         if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
185                 return NULL;
186         snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", t % USEC_PER_SEC);
187         if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
188                 return NULL;
189
190         return buf;
191 }
192
193 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
194         usec_t n, d;
195
196         n = now(CLOCK_REALTIME);
197
198         if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
199                 return NULL;
200
201         d = n - t;
202
203         if (d >= USEC_PER_YEAR)
204                 snprintf(buf, l, "%llu years %llu months ago",
205                          (unsigned long long) (d / USEC_PER_YEAR),
206                          (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
207         else if (d >= USEC_PER_MONTH)
208                 snprintf(buf, l, "%llu months %llu days ago",
209                          (unsigned long long) (d / USEC_PER_MONTH),
210                          (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
211         else if (d >= USEC_PER_WEEK)
212                 snprintf(buf, l, "%llu weeks %llu days ago",
213                          (unsigned long long) (d / USEC_PER_WEEK),
214                          (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
215         else if (d >= 2*USEC_PER_DAY)
216                 snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
217         else if (d >= 25*USEC_PER_HOUR)
218                 snprintf(buf, l, "1 day %lluh ago",
219                          (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
220         else if (d >= 6*USEC_PER_HOUR)
221                 snprintf(buf, l, "%lluh ago",
222                          (unsigned long long) (d / USEC_PER_HOUR));
223         else if (d >= USEC_PER_HOUR)
224                 snprintf(buf, l, "%lluh %llumin ago",
225                          (unsigned long long) (d / USEC_PER_HOUR),
226                          (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
227         else if (d >= 5*USEC_PER_MINUTE)
228                 snprintf(buf, l, "%llumin ago",
229                          (unsigned long long) (d / USEC_PER_MINUTE));
230         else if (d >= USEC_PER_MINUTE)
231                 snprintf(buf, l, "%llumin %llus ago",
232                          (unsigned long long) (d / USEC_PER_MINUTE),
233                          (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
234         else if (d >= USEC_PER_SEC)
235                 snprintf(buf, l, "%llus ago",
236                          (unsigned long long) (d / USEC_PER_SEC));
237         else if (d >= USEC_PER_MSEC)
238                 snprintf(buf, l, "%llums ago",
239                          (unsigned long long) (d / USEC_PER_MSEC));
240         else if (d > 0)
241                 snprintf(buf, l, "%lluus ago",
242                          (unsigned long long) d);
243         else
244                 snprintf(buf, l, "now");
245
246         buf[l-1] = 0;
247         return buf;
248 }
249
250 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
251         static const struct {
252                 const char *suffix;
253                 usec_t usec;
254         } table[] = {
255                 { "y", USEC_PER_YEAR },
256                 { "month", USEC_PER_MONTH },
257                 { "w", USEC_PER_WEEK },
258                 { "d", USEC_PER_DAY },
259                 { "h", USEC_PER_HOUR },
260                 { "min", USEC_PER_MINUTE },
261                 { "s", USEC_PER_SEC },
262                 { "ms", USEC_PER_MSEC },
263                 { "us", 1 },
264         };
265
266         unsigned i;
267         char *p = buf;
268         bool something = false;
269
270         assert(buf);
271         assert(l > 0);
272
273         if (t == (usec_t) -1)
274                 return NULL;
275
276         if (t <= 0) {
277                 snprintf(p, l, "0");
278                 p[l-1] = 0;
279                 return p;
280         }
281
282         /* The result of this function can be parsed with parse_sec */
283
284         for (i = 0; i < ELEMENTSOF(table); i++) {
285                 int k;
286                 size_t n;
287                 bool done = false;
288                 usec_t a, b;
289
290                 if (t <= 0)
291                         break;
292
293                 if (t < accuracy && something)
294                         break;
295
296                 if (t < table[i].usec)
297                         continue;
298
299                 if (l <= 1)
300                         break;
301
302                 a = t / table[i].usec;
303                 b = t % table[i].usec;
304
305                 /* Let's see if we should shows this in dot notation */
306                 if (t < USEC_PER_MINUTE && b > 0) {
307                         usec_t cc;
308                         int j;
309
310                         j = 0;
311                         for (cc = table[i].usec; cc > 1; cc /= 10)
312                                 j++;
313
314                         for (cc = accuracy; cc > 1; cc /= 10) {
315                                 b /= 10;
316                                 j--;
317                         }
318
319                         if (j > 0) {
320                                 k = snprintf(p, l,
321                                              "%s%llu.%0*llu%s",
322                                              p > buf ? " " : "",
323                                              (unsigned long long) a,
324                                              j,
325                                              (unsigned long long) b,
326                                              table[i].suffix);
327
328                                 t = 0;
329                                 done = true;
330                         }
331                 }
332
333                 /* No? Then let's show it normally */
334                 if (!done) {
335                         k = snprintf(p, l,
336                                      "%s%llu%s",
337                                      p > buf ? " " : "",
338                                      (unsigned long long) a,
339                                      table[i].suffix);
340
341                         t = b;
342                 }
343
344                 n = MIN((size_t) k, l);
345
346                 l -= n;
347                 p += n;
348
349                 something = true;
350         }
351
352         *p = 0;
353
354         return buf;
355 }
356
357 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
358
359         assert(f);
360         assert(name);
361         assert(t);
362
363         if (!dual_timestamp_is_set(t))
364                 return;
365
366         fprintf(f, "%s=%llu %llu\n",
367                 name,
368                 (unsigned long long) t->realtime,
369                 (unsigned long long) t->monotonic);
370 }
371
372 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
373         unsigned long long a, b;
374
375         assert(value);
376         assert(t);
377
378         if (sscanf(value, "%lli %llu", &a, &b) != 2)
379                 log_debug("Failed to parse finish timestamp value %s", value);
380         else {
381                 t->realtime = a;
382                 t->monotonic = b;
383         }
384 }
385
386 int parse_timestamp(const char *t, usec_t *usec) {
387         static const struct {
388                 const char *name;
389                 const int nr;
390         } day_nr[] = {
391                 { "Sunday",    0 },
392                 { "Sun",       0 },
393                 { "Monday",    1 },
394                 { "Mon",       1 },
395                 { "Tuesday",   2 },
396                 { "Tue",       2 },
397                 { "Wednesday", 3 },
398                 { "Wed",       3 },
399                 { "Thursday",  4 },
400                 { "Thu",       4 },
401                 { "Friday",    5 },
402                 { "Fri",       5 },
403                 { "Saturday",  6 },
404                 { "Sat",       6 },
405         };
406
407         const char *k;
408         struct tm tm, copy;
409         time_t x;
410         usec_t plus = 0, minus = 0, ret;
411         int r, weekday = -1;
412         unsigned i;
413
414         /*
415          * Allowed syntaxes:
416          *
417          *   2012-09-22 16:34:22
418          *   2012-09-22 16:34     (seconds will be set to 0)
419          *   2012-09-22           (time will be set to 00:00:00)
420          *   16:34:22             (date will be set to today)
421          *   16:34                (date will be set to today, seconds to 0)
422          *   now
423          *   yesterday            (time is set to 00:00:00)
424          *   today                (time is set to 00:00:00)
425          *   tomorrow             (time is set to 00:00:00)
426          *   +5min
427          *   -5days
428          *
429          */
430
431         assert(t);
432         assert(usec);
433
434         x = time(NULL);
435         assert_se(localtime_r(&x, &tm));
436         tm.tm_isdst = -1;
437
438         if (streq(t, "now"))
439                 goto finish;
440
441         else if (streq(t, "today")) {
442                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
443                 goto finish;
444
445         } else if (streq(t, "yesterday")) {
446                 tm.tm_mday --;
447                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
448                 goto finish;
449
450         } else if (streq(t, "tomorrow")) {
451                 tm.tm_mday ++;
452                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
453                 goto finish;
454
455         } else if (t[0] == '+') {
456
457                 r = parse_sec(t+1, &plus);
458                 if (r < 0)
459                         return r;
460
461                 goto finish;
462         } else if (t[0] == '-') {
463
464                 r = parse_sec(t+1, &minus);
465                 if (r < 0)
466                         return r;
467
468                 goto finish;
469
470         } else if (endswith(t, " ago")) {
471                 _cleanup_free_ char *z;
472
473                 z = strndup(t, strlen(t) - 4);
474                 if (!z)
475                         return -ENOMEM;
476
477                 r = parse_sec(z, &minus);
478                 if (r < 0)
479                         return r;
480
481                 goto finish;
482         }
483
484         for (i = 0; i < ELEMENTSOF(day_nr); i++) {
485                 size_t skip;
486
487                 if (!startswith_no_case(t, day_nr[i].name))
488                         continue;
489
490                 skip = strlen(day_nr[i].name);
491                 if (t[skip] != ' ')
492                         continue;
493
494                 weekday = day_nr[i].nr;
495                 t += skip + 1;
496                 break;
497         }
498
499         copy = tm;
500         k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
501         if (k && *k == 0)
502                 goto finish;
503
504         tm = copy;
505         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
506         if (k && *k == 0)
507                 goto finish;
508
509         tm = copy;
510         k = strptime(t, "%y-%m-%d %H:%M", &tm);
511         if (k && *k == 0) {
512                 tm.tm_sec = 0;
513                 goto finish;
514         }
515
516         tm = copy;
517         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
518         if (k && *k == 0) {
519                 tm.tm_sec = 0;
520                 goto finish;
521         }
522
523         tm = copy;
524         k = strptime(t, "%y-%m-%d", &tm);
525         if (k && *k == 0) {
526                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
527                 goto finish;
528         }
529
530         tm = copy;
531         k = strptime(t, "%Y-%m-%d", &tm);
532         if (k && *k == 0) {
533                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
534                 goto finish;
535         }
536
537         tm = copy;
538         k = strptime(t, "%H:%M:%S", &tm);
539         if (k && *k == 0)
540                 goto finish;
541
542         tm = copy;
543         k = strptime(t, "%H:%M", &tm);
544         if (k && *k == 0) {
545                 tm.tm_sec = 0;
546                 goto finish;
547         }
548
549         return -EINVAL;
550
551 finish:
552         x = mktime(&tm);
553         if (x == (time_t) -1)
554                 return -EINVAL;
555
556         if (weekday >= 0 && tm.tm_wday != weekday)
557                 return -EINVAL;
558
559         ret = (usec_t) x * USEC_PER_SEC;
560
561         ret += plus;
562         if (ret > minus)
563                 ret -= minus;
564         else
565                 ret = 0;
566
567         *usec = ret;
568
569         return 0;
570 }
571
572 int parse_sec(const char *t, usec_t *usec) {
573         static const struct {
574                 const char *suffix;
575                 usec_t usec;
576         } table[] = {
577                 { "seconds", USEC_PER_SEC },
578                 { "second", USEC_PER_SEC },
579                 { "sec", USEC_PER_SEC },
580                 { "s", USEC_PER_SEC },
581                 { "minutes", USEC_PER_MINUTE },
582                 { "minute", USEC_PER_MINUTE },
583                 { "min", USEC_PER_MINUTE },
584                 { "months", USEC_PER_MONTH },
585                 { "month", USEC_PER_MONTH },
586                 { "msec", USEC_PER_MSEC },
587                 { "ms", USEC_PER_MSEC },
588                 { "m", USEC_PER_MINUTE },
589                 { "hours", USEC_PER_HOUR },
590                 { "hour", USEC_PER_HOUR },
591                 { "hr", USEC_PER_HOUR },
592                 { "h", USEC_PER_HOUR },
593                 { "days", USEC_PER_DAY },
594                 { "day", USEC_PER_DAY },
595                 { "d", USEC_PER_DAY },
596                 { "weeks", USEC_PER_WEEK },
597                 { "week", USEC_PER_WEEK },
598                 { "w", USEC_PER_WEEK },
599                 { "years", USEC_PER_YEAR },
600                 { "year", USEC_PER_YEAR },
601                 { "y", USEC_PER_YEAR },
602                 { "usec", 1ULL },
603                 { "us", 1ULL },
604                 { "", USEC_PER_SEC }, /* default is sec */
605         };
606
607         const char *p;
608         usec_t r = 0;
609         bool something = false;
610
611         assert(t);
612         assert(usec);
613
614         p = t;
615         for (;;) {
616                 long long l, z = 0;
617                 char *e;
618                 unsigned i, n = 0;
619
620                 p += strspn(p, WHITESPACE);
621
622                 if (*p == 0) {
623                         if (!something)
624                                 return -EINVAL;
625
626                         break;
627                 }
628
629                 errno = 0;
630                 l = strtoll(p, &e, 10);
631
632                 if (errno > 0)
633                         return -errno;
634
635                 if (l < 0)
636                         return -ERANGE;
637
638                 if (*e == '.') {
639                         char *b = e + 1;
640
641                         errno = 0;
642                         z = strtoll(b, &e, 10);
643                         if (errno > 0)
644                                 return -errno;
645
646                         if (z < 0)
647                                 return -ERANGE;
648
649                         if (e == b)
650                                 return -EINVAL;
651
652                         n = e - b;
653
654                 } else if (e == p)
655                         return -EINVAL;
656
657                 e += strspn(e, WHITESPACE);
658
659                 for (i = 0; i < ELEMENTSOF(table); i++)
660                         if (startswith(e, table[i].suffix)) {
661                                 usec_t k = (usec_t) z * table[i].usec;
662
663                                 for (; n > 0; n--)
664                                         k /= 10;
665
666                                 r += (usec_t) l * table[i].usec + k;
667                                 p = e + strlen(table[i].suffix);
668
669                                 something = true;
670                                 break;
671                         }
672
673                 if (i >= ELEMENTSOF(table))
674                         return -EINVAL;
675
676         }
677
678         *usec = r;
679
680         return 0;
681 }
682
683 int parse_nsec(const char *t, nsec_t *nsec) {
684         static const struct {
685                 const char *suffix;
686                 nsec_t nsec;
687         } table[] = {
688                 { "seconds", NSEC_PER_SEC },
689                 { "second", NSEC_PER_SEC },
690                 { "sec", NSEC_PER_SEC },
691                 { "s", NSEC_PER_SEC },
692                 { "minutes", NSEC_PER_MINUTE },
693                 { "minute", NSEC_PER_MINUTE },
694                 { "min", NSEC_PER_MINUTE },
695                 { "months", NSEC_PER_MONTH },
696                 { "month", NSEC_PER_MONTH },
697                 { "msec", NSEC_PER_MSEC },
698                 { "ms", NSEC_PER_MSEC },
699                 { "m", NSEC_PER_MINUTE },
700                 { "hours", NSEC_PER_HOUR },
701                 { "hour", NSEC_PER_HOUR },
702                 { "hr", NSEC_PER_HOUR },
703                 { "h", NSEC_PER_HOUR },
704                 { "days", NSEC_PER_DAY },
705                 { "day", NSEC_PER_DAY },
706                 { "d", NSEC_PER_DAY },
707                 { "weeks", NSEC_PER_WEEK },
708                 { "week", NSEC_PER_WEEK },
709                 { "w", NSEC_PER_WEEK },
710                 { "years", NSEC_PER_YEAR },
711                 { "year", NSEC_PER_YEAR },
712                 { "y", NSEC_PER_YEAR },
713                 { "usec", NSEC_PER_USEC },
714                 { "us", NSEC_PER_USEC },
715                 { "nsec", 1ULL },
716                 { "ns", 1ULL },
717                 { "", 1ULL }, /* default is nsec */
718         };
719
720         const char *p;
721         nsec_t r = 0;
722         bool something = false;
723
724         assert(t);
725         assert(nsec);
726
727         p = t;
728         for (;;) {
729                 long long l, z = 0;
730                 char *e;
731                 unsigned i, n = 0;
732
733                 p += strspn(p, WHITESPACE);
734
735                 if (*p == 0) {
736                         if (!something)
737                                 return -EINVAL;
738
739                         break;
740                 }
741
742                 errno = 0;
743                 l = strtoll(p, &e, 10);
744
745                 if (errno > 0)
746                         return -errno;
747
748                 if (l < 0)
749                         return -ERANGE;
750
751                 if (*e == '.') {
752                         char *b = e + 1;
753
754                         errno = 0;
755                         z = strtoll(b, &e, 10);
756                         if (errno > 0)
757                                 return -errno;
758
759                         if (z < 0)
760                                 return -ERANGE;
761
762                         if (e == b)
763                                 return -EINVAL;
764
765                         n = e - b;
766
767                 } else if (e == p)
768                         return -EINVAL;
769
770                 e += strspn(e, WHITESPACE);
771
772                 for (i = 0; i < ELEMENTSOF(table); i++)
773                         if (startswith(e, table[i].suffix)) {
774                                 nsec_t k = (nsec_t) z * table[i].nsec;
775
776                                 for (; n > 0; n--)
777                                         k /= 10;
778
779                                 r += (nsec_t) l * table[i].nsec + k;
780                                 p = e + strlen(table[i].suffix);
781
782                                 something = true;
783                                 break;
784                         }
785
786                 if (i >= ELEMENTSOF(table))
787                         return -EINVAL;
788
789         }
790
791         *nsec = r;
792
793         return 0;
794 }