1 /* $XConsortium: reclotick.c /main/6 1996/11/21 19:45:29 drk $ */
3 * (c) Copyright 1993, 1994 Hewlett-Packard Company
4 * (c) Copyright 1993, 1994 International Business Machines Corp.
5 * (c) Copyright 1993, 1994 Novell, Inc.
6 * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
9 #define XOS_USE_NO_LOCKING
10 #define X_INCLUDE_TIME_H
11 #include <X11/Xos_r.h>
18 static Tick DoMinute(const Tick, const Tick, const RepeatEvent *,
20 static Tick DoDay(const Tick, const Tick, const RepeatEvent *,
22 static Tick DoWeek(const Tick, const Tick, const RepeatEvent *,
24 static Tick DoMonthDay(const Tick, const Tick, const RepeatEvent *,
26 static Tick DoMonthPos(const Tick, const Tick, const RepeatEvent *,
28 static Tick DoYearByMonth(const Tick, const Tick, const RepeatEvent *,
30 static Tick DoYearByDay(const Tick, const Tick, const RepeatEvent *,
32 static void DoDSTAdjustment(const Tick, struct tm *);
33 static RepeatEventState *InitRepeatEventState(const RepeatEvent *);
34 static Tick DSTAdjustment(const struct tm *, const struct tm *);
35 static int GetMonthDiff(const struct tm *, const struct tm *);
36 static int MonthDayNumIntervals(struct tm *, struct tm *,
37 const RepeatEvent *, const unsigned int *,
39 static int MonthPosNumIntervals(struct tm *, struct tm *,
40 const RepeatEvent *, const WeekDayTime *,
41 const unsigned int, struct tm *);
42 void FillInRepeatEvent(const Tick, RepeatEvent *);
45 * Return the closest time following or equal to the target time given a
50 const Tick _target_time,
51 const Tick start_time,
53 RepeatEventState **res)
57 target_time = _target_time;
59 if (!re) return (Tick)NULL;
61 FillInRepeatEvent(start_time, re);
63 if (!(*res = InitRepeatEventState(re)))
66 if (target_time < start_time)
67 target_time = start_time;
69 switch (re->re_type) {
71 closest_tick = DoMinute(target_time, start_time, re, *res);
74 closest_tick = DoDay(target_time, start_time, re, *res);
77 closest_tick = DoWeek(target_time, start_time, re, *res);
79 case RT_MONTHLY_POSITION:
80 /* Establish the real start time */
81 real_start_time = DoMonthPos(start_time, start_time, re, *res);
82 if (target_time < real_start_time)
83 target_time = real_start_time;
84 if (target_time == real_start_time) {
85 (*res)->res_duration = re->re_duration - 1;
86 closest_tick = real_start_time;
88 closest_tick = DoMonthPos(target_time,
89 real_start_time, re, *res);
92 closest_tick = DoMonthDay(target_time, start_time, re, *res);
95 closest_tick = DoYearByMonth(target_time, start_time, re, *res);
98 closest_tick = DoYearByDay(target_time, start_time, re, *res);
103 * Make sure the closest time is not past the appt's end time.
105 if ((!closest_tick) ||
106 (re->re_end_date && re->re_end_date < closest_tick)) {
113 * If the duration was not set (thus strictly using the end date)
114 * reset the RepeatEventState duration back to not-set. This is
115 * cleaner than having conditionals through out the code checking
116 * to see if the duration needs to be updated.
118 if (re->re_duration == RE_NOTSET)
119 (*res)->res_duration == RE_NOTSET;
129 const Tick target_time,
130 const Tick start_time,
131 const RepeatEvent *re,
132 RepeatEventState *res)
138 delta_seconds = target_time - start_time;
140 /* The number of intervals required to span the time from the
141 * start_time to the target_time given the interval size.
142 * The interval size comes from the rule (e.g. M5 or 5 * 60)
144 num_intervals = delta_seconds / (re->re_interval * 60) + 1;
146 if (num_intervals > re->re_duration) {
147 /* The Minute portion of rule does not allow us to reach
148 * the target_time because of the duration limit.
150 closest_tick = re->re_duration * re->re_interval * 60;
152 } else if (num_intervals < re->re_duration) {
153 /* In this case we reached the target_time without using
154 * up all of the intervals allotted to us by the duration.
156 closest_tick = num_intervals * re->re_interval * 60;
157 /* res->res_duration -= (num_intervals + 1); */
159 closest_tick = num_intervals * re->re_interval * 60;
160 /* res->res_duration -= (num_intervals + 1); */
171 const Tick target_time,
172 const Tick start_time,
173 const RepeatEvent *re,
174 RepeatEventState *res)
179 daysec = 60 * 60 * 24,
184 unsigned int ntime = RE_DAILY(re)->dd_ntime;
187 Time *time_list = RE_DAILY(re)->dd_time;
188 _Xltimeparams localtime_buf;
190 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
191 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
192 dst_adj = DSTAdjustment(&start_tm, &target_tm);
194 /* Normalize time to 00:00 */
197 start_tm.tm_hour = 0;
198 start_tm.tm_isdst = -1;
199 base_time = mktime(&start_tm);
201 delta_seconds = target_time - base_time + dst_adj;
203 /* The number of intervals required to span the time
204 * from the start_time to the target_time given the
205 * interval size. The interval size comes from the
206 * rule (e.g. D5 or 5 * daysec)
208 num_intervals = delta_seconds / (re->re_interval * daysec);
210 /* This brings us to the interval closest to the target
211 * time, although it may not actually be the same day.
213 base_time += num_intervals * (re->re_interval * daysec) - dst_adj;
215 if (!InTimeRange(num_intervals, re->re_duration)) {
216 /* We hit the duration limit. */
221 base_tm = *_XLocaltime(&base_time, localtime_buf);
223 /* If we are not on the same day we need to move
224 * forward one interval and take the earliest time.
225 * XXX: This won't work with composite rules.
227 if (!SAME_DAY(&base_tm, &target_tm)) {
228 /* Add one interval to the base_time. */
229 base_time += 1 * (re->re_interval * daysec);
232 /* By moving ahead one day we might have crossed a dst border.*/
233 DoDSTAdjustment(base_time, &base_tm);
235 if (!InTimeRange(num_intervals, re->re_duration)) {
236 /* We hit the duration limit. */
241 /* Take into account any specific times that are a part
242 * of this daily repeating rule: e.g. D2 0100 1000 1400 #3.
243 * We walk through the times for this appointment looking for
244 * one later than the target time.
246 for (i = 0; i < ntime; i++) {
247 /* Add the hour that is to be tested to the normalized
248 * time and see if it is later than the target time.
250 base_tm.tm_min = time_list[i]%100;
251 base_tm.tm_hour = time_list[i]/100;
252 base_tm.tm_isdst = -1;
253 next_time = mktime(&base_tm);
254 if (next_time >= target_time) {
255 res->res_duration = re->re_duration -
257 RES_DSTATE(res).res_time = i;
262 /* The target time falls after the latest time on
263 * this appt day. We must move forward one interval
264 * and take the earliest time.
265 * XXX: Composite rules issue.
267 base_tm = *_XLocaltime(&base_time, localtime_buf);
268 /* Add one interval to the base_time. */
269 base_time += 1 * (re->re_interval * daysec);
272 if (!InTimeRange(num_intervals, re->re_duration)) {
273 /* We hit the duration limit. */
278 /* By moving ahead one day we might have crossed a dst border.*/
279 DoDSTAdjustment(base_time, &base_tm);
281 /* Add the hour that is to be tested to the normalized
282 * time and see if it is later than the target time.
284 base_tm.tm_min = time_list[0]%100;
285 base_tm.tm_hour = time_list[0]/100;
286 base_tm.tm_isdst = -1;
287 next_time = mktime(&base_tm);
289 res->res_duration = re->re_duration - (num_intervals + 1);
290 RES_DSTATE(res).res_time = 0;
297 * Example: W2 MO WE FR #4
301 const Tick _target_time,
302 const Tick _start_time,
303 const RepeatEvent *re,
304 RepeatEventState *res)
310 daysec = 60 * 60 * 24,
312 unsigned int ntime = RE_WEEKLY(re)->wd_ndaytime;
316 Tick target_time = _target_time,
317 start_time = _start_time,
322 DayTime *day_list = RE_WEEKLY(re)->wd_daytime;
323 RepeatEventState *unused;
324 _Xltimeparams localtime_buf;
326 /* Make sure the start time is on the first real event slot. */
328 if (!(unused = InitRepeatEventState(re)))
330 start_time = DoWeek(NULL, _start_time, re, unused);
332 if (_target_time < start_time)
333 target_time = start_time;
335 target_time = _start_time;
337 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
338 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
339 appt_time = start_tm.tm_hour * 100 + start_tm.tm_min;
340 dst_adj = DSTAdjustment(&start_tm, &target_tm);
342 /* Normalize start time to the beginning of the week. */
343 start_tm.tm_mday -= start_tm.tm_wday;
344 start_tm.tm_sec = start_tm.tm_min = start_tm.tm_hour = 0;
345 start_tm.tm_isdst = -1;
346 begin_time = mktime(&start_tm);
347 start_tm = *_XLocaltime((const time_t *)&begin_time, localtime_buf);
349 delta_seconds = target_time - begin_time + dst_adj;
351 /* The number of intervals required to span the time
352 * from the start_time to the target_time given the
353 * interval size. The interval size comes from the
354 * rule (e.g. W5 or 5 * wksec)
356 num_intervals = delta_seconds / (re->re_interval * wksec);
358 /* This brings us to the interval closest to the target
359 * time, although it may not actually be the right week.
361 base_time = begin_time + num_intervals * (re->re_interval * wksec);
362 base_tm = *_XLocaltime(&base_time, localtime_buf);
363 dst_adj = DSTAdjustment(&start_tm, &base_tm);
365 if (!InTimeRange(num_intervals, re->re_duration)) {
366 /* We hit the duration limit. */
371 base_time -= dst_adj;
372 base_tm = *_XLocaltime(&base_time, localtime_buf);
375 if (same_week(&target_tm, &base_tm)) {
378 /* Take the next event */
379 for (i = 0; i < ntime; i++) {
380 if (day_list[i].dt_day > target_tm.tm_wday) {
381 event_wday = day_list[i].dt_day;
383 } else if (day_list[i].dt_day == target_tm.tm_wday) {
384 /* If they are the same day, the day_list time
385 * must be later than the target time.
387 int day_time = (day_list[i].dt_time) ?
388 day_list[i].dt_time[0]:
390 /* XXX: Must walk the time list too. */
391 if (TIME_OF_DAY(&target_tm) <=
392 HOURTOSEC(day_time)) {
393 event_wday = day_list[i].dt_day;
399 RES_WSTATE(res).res_daytime = i;
400 RES_WSTATE(res).res_time = 0;
402 /* The target date is on the same week, but falls after the
403 * last weekday the event could happen on.
405 if (event_wday == -1) {
406 /* XXX: Lose the goto. */
409 base_tm.tm_mday += GetWDayDiff(base_tm.tm_wday, event_wday);
412 /* We will need to go one more interval */
413 if (!InTimeRange(++num_intervals, re->re_duration)) {
414 /* We hit the duration limit. */
418 /* Since the base_tm has been normalized to the beginning
419 * of the week, we can assume we are on Sunday.
421 base_tm.tm_mday += re->re_interval * 7;
422 /* If the target day is smaller than the base day then we
423 * can take the first day in the next event week.
425 if (base_tm.tm_mday > target_tm.tm_mday) {
426 base_tm.tm_mday += day_list[0].dt_day;
428 RES_WSTATE(res).res_daytime = 0;
429 RES_WSTATE(res).res_time = 0;
432 base_tm.tm_hour = appt_time / 100;
433 base_tm.tm_min = appt_time % 100;
434 base_tm.tm_isdst = -1;
435 res->res_duration = re->re_duration - num_intervals;
436 next_time = mktime(&base_tm);
443 * Example: MD2 1 10 20 30 #10
447 const Tick target_time,
448 const Tick start_time,
449 const RepeatEvent *re,
450 RepeatEventState *res)
455 unsigned int ndays = RE_MONTHLY(re)->md_nitems;
461 unsigned int *day_list = RE_MONTHLY(re)->md_days;
462 _Xltimeparams localtime_buf;
464 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
465 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
466 event_day = day_list[0];
467 num_intervals = MonthDayNumIntervals(&start_tm, &target_tm, re,
470 if (!InTimeRange(num_intervals, re->re_duration)) {
471 /* We hit the duration limit. */
475 if (SAME_MONTH(&base_tm, &target_tm)) {
476 int next_interval = TRUE,
479 for (i = 0; i < ndays; i++) {
482 day = DayOfMonth(day_list[i], base_tm.tm_mon,
485 if (day < target_tm.tm_mday)
487 if (day == target_tm.tm_mday)
488 /* If it is on the same day, the event time
489 * must be later than the target time.
491 if (TIME_OF_DAY(&target_tm)
492 > TIME_OF_DAY(&start_tm))
496 next_interval = FALSE;
499 if (day > target_tm.tm_mday) {
501 next_interval = FALSE;
505 /* We are on the right month and we found a time after the
508 if (!next_interval) {
509 base_tm.tm_mday = event_day;
510 base_tm.tm_isdst = -1;
511 next_time = mktime(&base_tm);
512 /* If the day exists (e.g. 31st in July) we're done */
513 if (DayExists(event_day, base_tm.tm_mon,
515 /* Update repeat state info */
516 res->res_duration = re->re_duration
518 RES_MSTATE(res).res_day = i;
519 next_time = mktime(&base_tm);
527 if (!InTimeRange(num_intervals, re->re_duration)) {
528 /* We hit the duration limit. */
533 /* Since we are moving to the next interval, use the first day */
534 event_day = day_list[0];
536 /* Event is in the next interval */
537 base_tm.tm_mon += 1 * re->re_interval;
539 base_tm.tm_isdst = -1;
540 base_time = mktime(&base_tm);
541 base_tm = *_XLocaltime(&base_time, localtime_buf);
543 /* Stop when the day exists in that month */
544 } while (!DayExists(event_day, base_tm.tm_mon, base_tm.tm_year));
546 base_tm.tm_mday = DayOfMonth(event_day, base_tm.tm_mon,
548 base_tm.tm_isdst = -1;
549 next_time = mktime(&base_tm);
551 res->res_duration = re->re_duration - num_intervals;
552 RES_MSTATE(res).res_day = 0;
560 * Example: MP2 1+ MO TU 2- TH #3
564 const Tick target_time,
565 const Tick start_time,
566 const RepeatEvent *re,
567 RepeatEventState *res)
571 unsigned int ndays = RE_MONTHLY(re)->md_nitems;
576 WeekDayTime *wdt_list = RE_MONTHLY(re)->md_weektime;
577 _Xltimeparams localtime_buf;
579 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
580 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
581 num_intervals = MonthPosNumIntervals(&start_tm, &target_tm, re,
582 wdt_list, ndays, &base_tm);
585 if (!InTimeRange(num_intervals, re->re_duration)) {
586 /* We hit the duration limit. */
590 base_tm.tm_isdst = -1;
592 if (SAME_MONTH(&target_tm, &base_tm)) {
593 base_time = mktime(&base_tm);
594 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
595 base_time = WeekNumberToDay(base_time,
596 wdt_list[0].wdt_week[0],
597 wdt_list[0].wdt_day[0]);
598 if (base_time >= target_time)
600 /* target_time came after the slot for this month */
605 base_tm.tm_mon += re->re_interval;
606 base_tm.tm_isdst = -1;
607 /* Move to the first interval after the target time */
608 base_time = mktime(&base_tm);
609 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
610 base_time = WeekNumberToDay(base_time,
611 wdt_list[0].wdt_week[0],
612 wdt_list[0].wdt_day[0]);
613 } while (!base_time);
617 /* Update repeat state info */
618 res->res_duration = re->re_duration - num_intervals;
619 RES_MSTATE(res).res_weektime = 0;
620 RES_MSTATE(res).res_wday = 0;
621 RES_MSTATE(res).res_wtime = 0;
622 RES_MSTATE(res).res_wweek = 0;
629 * Example: YM1 2 5 9 #4
633 const Tick _target_time,
634 const Tick _start_time,
635 const RepeatEvent *re,
636 RepeatEventState *res)
640 unsigned int nitems = RE_YEARLY(re)->yd_nitems;
645 start_time = _start_time,
646 target_time = _target_time;
647 unsigned int *month_list = RE_YEARLY(re)->yd_items;
648 RepeatEventState *unused;
649 _Xltimeparams localtime_buf;
651 /* Make sure the start time is on the first real event slot. */
653 if (!(unused = InitRepeatEventState(re)))
655 start_time = DoYearByMonth(NULL, _start_time, re, unused);
657 if (_target_time < start_time)
658 target_time = start_time;
660 target_time = _start_time;
662 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
663 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
664 nyears = target_tm.tm_year - start_tm.tm_year;
665 num_intervals = nyears / re->re_interval;
667 if (!InTimeRange(num_intervals, re->re_duration)) {
668 /* We hit the duration limit. */
673 base_tm.tm_year += num_intervals * re->re_interval;
674 base_tm.tm_isdst = -1;
676 base_time = mktime(&base_tm);
677 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
679 if (base_tm.tm_year == target_tm.tm_year) {
682 /* Look for a month that is >= the target month */
683 for (i = 0; i < nitems; i++) {
684 /* If the months are equal the target time has to be
685 * less than the next tick.
687 if (month_list[i] - 1 == target_tm.tm_mon) {
688 base_tm.tm_mon = month_list[i] - 1;
689 base_tm.tm_isdst = -1;
690 base_time = mktime(&base_tm);
691 base_tm = *_XLocaltime(&base_time, localtime_buf);
692 if (TIMEOFMONTH(&base_tm) >=
693 TIMEOFMONTH(&target_tm)){
694 res->res_duration = re->re_duration -
696 RES_YSTATE(res).res_daymonth = i;
699 } else if (month_list[i] - 1 >= target_tm.tm_mon) {
700 base_tm.tm_mon = month_list[i] - 1;
701 base_tm.tm_isdst = -1;
702 base_time = mktime(&base_tm);
703 res->res_duration = re->re_duration -
705 RES_YSTATE(res).res_daymonth = i;
712 * The base year is greater than the target year, take the first
715 if (!InTimeRange(++num_intervals, re->re_duration)) {
716 /* We hit the duration limit. */
721 base_tm.tm_year += re->re_interval;
722 base_tm.tm_mon = month_list[0] - 1;
723 base_tm.tm_isdst = -1;
724 base_time = mktime(&base_tm);
726 res->res_duration = re->re_duration - num_intervals;
727 RES_YSTATE(res).res_daymonth = 0;
733 * Example: YD1 100 200 300 #4
737 const Tick _target_time,
738 const Tick _start_time,
739 const RepeatEvent *re,
740 RepeatEventState *res)
744 unsigned int nitems = RE_YEARLY(re)->yd_nitems;
749 target_time = _target_time,
750 start_time = _start_time;
751 unsigned int *day_list = RE_YEARLY(re)->yd_items;
752 RepeatEventState *unused;
753 _Xltimeparams localtime_buf;
755 /* Make sure the start time is on the first real event slot. */
757 if (!(unused = InitRepeatEventState(re)))
759 start_time = DoYearByDay(NULL, _start_time, re, unused);
761 if (_target_time < start_time)
762 target_time = start_time;
764 target_time = _start_time;
766 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
767 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
768 nyears = target_tm.tm_year - start_tm.tm_year;
769 num_intervals = nyears / re->re_interval;
771 if (!InTimeRange(num_intervals, re->re_duration)) {
772 /* We hit the duration limit. */
777 * XXX: day_list == 366 is a special case...that is not supported
782 base_tm.tm_year += num_intervals * re->re_interval;
784 /* If the years are the same then go down the list of days looking
785 * for one later than the target time.
787 if (base_tm.tm_year == target_tm.tm_year) {
789 for (i = 0; i < nitems; i++) {
790 base_tm.tm_mday = day_list[i];
792 base_tm.tm_isdst = -1;
793 base_time = mktime(&base_tm);
795 /* We found the closest tick */
796 if (base_time >= target_time) {
797 res->res_duration = re->re_duration -
799 RES_YSTATE(res).res_daymonth = i;
806 * We either were not on the same year or the above fell through
807 * as we crossed into the next interval.
812 if (!InTimeRange(num_intervals, re->re_duration)) {
813 /* We hit the duration limit. */
818 base_tm.tm_year += 1 * re->re_interval;
819 base_tm.tm_mday = day_list[0];
821 base_tm.tm_isdst = -1;
822 base_time = mktime(&base_tm);
824 res->res_duration = re->re_duration - num_intervals;
825 RES_YSTATE(res).res_daymonth = 0;
831 /* Calculate the number of months between two dates */
832 /* 3/20/90 - 1/2/94 = 46 months */
835 const struct tm *start_tm,
836 const struct tm *end_tm)
838 return ((end_tm->tm_year - start_tm->tm_year + 1) * 12 -
839 (start_tm->tm_mon + 1) - (12 - (end_tm->tm_mon + 1)));
844 const struct tm *tm1,
845 const struct tm *tm2)
847 if (tm1->tm_isdst == -1 || tm2->tm_isdst == -1)
850 if (tm1->tm_isdst != tm2->tm_isdst) {
851 if (tm1->tm_isdst) /* From day light savings to standard */
853 else /* From standard to day light savings */
861 const Tick begin_time,
862 struct tm *end_time) /* Return */
866 _begin_time = begin_time;
867 _Xltimeparams localtime_buf;
869 /* By moving ahead one day we might have crossed a dst border.*/
870 next_day = *_XLocaltime(&begin_time, localtime_buf);
871 dst_adj = DSTAdjustment(end_time, &next_day);
873 _begin_time -= dst_adj;
874 *end_time = *_XLocaltime(&_begin_time, localtime_buf);
876 *end_time = next_day;
880 * Initialize the RepeatEventState struct.
882 static RepeatEventState *
883 InitRepeatEventState(
884 const RepeatEvent *re)
886 RepeatEventState *res;
888 if (!(res = (RepeatEventState *)calloc(1, sizeof(RepeatEventState))))
889 return (RepeatEventState *)NULL;
892 res->res_duration = re->re_duration;
899 * Determine the number of intervals between the start_tm and the target_tm,
900 * the base_tm which is returned is the last event generated before the
904 MonthDayNumIntervals(
906 struct tm *target_tm,
907 const RepeatEvent *re,
908 const unsigned int *md_days,
909 struct tm *base_tm) /* Return */
911 int num_intervals = 0;
917 _Xltimeparams localtime_buf;
919 /* The 28th - 31st may not exist in a given month thus if only these
920 * days are specified in a rule it is necessary to calculate the
921 * correct month by brute force versus using a mathematical calculation.
923 if (md_days[0] > 28) {
924 *base_tm = *start_tm;
927 cur_tm.tm_isdst = -1;
928 cur_time = mktime(&cur_tm);
929 target_tm->tm_isdst = -1;
930 target_time = mktime((struct tm*)target_tm);
931 last_time = cur_time;
933 while (cur_time < target_time) {
934 cur_tm.tm_mon += re->re_interval;
935 cur_tm.tm_isdst = -1;
936 cur_time = mktime(&cur_tm);
937 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
939 if (DayExists(md_days[0], cur_tm.tm_mon,
941 if (cur_time >= target_time) {
942 cur_time = last_time;
943 cur_tm = *_XLocaltime((const time_t *)
944 &last_time, localtime_buf);
947 /* Remember the last time in case we need to
948 * back up one interval.
950 last_time = cur_time;
955 if (!InTimeRange(num_intervals, re->re_duration))
958 if (SAME_MONTH(target_tm, &cur_tm)) break;
961 num_intervals = GetMonthDiff(start_tm, target_tm)
963 *base_tm = *start_tm;
964 base_tm->tm_isdst = -1;
965 /* Move to the closest interval before the target time */
966 base_tm->tm_mon += num_intervals * re->re_interval;
967 base_time = mktime(base_tm);
968 *base_tm = *_XLocaltime(&base_time, localtime_buf);
971 return (num_intervals);
975 * Count the number of intervals up to, but before the target time. The
976 * base time returned in the last valid interval before the target time.
979 MonthPosNumIntervals(
981 struct tm *target_tm,
982 const RepeatEvent *re,
983 const WeekDayTime *wdt_list,
984 const unsigned int nwdt_list,
985 struct tm *base_tm) /* Return */
987 int num_intervals = 0,
994 _Xltimeparams localtime_buf;
996 for (i = 0; i < nwdt_list; i++) {
997 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
998 if ((wdt_list[i].wdt_week[j] != WK_F5) &&
999 (wdt_list[i].wdt_week[j] != WK_L5)) {
1000 brute_force = FALSE;
1004 if (brute_force == FALSE) break;
1007 /* The weekday associated with +5 or -5 may not exist in a given
1008 * month thus if only these weekdays are specified in a rule it is
1009 * necessary to calculate the correct month by brute force versus
1010 * using a mathematical calculation.
1013 *base_tm = *start_tm;
1015 cur_tm.tm_isdst = -1;
1017 cur_time = mktime(&cur_tm);
1018 target_tm->tm_isdst = -1;
1019 target_time = mktime((struct tm *)target_tm);
1021 /* Count the start_time */
1022 if (cur_time < target_time)
1025 while (cur_time < target_time) {
1026 if (SAME_MONTH(target_tm, &cur_tm)) break;
1028 cur_tm.tm_mon += re->re_interval;
1029 cur_tm.tm_isdst = -1;
1030 cur_time = mktime(&cur_tm);
1031 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
1033 if (OccurenceExists(wdt_list, nwdt_list, cur_time)) {
1035 /* Only update the cur_tm if valid slot there */
1039 if (!InTimeRange(num_intervals, re->re_duration))
1044 num_intervals = GetMonthDiff(start_tm, target_tm)
1046 *base_tm = *start_tm;
1047 base_tm->tm_isdst = -1;
1048 /* Move to the closest interval before the target time */
1049 base_tm->tm_mon += num_intervals * re->re_interval;
1050 base_time = mktime(base_tm);
1051 *base_tm = *_XLocaltime(&base_time, localtime_buf);
1054 return (num_intervals);
1059 const Tick start_time,
1062 struct tm *start_tm;
1064 _Xltimeparams localtime_buf;
1066 start_tm = _XLocaltime(&start_time, localtime_buf);
1068 switch (re->re_type) {
1072 if (!RE_DAILY(re)->dd_ntime) {
1073 RE_DAILY(re)->dd_time = (Time *)calloc(1, sizeof(Time));
1074 RE_DAILY(re)->dd_time[0] = start_tm->tm_hour * 100 +
1076 RE_DAILY(re)->dd_ntime = 1;
1080 if (!RE_WEEKLY(re)->wd_ndaytime) {
1081 RE_WEEKLY(re)->wd_daytime =
1082 (DayTime *)calloc(1, sizeof(DayTime));
1083 RE_WEEKLY(re)->wd_daytime[0].dt_day = start_tm->tm_wday;
1084 RE_WEEKLY(re)->wd_daytime[0].dt_ntime = 1;
1085 RE_WEEKLY(re)->wd_daytime[0].dt_time =
1086 (Time *)calloc(1, sizeof(Time));
1087 RE_WEEKLY(re)->wd_daytime[0].dt_time[0] =
1088 start_tm->tm_hour * 100 +
1090 RE_WEEKLY(re)->wd_ndaytime = 1;
1093 for (i = 0; i < RE_WEEKLY(re)->wd_ndaytime; i++) {
1094 if (!RE_WEEKLY(re)->wd_daytime[i].dt_ntime) {
1095 RE_WEEKLY(re)->wd_daytime[i].dt_ntime =
1097 RE_WEEKLY(re)->wd_daytime[i].dt_time =
1098 (Time *)calloc(1, sizeof(Time));
1099 RE_WEEKLY(re)->wd_daytime[i].dt_time[0]=
1100 start_tm->tm_hour * 100 +
1106 case RT_MONTHLY_POSITION:
1107 if (!RE_MONTHLY(re)->md_nitems) {
1108 RE_MONTHLY(re)->md_weektime =
1109 (WeekDayTime *)calloc(1, sizeof(WeekDayTime));
1110 RE_MONTHLY(re)->md_weektime[0].wdt_nday = 1;
1111 RE_MONTHLY(re)->md_weektime[0].wdt_day =
1112 (WeekDay *)calloc(1, sizeof(WeekDay));
1113 RE_MONTHLY(re)->md_weektime[0].wdt_day[0] =
1115 RE_MONTHLY(re)->md_weektime[0].wdt_nweek = 1;
1116 RE_MONTHLY(re)->md_weektime[0].wdt_week =
1117 (WeekNumber *)calloc(1, sizeof(WeekNumber));
1118 RE_MONTHLY(re)->md_weektime[0].wdt_week[0] =
1119 GetWeekNumber(start_time);
1120 RE_MONTHLY(re)->md_nitems = 1;
1123 case RT_MONTHLY_DAY:
1124 if (!RE_MONTHLY(re)->md_nitems) {
1125 RE_MONTHLY(re)->md_days =
1126 (unsigned int *)calloc(1, sizeof(unsigned int));
1127 RE_MONTHLY(re)->md_days[0] = start_tm->tm_mday;
1128 RE_MONTHLY(re)->md_nitems = 1;
1131 case RT_YEARLY_MONTH:
1132 if (!RE_YEARLY(re)->yd_nitems) {
1133 RE_YEARLY(re)->yd_items =
1134 (unsigned int *)calloc(1, sizeof(unsigned int));
1135 RE_YEARLY(re)->yd_items[0] = start_tm->tm_mon + 1;
1136 RE_YEARLY(re)->yd_nitems = 1;
1140 if (!RE_YEARLY(re)->yd_nitems) {
1141 RE_YEARLY(re)->yd_items =
1142 (unsigned int *)calloc(1, sizeof(unsigned int));
1143 RE_YEARLY(re)->yd_items[0] = start_tm->tm_yday;
1144 RE_YEARLY(re)->yd_nitems = 1;