]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/calendarspec.c
Merge pull request #787 from dvdhrm/bus-list
[thirdparty/systemd.git] / src / basic / calendarspec.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 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 <stdlib.h>
23 #include <string.h>
24
25 #include "calendarspec.h"
26
27 #define BITS_WEEKDAYS 127
28
29 static void free_chain(CalendarComponent *c) {
30 CalendarComponent *n;
31
32 while (c) {
33 n = c->next;
34 free(c);
35 c = n;
36 }
37 }
38
39 void calendar_spec_free(CalendarSpec *c) {
40
41 if (!c)
42 return;
43
44 free_chain(c->year);
45 free_chain(c->month);
46 free_chain(c->day);
47 free_chain(c->hour);
48 free_chain(c->minute);
49 free_chain(c->second);
50
51 free(c);
52 }
53
54 static int component_compare(const void *_a, const void *_b) {
55 CalendarComponent * const *a = _a, * const *b = _b;
56
57 if ((*a)->value < (*b)->value)
58 return -1;
59 if ((*a)->value > (*b)->value)
60 return 1;
61
62 if ((*a)->repeat < (*b)->repeat)
63 return -1;
64 if ((*a)->repeat > (*b)->repeat)
65 return 1;
66
67 return 0;
68 }
69
70 static void sort_chain(CalendarComponent **c) {
71 unsigned n = 0, k;
72 CalendarComponent **b, *i, **j, *next;
73
74 assert(c);
75
76 for (i = *c; i; i = i->next)
77 n++;
78
79 if (n <= 1)
80 return;
81
82 j = b = alloca(sizeof(CalendarComponent*) * n);
83 for (i = *c; i; i = i->next)
84 *(j++) = i;
85
86 qsort(b, n, sizeof(CalendarComponent*), component_compare);
87
88 b[n-1]->next = NULL;
89 next = b[n-1];
90
91 /* Drop non-unique entries */
92 for (k = n-1; k > 0; k--) {
93 if (b[k-1]->value == next->value &&
94 b[k-1]->repeat == next->repeat) {
95 free(b[k-1]);
96 continue;
97 }
98
99 b[k-1]->next = next;
100 next = b[k-1];
101 }
102
103 *c = next;
104 }
105
106 static void fix_year(CalendarComponent *c) {
107 /* Turns 12 → 2012, 89 → 1989 */
108
109 while(c) {
110 CalendarComponent *n = c->next;
111
112 if (c->value >= 0 && c->value < 70)
113 c->value += 2000;
114
115 if (c->value >= 70 && c->value < 100)
116 c->value += 1900;
117
118 c = n;
119 }
120 }
121
122 int calendar_spec_normalize(CalendarSpec *c) {
123 assert(c);
124
125 if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
126 c->weekdays_bits = -1;
127
128 fix_year(c->year);
129
130 sort_chain(&c->year);
131 sort_chain(&c->month);
132 sort_chain(&c->day);
133 sort_chain(&c->hour);
134 sort_chain(&c->minute);
135 sort_chain(&c->second);
136
137 return 0;
138 }
139
140 _pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
141 if (!c)
142 return true;
143
144 if (c->value < from || c->value > to)
145 return false;
146
147 if (c->value + c->repeat > to)
148 return false;
149
150 if (c->next)
151 return chain_valid(c->next, from, to);
152
153 return true;
154 }
155
156 _pure_ bool calendar_spec_valid(CalendarSpec *c) {
157 assert(c);
158
159 if (c->weekdays_bits > BITS_WEEKDAYS)
160 return false;
161
162 if (!chain_valid(c->year, 1970, 2199))
163 return false;
164
165 if (!chain_valid(c->month, 1, 12))
166 return false;
167
168 if (!chain_valid(c->day, 1, 31))
169 return false;
170
171 if (!chain_valid(c->hour, 0, 23))
172 return false;
173
174 if (!chain_valid(c->minute, 0, 59))
175 return false;
176
177 if (!chain_valid(c->second, 0, 59))
178 return false;
179
180 return true;
181 }
182
183 static void format_weekdays(FILE *f, const CalendarSpec *c) {
184 static const char *const days[] = {
185 "Mon",
186 "Tue",
187 "Wed",
188 "Thu",
189 "Fri",
190 "Sat",
191 "Sun"
192 };
193
194 int l, x;
195 bool need_colon = false;
196
197 assert(f);
198 assert(c);
199 assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
200
201 for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) {
202
203 if (c->weekdays_bits & (1 << x)) {
204
205 if (l < 0) {
206 if (need_colon)
207 fputc(',', f);
208 else
209 need_colon = true;
210
211 fputs(days[x], f);
212 l = x;
213 }
214
215 } else if (l >= 0) {
216
217 if (x > l + 1) {
218 fputc(x > l + 2 ? '-' : ',', f);
219 fputs(days[x-1], f);
220 }
221
222 l = -1;
223 }
224 }
225
226 if (l >= 0 && x > l + 1) {
227 fputc(x > l + 2 ? '-' : ',', f);
228 fputs(days[x-1], f);
229 }
230 }
231
232 static void format_chain(FILE *f, int space, const CalendarComponent *c) {
233 assert(f);
234
235 if (!c) {
236 fputc('*', f);
237 return;
238 }
239
240 assert(c->value >= 0);
241 fprintf(f, "%0*i", space, c->value);
242
243 if (c->repeat > 0)
244 fprintf(f, "/%i", c->repeat);
245
246 if (c->next) {
247 fputc(',', f);
248 format_chain(f, space, c->next);
249 }
250 }
251
252 int calendar_spec_to_string(const CalendarSpec *c, char **p) {
253 char *buf = NULL;
254 size_t sz = 0;
255 FILE *f;
256 int r;
257
258 assert(c);
259 assert(p);
260
261 f = open_memstream(&buf, &sz);
262 if (!f)
263 return -ENOMEM;
264
265 if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
266 format_weekdays(f, c);
267 fputc(' ', f);
268 }
269
270 format_chain(f, 4, c->year);
271 fputc('-', f);
272 format_chain(f, 2, c->month);
273 fputc('-', f);
274 format_chain(f, 2, c->day);
275 fputc(' ', f);
276 format_chain(f, 2, c->hour);
277 fputc(':', f);
278 format_chain(f, 2, c->minute);
279 fputc(':', f);
280 format_chain(f, 2, c->second);
281
282 r = fflush_and_check(f);
283 if (r < 0) {
284 free(buf);
285 fclose(f);
286 return r;
287 }
288
289 fclose(f);
290
291 *p = buf;
292 return 0;
293 }
294
295 static int parse_weekdays(const char **p, CalendarSpec *c) {
296 static const struct {
297 const char *name;
298 const int nr;
299 } day_nr[] = {
300 { "Monday", 0 },
301 { "Mon", 0 },
302 { "Tuesday", 1 },
303 { "Tue", 1 },
304 { "Wednesday", 2 },
305 { "Wed", 2 },
306 { "Thursday", 3 },
307 { "Thu", 3 },
308 { "Friday", 4 },
309 { "Fri", 4 },
310 { "Saturday", 5 },
311 { "Sat", 5 },
312 { "Sunday", 6 },
313 { "Sun", 6 }
314 };
315
316 int l = -1;
317 bool first = true;
318
319 assert(p);
320 assert(*p);
321 assert(c);
322
323 for (;;) {
324 unsigned i;
325
326 if (!first && **p == ' ')
327 return 0;
328
329 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
330 size_t skip;
331
332 if (!startswith_no_case(*p, day_nr[i].name))
333 continue;
334
335 skip = strlen(day_nr[i].name);
336
337 if ((*p)[skip] != '-' &&
338 (*p)[skip] != ',' &&
339 (*p)[skip] != ' ' &&
340 (*p)[skip] != 0)
341 return -EINVAL;
342
343 c->weekdays_bits |= 1 << day_nr[i].nr;
344
345 if (l >= 0) {
346 int j;
347
348 if (l > day_nr[i].nr)
349 return -EINVAL;
350
351 for (j = l + 1; j < day_nr[i].nr; j++)
352 c->weekdays_bits |= 1 << j;
353 }
354
355 *p += skip;
356 break;
357 }
358
359 /* Couldn't find this prefix, so let's assume the
360 weekday was not specified and let's continue with
361 the date */
362 if (i >= ELEMENTSOF(day_nr))
363 return first ? 0 : -EINVAL;
364
365 /* We reached the end of the string */
366 if (**p == 0)
367 return 0;
368
369 /* We reached the end of the weekday spec part */
370 if (**p == ' ') {
371 *p += strspn(*p, " ");
372 return 0;
373 }
374
375 if (**p == '-') {
376 if (l >= 0)
377 return -EINVAL;
378
379 l = day_nr[i].nr;
380 } else
381 l = -1;
382
383 *p += 1;
384 first = false;
385 }
386 }
387
388 static int prepend_component(const char **p, CalendarComponent **c) {
389 unsigned long value, repeat = 0;
390 char *e = NULL, *ee = NULL;
391 CalendarComponent *cc;
392
393 assert(p);
394 assert(c);
395
396 errno = 0;
397 value = strtoul(*p, &e, 10);
398 if (errno > 0)
399 return -errno;
400 if (e == *p)
401 return -EINVAL;
402 if ((unsigned long) (int) value != value)
403 return -ERANGE;
404
405 if (*e == '/') {
406 repeat = strtoul(e+1, &ee, 10);
407 if (errno > 0)
408 return -errno;
409 if (ee == e+1)
410 return -EINVAL;
411 if ((unsigned long) (int) repeat != repeat)
412 return -ERANGE;
413 if (repeat <= 0)
414 return -ERANGE;
415
416 e = ee;
417 }
418
419 if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
420 return -EINVAL;
421
422 cc = new0(CalendarComponent, 1);
423 if (!cc)
424 return -ENOMEM;
425
426 cc->value = value;
427 cc->repeat = repeat;
428 cc->next = *c;
429
430 *p = e;
431 *c = cc;
432
433 if (*e ==',') {
434 *p += 1;
435 return prepend_component(p, c);
436 }
437
438 return 0;
439 }
440
441 static int parse_chain(const char **p, CalendarComponent **c) {
442 const char *t;
443 CalendarComponent *cc = NULL;
444 int r;
445
446 assert(p);
447 assert(c);
448
449 t = *p;
450
451 if (t[0] == '*') {
452 *p = t + 1;
453 *c = NULL;
454 return 0;
455 }
456
457 r = prepend_component(&t, &cc);
458 if (r < 0) {
459 free_chain(cc);
460 return r;
461 }
462
463 *p = t;
464 *c = cc;
465 return 0;
466 }
467
468 static int const_chain(int value, CalendarComponent **c) {
469 CalendarComponent *cc = NULL;
470
471 assert(c);
472
473 cc = new0(CalendarComponent, 1);
474 if (!cc)
475 return -ENOMEM;
476
477 cc->value = value;
478 cc->repeat = 0;
479 cc->next = *c;
480
481 *c = cc;
482
483 return 0;
484 }
485
486 static int parse_date(const char **p, CalendarSpec *c) {
487 const char *t;
488 int r;
489 CalendarComponent *first, *second, *third;
490
491 assert(p);
492 assert(*p);
493 assert(c);
494
495 t = *p;
496
497 if (*t == 0)
498 return 0;
499
500 r = parse_chain(&t, &first);
501 if (r < 0)
502 return r;
503
504 /* Already the end? A ':' as separator? In that case this was a time, not a date */
505 if (*t == 0 || *t == ':') {
506 free_chain(first);
507 return 0;
508 }
509
510 if (*t != '-') {
511 free_chain(first);
512 return -EINVAL;
513 }
514
515 t++;
516 r = parse_chain(&t, &second);
517 if (r < 0) {
518 free_chain(first);
519 return r;
520 }
521
522 /* Got two parts, hence it's month and day */
523 if (*t == ' ' || *t == 0) {
524 *p = t + strspn(t, " ");
525 c->month = first;
526 c->day = second;
527 return 0;
528 }
529
530 if (*t != '-') {
531 free_chain(first);
532 free_chain(second);
533 return -EINVAL;
534 }
535
536 t++;
537 r = parse_chain(&t, &third);
538 if (r < 0) {
539 free_chain(first);
540 free_chain(second);
541 return r;
542 }
543
544 /* Got tree parts, hence it is year, month and day */
545 if (*t == ' ' || *t == 0) {
546 *p = t + strspn(t, " ");
547 c->year = first;
548 c->month = second;
549 c->day = third;
550 return 0;
551 }
552
553 free_chain(first);
554 free_chain(second);
555 free_chain(third);
556 return -EINVAL;
557 }
558
559 static int parse_time(const char **p, CalendarSpec *c) {
560 CalendarComponent *h = NULL, *m = NULL, *s = NULL;
561 const char *t;
562 int r;
563
564 assert(p);
565 assert(*p);
566 assert(c);
567
568 t = *p;
569
570 if (*t == 0) {
571 /* If no time is specified at all, but a date of some
572 * kind, then this means 00:00:00 */
573 if (c->day || c->weekdays_bits > 0)
574 goto null_hour;
575
576 goto finish;
577 }
578
579 r = parse_chain(&t, &h);
580 if (r < 0)
581 goto fail;
582
583 if (*t != ':') {
584 r = -EINVAL;
585 goto fail;
586 }
587
588 t++;
589 r = parse_chain(&t, &m);
590 if (r < 0)
591 goto fail;
592
593 /* Already at the end? Then it's hours and minutes, and seconds are 0 */
594 if (*t == 0) {
595 if (m != NULL)
596 goto null_second;
597
598 goto finish;
599 }
600
601 if (*t != ':') {
602 r = -EINVAL;
603 goto fail;
604 }
605
606 t++;
607 r = parse_chain(&t, &s);
608 if (r < 0)
609 goto fail;
610
611 /* At the end? Then it's hours, minutes and seconds */
612 if (*t == 0)
613 goto finish;
614
615 r = -EINVAL;
616 goto fail;
617
618 null_hour:
619 r = const_chain(0, &h);
620 if (r < 0)
621 goto fail;
622
623 r = const_chain(0, &m);
624 if (r < 0)
625 goto fail;
626
627 null_second:
628 r = const_chain(0, &s);
629 if (r < 0)
630 goto fail;
631
632 finish:
633 *p = t;
634 c->hour = h;
635 c->minute = m;
636 c->second = s;
637 return 0;
638
639 fail:
640 free_chain(h);
641 free_chain(m);
642 free_chain(s);
643 return r;
644 }
645
646 int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
647 CalendarSpec *c;
648 int r;
649
650 assert(p);
651 assert(spec);
652
653 if (isempty(p))
654 return -EINVAL;
655
656 c = new0(CalendarSpec, 1);
657 if (!c)
658 return -ENOMEM;
659
660 if (strcaseeq(p, "minutely")) {
661 r = const_chain(0, &c->second);
662 if (r < 0)
663 goto fail;
664
665 } else if (strcaseeq(p, "hourly")) {
666 r = const_chain(0, &c->minute);
667 if (r < 0)
668 goto fail;
669 r = const_chain(0, &c->second);
670 if (r < 0)
671 goto fail;
672
673 } else if (strcaseeq(p, "daily")) {
674 r = const_chain(0, &c->hour);
675 if (r < 0)
676 goto fail;
677 r = const_chain(0, &c->minute);
678 if (r < 0)
679 goto fail;
680 r = const_chain(0, &c->second);
681 if (r < 0)
682 goto fail;
683
684 } else if (strcaseeq(p, "monthly")) {
685 r = const_chain(1, &c->day);
686 if (r < 0)
687 goto fail;
688 r = const_chain(0, &c->hour);
689 if (r < 0)
690 goto fail;
691 r = const_chain(0, &c->minute);
692 if (r < 0)
693 goto fail;
694 r = const_chain(0, &c->second);
695 if (r < 0)
696 goto fail;
697
698 } else if (strcaseeq(p, "annually") ||
699 strcaseeq(p, "yearly") ||
700 strcaseeq(p, "anually") /* backwards compatibility */ ) {
701
702 r = const_chain(1, &c->month);
703 if (r < 0)
704 goto fail;
705 r = const_chain(1, &c->day);
706 if (r < 0)
707 goto fail;
708 r = const_chain(0, &c->hour);
709 if (r < 0)
710 goto fail;
711 r = const_chain(0, &c->minute);
712 if (r < 0)
713 goto fail;
714 r = const_chain(0, &c->second);
715 if (r < 0)
716 goto fail;
717
718 } else if (strcaseeq(p, "weekly")) {
719
720 c->weekdays_bits = 1;
721
722 r = const_chain(0, &c->hour);
723 if (r < 0)
724 goto fail;
725 r = const_chain(0, &c->minute);
726 if (r < 0)
727 goto fail;
728 r = const_chain(0, &c->second);
729 if (r < 0)
730 goto fail;
731
732 } else if (strcaseeq(p, "quarterly")) {
733
734 r = const_chain(1, &c->month);
735 if (r < 0)
736 goto fail;
737 r = const_chain(4, &c->month);
738 if (r < 0)
739 goto fail;
740 r = const_chain(7, &c->month);
741 if (r < 0)
742 goto fail;
743 r = const_chain(10, &c->month);
744 if (r < 0)
745 goto fail;
746 r = const_chain(1, &c->day);
747 if (r < 0)
748 goto fail;
749 r = const_chain(0, &c->hour);
750 if (r < 0)
751 goto fail;
752 r = const_chain(0, &c->minute);
753 if (r < 0)
754 goto fail;
755 r = const_chain(0, &c->second);
756 if (r < 0)
757 goto fail;
758
759 } else if (strcaseeq(p, "biannually") ||
760 strcaseeq(p, "bi-annually") ||
761 strcaseeq(p, "semiannually") ||
762 strcaseeq(p, "semi-annually")) {
763
764 r = const_chain(1, &c->month);
765 if (r < 0)
766 goto fail;
767 r = const_chain(7, &c->month);
768 if (r < 0)
769 goto fail;
770 r = const_chain(1, &c->day);
771 if (r < 0)
772 goto fail;
773 r = const_chain(0, &c->hour);
774 if (r < 0)
775 goto fail;
776 r = const_chain(0, &c->minute);
777 if (r < 0)
778 goto fail;
779 r = const_chain(0, &c->second);
780 if (r < 0)
781 goto fail;
782
783 } else {
784 r = parse_weekdays(&p, c);
785 if (r < 0)
786 goto fail;
787
788 r = parse_date(&p, c);
789 if (r < 0)
790 goto fail;
791
792 r = parse_time(&p, c);
793 if (r < 0)
794 goto fail;
795
796 if (*p != 0) {
797 r = -EINVAL;
798 goto fail;
799 }
800 }
801
802 r = calendar_spec_normalize(c);
803 if (r < 0)
804 goto fail;
805
806 if (!calendar_spec_valid(c)) {
807 r = -EINVAL;
808 goto fail;
809 }
810
811 *spec = c;
812 return 0;
813
814 fail:
815 calendar_spec_free(c);
816 return r;
817 }
818
819 static int find_matching_component(const CalendarComponent *c, int *val) {
820 const CalendarComponent *n;
821 int d = -1;
822 bool d_set = false;
823 int r;
824
825 assert(val);
826
827 if (!c)
828 return 0;
829
830 while (c) {
831 n = c->next;
832
833 if (c->value >= *val) {
834
835 if (!d_set || c->value < d) {
836 d = c->value;
837 d_set = true;
838 }
839
840 } else if (c->repeat > 0) {
841 int k;
842
843 k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat);
844
845 if (!d_set || k < d) {
846 d = k;
847 d_set = true;
848 }
849 }
850
851 c = n;
852 }
853
854 if (!d_set)
855 return -ENOENT;
856
857 r = *val != d;
858 *val = d;
859 return r;
860 }
861
862 static bool tm_out_of_bounds(const struct tm *tm) {
863 struct tm t;
864 assert(tm);
865
866 t = *tm;
867
868 if (mktime(&t) == (time_t) -1)
869 return true;
870
871 /* Did any normalization take place? If so, it was out of bounds before */
872 return
873 t.tm_year != tm->tm_year ||
874 t.tm_mon != tm->tm_mon ||
875 t.tm_mday != tm->tm_mday ||
876 t.tm_hour != tm->tm_hour ||
877 t.tm_min != tm->tm_min ||
878 t.tm_sec != tm->tm_sec;
879 }
880
881 static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
882 struct tm t;
883 int k;
884
885 if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
886 return true;
887
888 t = *tm;
889 if (mktime(&t) == (time_t) -1)
890 return false;
891
892 k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
893 return (weekdays_bits & (1 << k));
894 }
895
896 static int find_next(const CalendarSpec *spec, struct tm *tm) {
897 struct tm c;
898 int r;
899
900 assert(spec);
901 assert(tm);
902
903 c = *tm;
904
905 for (;;) {
906 /* Normalize the current date */
907 mktime(&c);
908 c.tm_isdst = -1;
909
910 c.tm_year += 1900;
911 r = find_matching_component(spec->year, &c.tm_year);
912 c.tm_year -= 1900;
913
914 if (r > 0) {
915 c.tm_mon = 0;
916 c.tm_mday = 1;
917 c.tm_hour = c.tm_min = c.tm_sec = 0;
918 }
919 if (r < 0 || tm_out_of_bounds(&c))
920 return r;
921
922 c.tm_mon += 1;
923 r = find_matching_component(spec->month, &c.tm_mon);
924 c.tm_mon -= 1;
925
926 if (r > 0) {
927 c.tm_mday = 1;
928 c.tm_hour = c.tm_min = c.tm_sec = 0;
929 }
930 if (r < 0 || tm_out_of_bounds(&c)) {
931 c.tm_year ++;
932 c.tm_mon = 0;
933 c.tm_mday = 1;
934 c.tm_hour = c.tm_min = c.tm_sec = 0;
935 continue;
936 }
937
938 r = find_matching_component(spec->day, &c.tm_mday);
939 if (r > 0)
940 c.tm_hour = c.tm_min = c.tm_sec = 0;
941 if (r < 0 || tm_out_of_bounds(&c)) {
942 c.tm_mon ++;
943 c.tm_mday = 1;
944 c.tm_hour = c.tm_min = c.tm_sec = 0;
945 continue;
946 }
947
948 if (!matches_weekday(spec->weekdays_bits, &c)) {
949 c.tm_mday++;
950 c.tm_hour = c.tm_min = c.tm_sec = 0;
951 continue;
952 }
953
954 r = find_matching_component(spec->hour, &c.tm_hour);
955 if (r > 0)
956 c.tm_min = c.tm_sec = 0;
957 if (r < 0 || tm_out_of_bounds(&c)) {
958 c.tm_mday ++;
959 c.tm_hour = c.tm_min = c.tm_sec = 0;
960 continue;
961 }
962
963 r = find_matching_component(spec->minute, &c.tm_min);
964 if (r > 0)
965 c.tm_sec = 0;
966 if (r < 0 || tm_out_of_bounds(&c)) {
967 c.tm_hour ++;
968 c.tm_min = c.tm_sec = 0;
969 continue;
970 }
971
972 r = find_matching_component(spec->second, &c.tm_sec);
973 if (r < 0 || tm_out_of_bounds(&c)) {
974 c.tm_min ++;
975 c.tm_sec = 0;
976 continue;
977 }
978
979
980 *tm = c;
981 return 0;
982 }
983 }
984
985 int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
986 struct tm tm;
987 time_t t;
988 int r;
989
990 assert(spec);
991 assert(next);
992
993 t = (time_t) (usec / USEC_PER_SEC) + 1;
994 assert_se(localtime_r(&t, &tm));
995
996 r = find_next(spec, &tm);
997 if (r < 0)
998 return r;
999
1000 t = mktime(&tm);
1001 if (t == (time_t) -1)
1002 return -EINVAL;
1003
1004 *next = (usec_t) t * USEC_PER_SEC;
1005 return 0;
1006 }