chiark / gitweb /
time: earlier exit from format_timestamp_relative() on special times
[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 #include <sys/timex.h>
25 #include <sys/timerfd.h>
26
27 #include "util.h"
28 #include "time-util.h"
29 #include "strv.h"
30
31 usec_t now(clockid_t clock_id) {
32         struct timespec ts;
33
34         assert_se(clock_gettime(clock_id, &ts) == 0);
35
36         return timespec_load(&ts);
37 }
38
39 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
40         assert(ts);
41
42         ts->realtime = now(CLOCK_REALTIME);
43         ts->monotonic = now(CLOCK_MONOTONIC);
44
45         return ts;
46 }
47
48 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
49         int64_t delta;
50         assert(ts);
51
52         if (u == USEC_INFINITY) {
53                 ts->realtime = ts->monotonic = USEC_INFINITY;
54                 return ts;
55         }
56
57         ts->realtime = u;
58
59         if (u == 0)
60                 ts->monotonic = 0;
61         else {
62                 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
63
64                 ts->monotonic = now(CLOCK_MONOTONIC);
65
66                 if ((int64_t) ts->monotonic > delta)
67                         ts->monotonic -= delta;
68                 else
69                         ts->monotonic = 0;
70         }
71
72         return ts;
73 }
74
75 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
76         int64_t delta;
77         assert(ts);
78
79         if (u == USEC_INFINITY) {
80                 ts->realtime = ts->monotonic = USEC_INFINITY;
81                 return ts;
82         }
83
84         ts->monotonic = u;
85         delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
86
87         ts->realtime = now(CLOCK_REALTIME);
88         if ((int64_t) ts->realtime > delta)
89                 ts->realtime -= delta;
90         else
91                 ts->realtime = 0;
92
93         return ts;
94 }
95
96 usec_t timespec_load(const struct timespec *ts) {
97         assert(ts);
98
99         if (ts->tv_sec == (time_t) -1 &&
100             ts->tv_nsec == (long) -1)
101                 return USEC_INFINITY;
102
103         if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
104                 return USEC_INFINITY;
105
106         return
107                 (usec_t) ts->tv_sec * USEC_PER_SEC +
108                 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
109 }
110
111 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
112         assert(ts);
113
114         if (u == USEC_INFINITY) {
115                 ts->tv_sec = (time_t) -1;
116                 ts->tv_nsec = (long) -1;
117                 return ts;
118         }
119
120         ts->tv_sec = (time_t) (u / USEC_PER_SEC);
121         ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
122
123         return ts;
124 }
125
126 usec_t timeval_load(const struct timeval *tv) {
127         assert(tv);
128
129         if (tv->tv_sec == (time_t) -1 &&
130             tv->tv_usec == (suseconds_t) -1)
131                 return USEC_INFINITY;
132
133         if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
134                 return USEC_INFINITY;
135
136         return
137                 (usec_t) tv->tv_sec * USEC_PER_SEC +
138                 (usec_t) tv->tv_usec;
139 }
140
141 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
142         assert(tv);
143
144         if (u == USEC_INFINITY) {
145                 tv->tv_sec = (time_t) -1;
146                 tv->tv_usec = (suseconds_t) -1;
147         } else {
148                 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
149                 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
150         }
151
152         return tv;
153 }
154
155 static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
156         struct tm tm;
157         time_t sec;
158
159         assert(buf);
160         assert(l > 0);
161
162         if (t <= 0 || t == USEC_INFINITY)
163                 return NULL;
164
165         sec = (time_t) (t / USEC_PER_SEC);
166
167         if (utc)
168                 gmtime_r(&sec, &tm);
169         else
170                 localtime_r(&sec, &tm);
171         if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
172                 return NULL;
173
174         return buf;
175 }
176
177 char *format_timestamp(char *buf, size_t l, usec_t t) {
178         return format_timestamp_internal(buf, l, t, false);
179 }
180
181 char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
182         return format_timestamp_internal(buf, l, t, true);
183 }
184
185 static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
186         struct tm tm;
187         time_t sec;
188
189         assert(buf);
190         assert(l > 0);
191
192         if (t <= 0 || t == USEC_INFINITY)
193                 return NULL;
194
195         sec = (time_t) (t / USEC_PER_SEC);
196         if (utc)
197                 gmtime_r(&sec, &tm);
198         else
199                 localtime_r(&sec, &tm);
200
201         if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
202                 return NULL;
203         snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
204         if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
205                 return NULL;
206
207         return buf;
208 }
209
210 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
211         return format_timestamp_internal_us(buf, l, t, false);
212 }
213
214 char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
215         return format_timestamp_internal_us(buf, l, t, true);
216 }
217
218 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
219         const char *s;
220         usec_t n, d;
221
222         if (t <= 0 || t == USEC_INFINITY)
223                 return NULL;
224
225         n = now(CLOCK_REALTIME);
226         if (n > t) {
227                 d = n - t;
228                 s = "ago";
229         } else {
230                 d = t - n;
231                 s = "left";
232         }
233
234         if (d >= USEC_PER_YEAR)
235                 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
236                          d / USEC_PER_YEAR,
237                          (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
238         else if (d >= USEC_PER_MONTH)
239                 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
240                          d / USEC_PER_MONTH,
241                          (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
242         else if (d >= USEC_PER_WEEK)
243                 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
244                          d / USEC_PER_WEEK,
245                          (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
246         else if (d >= 2*USEC_PER_DAY)
247                 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
248         else if (d >= 25*USEC_PER_HOUR)
249                 snprintf(buf, l, "1 day " USEC_FMT "h %s",
250                          (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
251         else if (d >= 6*USEC_PER_HOUR)
252                 snprintf(buf, l, USEC_FMT "h %s",
253                          d / USEC_PER_HOUR, s);
254         else if (d >= USEC_PER_HOUR)
255                 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
256                          d / USEC_PER_HOUR,
257                          (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
258         else if (d >= 5*USEC_PER_MINUTE)
259                 snprintf(buf, l, USEC_FMT "min %s",
260                          d / USEC_PER_MINUTE, s);
261         else if (d >= USEC_PER_MINUTE)
262                 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
263                          d / USEC_PER_MINUTE,
264                          (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
265         else if (d >= USEC_PER_SEC)
266                 snprintf(buf, l, USEC_FMT "s %s",
267                          d / USEC_PER_SEC, s);
268         else if (d >= USEC_PER_MSEC)
269                 snprintf(buf, l, USEC_FMT "ms %s",
270                          d / USEC_PER_MSEC, s);
271         else if (d > 0)
272                 snprintf(buf, l, USEC_FMT"us %s",
273                          d, s);
274         else
275                 snprintf(buf, l, "now");
276
277         buf[l-1] = 0;
278         return buf;
279 }
280
281 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
282         static const struct {
283                 const char *suffix;
284                 usec_t usec;
285         } table[] = {
286                 { "y", USEC_PER_YEAR },
287                 { "month", USEC_PER_MONTH },
288                 { "w", USEC_PER_WEEK },
289                 { "d", USEC_PER_DAY },
290                 { "h", USEC_PER_HOUR },
291                 { "min", USEC_PER_MINUTE },
292                 { "s", USEC_PER_SEC },
293                 { "ms", USEC_PER_MSEC },
294                 { "us", 1 },
295         };
296
297         unsigned i;
298         char *p = buf;
299         bool something = false;
300
301         assert(buf);
302         assert(l > 0);
303
304         if (t == USEC_INFINITY || t <= 0) {
305                 strncpy(p, t == USEC_INFINITY ? "infinity" : "0", l);
306                 p[l-1] = 0;
307                 return p;
308         }
309
310         /* The result of this function can be parsed with parse_sec */
311
312         for (i = 0; i < ELEMENTSOF(table); i++) {
313                 int k = 0;
314                 size_t n;
315                 bool done = false;
316                 usec_t a, b;
317
318                 if (t <= 0)
319                         break;
320
321                 if (t < accuracy && something)
322                         break;
323
324                 if (t < table[i].usec)
325                         continue;
326
327                 if (l <= 1)
328                         break;
329
330                 a = t / table[i].usec;
331                 b = t % table[i].usec;
332
333                 /* Let's see if we should shows this in dot notation */
334                 if (t < USEC_PER_MINUTE && b > 0) {
335                         usec_t cc;
336                         int j;
337
338                         j = 0;
339                         for (cc = table[i].usec; cc > 1; cc /= 10)
340                                 j++;
341
342                         for (cc = accuracy; cc > 1; cc /= 10) {
343                                 b /= 10;
344                                 j--;
345                         }
346
347                         if (j > 0) {
348                                 k = snprintf(p, l,
349                                              "%s"USEC_FMT".%0*llu%s",
350                                              p > buf ? " " : "",
351                                              a,
352                                              j,
353                                              (unsigned long long) b,
354                                              table[i].suffix);
355
356                                 t = 0;
357                                 done = true;
358                         }
359                 }
360
361                 /* No? Then let's show it normally */
362                 if (!done) {
363                         k = snprintf(p, l,
364                                      "%s"USEC_FMT"%s",
365                                      p > buf ? " " : "",
366                                      a,
367                                      table[i].suffix);
368
369                         t = b;
370                 }
371
372                 n = MIN((size_t) k, l);
373
374                 l -= n;
375                 p += n;
376
377                 something = true;
378         }
379
380         *p = 0;
381
382         return buf;
383 }
384
385 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
386
387         assert(f);
388         assert(name);
389         assert(t);
390
391         if (!dual_timestamp_is_set(t))
392                 return;
393
394         fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
395                 name,
396                 t->realtime,
397                 t->monotonic);
398 }
399
400 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
401         unsigned long long a, b;
402
403         assert(value);
404         assert(t);
405
406         if (sscanf(value, "%llu %llu", &a, &b) != 2)
407                 log_debug("Failed to parse finish timestamp value %s", value);
408         else {
409                 t->realtime = a;
410                 t->monotonic = b;
411         }
412 }
413
414 int parse_timestamp(const char *t, usec_t *usec) {
415         static const struct {
416                 const char *name;
417                 const int nr;
418         } day_nr[] = {
419                 { "Sunday",    0 },
420                 { "Sun",       0 },
421                 { "Monday",    1 },
422                 { "Mon",       1 },
423                 { "Tuesday",   2 },
424                 { "Tue",       2 },
425                 { "Wednesday", 3 },
426                 { "Wed",       3 },
427                 { "Thursday",  4 },
428                 { "Thu",       4 },
429                 { "Friday",    5 },
430                 { "Fri",       5 },
431                 { "Saturday",  6 },
432                 { "Sat",       6 },
433         };
434
435         const char *k;
436         struct tm tm, copy;
437         time_t x;
438         usec_t plus = 0, minus = 0, ret;
439         int r, weekday = -1;
440         unsigned i;
441
442         /*
443          * Allowed syntaxes:
444          *
445          *   2012-09-22 16:34:22
446          *   2012-09-22 16:34     (seconds will be set to 0)
447          *   2012-09-22           (time will be set to 00:00:00)
448          *   16:34:22             (date will be set to today)
449          *   16:34                (date will be set to today, seconds to 0)
450          *   now
451          *   yesterday            (time is set to 00:00:00)
452          *   today                (time is set to 00:00:00)
453          *   tomorrow             (time is set to 00:00:00)
454          *   +5min
455          *   -5days
456          *   @2147483647          (seconds since epoch)
457          *
458          */
459
460         assert(t);
461         assert(usec);
462
463         x = time(NULL);
464         assert_se(localtime_r(&x, &tm));
465         tm.tm_isdst = -1;
466
467         if (streq(t, "now"))
468                 goto finish;
469
470         else if (streq(t, "today")) {
471                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
472                 goto finish;
473
474         } else if (streq(t, "yesterday")) {
475                 tm.tm_mday --;
476                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
477                 goto finish;
478
479         } else if (streq(t, "tomorrow")) {
480                 tm.tm_mday ++;
481                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
482                 goto finish;
483
484         } else if (t[0] == '+') {
485                 r = parse_sec(t+1, &plus);
486                 if (r < 0)
487                         return r;
488
489                 goto finish;
490
491         } else if (t[0] == '-') {
492                 r = parse_sec(t+1, &minus);
493                 if (r < 0)
494                         return r;
495
496                 goto finish;
497
498         } else if (t[0] == '@')
499                 return parse_sec(t + 1, usec);
500
501         else if (endswith(t, " ago")) {
502                 _cleanup_free_ char *z;
503
504                 z = strndup(t, strlen(t) - 4);
505                 if (!z)
506                         return -ENOMEM;
507
508                 r = parse_sec(z, &minus);
509                 if (r < 0)
510                         return r;
511
512                 goto finish;
513         } else if (endswith(t, " left")) {
514                 _cleanup_free_ char *z;
515
516                 z = strndup(t, strlen(t) - 4);
517                 if (!z)
518                         return -ENOMEM;
519
520                 r = parse_sec(z, &plus);
521                 if (r < 0)
522                         return r;
523
524                 goto finish;
525         }
526
527         for (i = 0; i < ELEMENTSOF(day_nr); i++) {
528                 size_t skip;
529
530                 if (!startswith_no_case(t, day_nr[i].name))
531                         continue;
532
533                 skip = strlen(day_nr[i].name);
534                 if (t[skip] != ' ')
535                         continue;
536
537                 weekday = day_nr[i].nr;
538                 t += skip + 1;
539                 break;
540         }
541
542         copy = tm;
543         k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
544         if (k && *k == 0)
545                 goto finish;
546
547         tm = copy;
548         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
549         if (k && *k == 0)
550                 goto finish;
551
552         tm = copy;
553         k = strptime(t, "%y-%m-%d %H:%M", &tm);
554         if (k && *k == 0) {
555                 tm.tm_sec = 0;
556                 goto finish;
557         }
558
559         tm = copy;
560         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
561         if (k && *k == 0) {
562                 tm.tm_sec = 0;
563                 goto finish;
564         }
565
566         tm = copy;
567         k = strptime(t, "%y-%m-%d", &tm);
568         if (k && *k == 0) {
569                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
570                 goto finish;
571         }
572
573         tm = copy;
574         k = strptime(t, "%Y-%m-%d", &tm);
575         if (k && *k == 0) {
576                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
577                 goto finish;
578         }
579
580         tm = copy;
581         k = strptime(t, "%H:%M:%S", &tm);
582         if (k && *k == 0)
583                 goto finish;
584
585         tm = copy;
586         k = strptime(t, "%H:%M", &tm);
587         if (k && *k == 0) {
588                 tm.tm_sec = 0;
589                 goto finish;
590         }
591
592         return -EINVAL;
593
594 finish:
595         x = mktime(&tm);
596         if (x == (time_t) -1)
597                 return -EINVAL;
598
599         if (weekday >= 0 && tm.tm_wday != weekday)
600                 return -EINVAL;
601
602         ret = (usec_t) x * USEC_PER_SEC;
603
604         ret += plus;
605         if (ret > minus)
606                 ret -= minus;
607         else
608                 ret = 0;
609
610         *usec = ret;
611
612         return 0;
613 }
614
615 int parse_sec(const char *t, usec_t *usec) {
616         static const struct {
617                 const char *suffix;
618                 usec_t usec;
619         } table[] = {
620                 { "seconds", USEC_PER_SEC },
621                 { "second", USEC_PER_SEC },
622                 { "sec", USEC_PER_SEC },
623                 { "s", USEC_PER_SEC },
624                 { "minutes", USEC_PER_MINUTE },
625                 { "minute", USEC_PER_MINUTE },
626                 { "min", USEC_PER_MINUTE },
627                 { "months", USEC_PER_MONTH },
628                 { "month", USEC_PER_MONTH },
629                 { "msec", USEC_PER_MSEC },
630                 { "ms", USEC_PER_MSEC },
631                 { "m", USEC_PER_MINUTE },
632                 { "hours", USEC_PER_HOUR },
633                 { "hour", USEC_PER_HOUR },
634                 { "hr", USEC_PER_HOUR },
635                 { "h", USEC_PER_HOUR },
636                 { "days", USEC_PER_DAY },
637                 { "day", USEC_PER_DAY },
638                 { "d", USEC_PER_DAY },
639                 { "weeks", USEC_PER_WEEK },
640                 { "week", USEC_PER_WEEK },
641                 { "w", USEC_PER_WEEK },
642                 { "years", USEC_PER_YEAR },
643                 { "year", USEC_PER_YEAR },
644                 { "y", USEC_PER_YEAR },
645                 { "usec", 1ULL },
646                 { "us", 1ULL },
647                 { "", USEC_PER_SEC }, /* default is sec */
648         };
649
650         const char *p, *s;
651         usec_t r = 0;
652         bool something = false;
653
654         assert(t);
655         assert(usec);
656
657         p = t;
658
659         p += strspn(p, WHITESPACE);
660         s = startswith(p, "infinity");
661         if (s) {
662                 s += strspn(s, WHITESPACE);
663                 if (*s != 0)
664                         return -EINVAL;
665
666                 *usec = USEC_INFINITY;
667                 return 0;
668         }
669
670         for (;;) {
671                 long long l, z = 0;
672                 char *e;
673                 unsigned i, n = 0;
674
675                 p += strspn(p, WHITESPACE);
676
677                 if (*p == 0) {
678                         if (!something)
679                                 return -EINVAL;
680
681                         break;
682                 }
683
684                 errno = 0;
685                 l = strtoll(p, &e, 10);
686
687                 if (errno > 0)
688                         return -errno;
689
690                 if (l < 0)
691                         return -ERANGE;
692
693                 if (*e == '.') {
694                         char *b = e + 1;
695
696                         errno = 0;
697                         z = strtoll(b, &e, 10);
698                         if (errno > 0)
699                                 return -errno;
700
701                         if (z < 0)
702                                 return -ERANGE;
703
704                         if (e == b)
705                                 return -EINVAL;
706
707                         n = e - b;
708
709                 } else if (e == p)
710                         return -EINVAL;
711
712                 e += strspn(e, WHITESPACE);
713
714                 for (i = 0; i < ELEMENTSOF(table); i++)
715                         if (startswith(e, table[i].suffix)) {
716                                 usec_t k = (usec_t) z * table[i].usec;
717
718                                 for (; n > 0; n--)
719                                         k /= 10;
720
721                                 r += (usec_t) l * table[i].usec + k;
722                                 p = e + strlen(table[i].suffix);
723
724                                 something = true;
725                                 break;
726                         }
727
728                 if (i >= ELEMENTSOF(table))
729                         return -EINVAL;
730
731         }
732
733         *usec = r;
734
735         return 0;
736 }
737
738 int parse_nsec(const char *t, nsec_t *nsec) {
739         static const struct {
740                 const char *suffix;
741                 nsec_t nsec;
742         } table[] = {
743                 { "seconds", NSEC_PER_SEC },
744                 { "second", NSEC_PER_SEC },
745                 { "sec", NSEC_PER_SEC },
746                 { "s", NSEC_PER_SEC },
747                 { "minutes", NSEC_PER_MINUTE },
748                 { "minute", NSEC_PER_MINUTE },
749                 { "min", NSEC_PER_MINUTE },
750                 { "months", NSEC_PER_MONTH },
751                 { "month", NSEC_PER_MONTH },
752                 { "msec", NSEC_PER_MSEC },
753                 { "ms", NSEC_PER_MSEC },
754                 { "m", NSEC_PER_MINUTE },
755                 { "hours", NSEC_PER_HOUR },
756                 { "hour", NSEC_PER_HOUR },
757                 { "hr", NSEC_PER_HOUR },
758                 { "h", NSEC_PER_HOUR },
759                 { "days", NSEC_PER_DAY },
760                 { "day", NSEC_PER_DAY },
761                 { "d", NSEC_PER_DAY },
762                 { "weeks", NSEC_PER_WEEK },
763                 { "week", NSEC_PER_WEEK },
764                 { "w", NSEC_PER_WEEK },
765                 { "years", NSEC_PER_YEAR },
766                 { "year", NSEC_PER_YEAR },
767                 { "y", NSEC_PER_YEAR },
768                 { "usec", NSEC_PER_USEC },
769                 { "us", NSEC_PER_USEC },
770                 { "nsec", 1ULL },
771                 { "ns", 1ULL },
772                 { "", 1ULL }, /* default is nsec */
773         };
774
775         const char *p, *s;
776         nsec_t r = 0;
777         bool something = false;
778
779         assert(t);
780         assert(nsec);
781
782         p = t;
783
784         p += strspn(p, WHITESPACE);
785         s = startswith(p, "infinity");
786         if (s) {
787                 s += strspn(s, WHITESPACE);
788                 if (!*s != 0)
789                         return -EINVAL;
790
791                 *nsec = NSEC_INFINITY;
792                 return 0;
793         }
794
795         for (;;) {
796                 long long l, z = 0;
797                 char *e;
798                 unsigned i, n = 0;
799
800                 p += strspn(p, WHITESPACE);
801
802                 if (*p == 0) {
803                         if (!something)
804                                 return -EINVAL;
805
806                         break;
807                 }
808
809                 errno = 0;
810                 l = strtoll(p, &e, 10);
811
812                 if (errno > 0)
813                         return -errno;
814
815                 if (l < 0)
816                         return -ERANGE;
817
818                 if (*e == '.') {
819                         char *b = e + 1;
820
821                         errno = 0;
822                         z = strtoll(b, &e, 10);
823                         if (errno > 0)
824                                 return -errno;
825
826                         if (z < 0)
827                                 return -ERANGE;
828
829                         if (e == b)
830                                 return -EINVAL;
831
832                         n = e - b;
833
834                 } else if (e == p)
835                         return -EINVAL;
836
837                 e += strspn(e, WHITESPACE);
838
839                 for (i = 0; i < ELEMENTSOF(table); i++)
840                         if (startswith(e, table[i].suffix)) {
841                                 nsec_t k = (nsec_t) z * table[i].nsec;
842
843                                 for (; n > 0; n--)
844                                         k /= 10;
845
846                                 r += (nsec_t) l * table[i].nsec + k;
847                                 p = e + strlen(table[i].suffix);
848
849                                 something = true;
850                                 break;
851                         }
852
853                 if (i >= ELEMENTSOF(table))
854                         return -EINVAL;
855
856         }
857
858         *nsec = r;
859
860         return 0;
861 }
862
863 bool ntp_synced(void) {
864         struct timex txc = {};
865
866         if (adjtimex(&txc) < 0)
867                 return false;
868
869         if (txc.status & STA_UNSYNC)
870                 return false;
871
872         return true;
873 }
874
875 int get_timezones(char ***ret) {
876         _cleanup_fclose_ FILE *f = NULL;
877         _cleanup_strv_free_ char **zones = NULL;
878         size_t n_zones = 0, n_allocated = 0;
879
880         assert(ret);
881
882         zones = strv_new("UTC", NULL);
883         if (!zones)
884                 return -ENOMEM;
885
886         n_allocated = 2;
887         n_zones = 1;
888
889         f = fopen("/usr/share/zoneinfo/zone.tab", "re");
890         if (f) {
891                 char l[LINE_MAX];
892
893                 FOREACH_LINE(l, f, return -errno) {
894                         char *p, *w;
895                         size_t k;
896
897                         p = strstrip(l);
898
899                         if (isempty(p) || *p == '#')
900                                 continue;
901
902                         /* Skip over country code */
903                         p += strcspn(p, WHITESPACE);
904                         p += strspn(p, WHITESPACE);
905
906                         /* Skip over coordinates */
907                         p += strcspn(p, WHITESPACE);
908                         p += strspn(p, WHITESPACE);
909
910                         /* Found timezone name */
911                         k = strcspn(p, WHITESPACE);
912                         if (k <= 0)
913                                 continue;
914
915                         w = strndup(p, k);
916                         if (!w)
917                                 return -ENOMEM;
918
919                         if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
920                                 free(w);
921                                 return -ENOMEM;
922                         }
923
924                         zones[n_zones++] = w;
925                         zones[n_zones] = NULL;
926                 }
927
928                 strv_sort(zones);
929
930         } else if (errno != ENOENT)
931                 return -errno;
932
933         *ret = zones;
934         zones = NULL;
935
936         return 0;
937 }
938
939 bool timezone_is_valid(const char *name) {
940         bool slash = false;
941         const char *p, *t;
942         struct stat st;
943
944         if (!name || *name == 0 || *name == '/')
945                 return false;
946
947         for (p = name; *p; p++) {
948                 if (!(*p >= '0' && *p <= '9') &&
949                     !(*p >= 'a' && *p <= 'z') &&
950                     !(*p >= 'A' && *p <= 'Z') &&
951                     !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
952                         return false;
953
954                 if (*p == '/') {
955
956                         if (slash)
957                                 return false;
958
959                         slash = true;
960                 } else
961                         slash = false;
962         }
963
964         if (slash)
965                 return false;
966
967         t = strappenda("/usr/share/zoneinfo/", name);
968         if (stat(t, &st) < 0)
969                 return false;
970
971         if (!S_ISREG(st.st_mode))
972                 return false;
973
974         return true;
975 }
976
977 clockid_t clock_boottime_or_monotonic(void) {
978         static clockid_t clock = -1;
979         int fd;
980
981         if (clock != -1)
982                 return clock;
983
984         fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
985         if (fd < 0)
986                 clock = CLOCK_MONOTONIC;
987         else {
988                 safe_close(fd);
989                 clock = CLOCK_BOOTTIME;
990         }
991
992         return clock;
993 }