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