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