2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: reclotick.c /main/6 1996/11/21 19:45:29 drk $ */
25 * (c) Copyright 1993, 1994 Hewlett-Packard Company
26 * (c) Copyright 1993, 1994 International Business Machines Corp.
27 * (c) Copyright 1993, 1994 Novell, Inc.
28 * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
31 #define XOS_USE_NO_LOCKING
32 #define X_INCLUDE_TIME_H
36 #include <X11/Xos_r.h>
43 static Tick DoMinute(const Tick, const Tick, const RepeatEvent *,
45 static Tick DoDay(const Tick, const Tick, const RepeatEvent *,
47 static Tick DoWeek(const Tick, const Tick, const RepeatEvent *,
49 static Tick DoMonthDay(const Tick, const Tick, const RepeatEvent *,
51 static Tick DoMonthPos(const Tick, const Tick, const RepeatEvent *,
53 static Tick DoYearByMonth(const Tick, const Tick, const RepeatEvent *,
55 static Tick DoYearByDay(const Tick, const Tick, const RepeatEvent *,
57 static void DoDSTAdjustment(const Tick, struct tm *);
58 static RepeatEventState *InitRepeatEventState(const RepeatEvent *);
59 static Tick DSTAdjustment(const struct tm *, const struct tm *);
60 static int GetMonthDiff(const struct tm *, const struct tm *);
61 static int MonthDayNumIntervals(struct tm *, struct tm *,
62 const RepeatEvent *, const unsigned int *,
64 static int MonthPosNumIntervals(struct tm *, struct tm *,
65 const RepeatEvent *, const WeekDayTime *,
66 const unsigned int, struct tm *);
67 void FillInRepeatEvent(const Tick, RepeatEvent *);
70 * Return the closest time following or equal to the target time given a
75 const Tick _target_time,
76 const Tick start_time,
78 RepeatEventState **res)
82 target_time = _target_time;
84 if (!re) return (Tick)NULL;
86 FillInRepeatEvent(start_time, re);
88 if (!(*res = InitRepeatEventState(re)))
91 if (target_time < start_time)
92 target_time = start_time;
94 switch (re->re_type) {
96 closest_tick = DoMinute(target_time, start_time, re, *res);
99 closest_tick = DoDay(target_time, start_time, re, *res);
102 closest_tick = DoWeek(target_time, start_time, re, *res);
104 case RT_MONTHLY_POSITION:
105 /* Establish the real start time */
106 real_start_time = DoMonthPos(start_time, start_time, re, *res);
107 if (target_time < real_start_time)
108 target_time = real_start_time;
109 if (target_time == real_start_time) {
110 (*res)->res_duration = re->re_duration - 1;
111 closest_tick = real_start_time;
113 closest_tick = DoMonthPos(target_time,
114 real_start_time, re, *res);
117 closest_tick = DoMonthDay(target_time, start_time, re, *res);
119 case RT_YEARLY_MONTH:
120 closest_tick = DoYearByMonth(target_time, start_time, re, *res);
123 closest_tick = DoYearByDay(target_time, start_time, re, *res);
128 * Make sure the closest time is not past the appt's end time.
130 if ((!closest_tick) ||
131 (re->re_end_date && re->re_end_date < closest_tick)) {
138 * If the duration was not set (thus strictly using the end date)
139 * reset the RepeatEventState duration back to not-set. This is
140 * cleaner than having conditionals through out the code checking
141 * to see if the duration needs to be updated.
143 if (re->re_duration == RE_NOTSET)
144 (*res)->res_duration == RE_NOTSET;
154 const Tick target_time,
155 const Tick start_time,
156 const RepeatEvent *re,
157 RepeatEventState *res)
163 delta_seconds = target_time - start_time;
165 /* The number of intervals required to span the time from the
166 * start_time to the target_time given the interval size.
167 * The interval size comes from the rule (e.g. M5 or 5 * 60)
169 num_intervals = delta_seconds / (re->re_interval * 60) + 1;
171 if (num_intervals > re->re_duration) {
172 /* The Minute portion of rule does not allow us to reach
173 * the target_time because of the duration limit.
175 closest_tick = re->re_duration * re->re_interval * 60;
177 } else if (num_intervals < re->re_duration) {
178 /* In this case we reached the target_time without using
179 * up all of the intervals allotted to us by the duration.
181 closest_tick = num_intervals * re->re_interval * 60;
182 /* res->res_duration -= (num_intervals + 1); */
184 closest_tick = num_intervals * re->re_interval * 60;
185 /* res->res_duration -= (num_intervals + 1); */
196 const Tick target_time,
197 const Tick start_time,
198 const RepeatEvent *re,
199 RepeatEventState *res)
204 daysec = 60 * 60 * 24,
209 unsigned int ntime = RE_DAILY(re)->dd_ntime;
212 Time *time_list = RE_DAILY(re)->dd_time;
213 _Xltimeparams localtime_buf;
215 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
216 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
217 dst_adj = DSTAdjustment(&start_tm, &target_tm);
219 /* Normalize time to 00:00 */
222 start_tm.tm_hour = 0;
223 start_tm.tm_isdst = -1;
224 base_time = mktime(&start_tm);
226 delta_seconds = target_time - base_time + dst_adj;
228 /* The number of intervals required to span the time
229 * from the start_time to the target_time given the
230 * interval size. The interval size comes from the
231 * rule (e.g. D5 or 5 * daysec)
233 num_intervals = delta_seconds / (re->re_interval * daysec);
235 /* This brings us to the interval closest to the target
236 * time, although it may not actually be the same day.
238 base_time += num_intervals * (re->re_interval * daysec) - dst_adj;
240 if (!InTimeRange(num_intervals, re->re_duration)) {
241 /* We hit the duration limit. */
246 base_tm = *_XLocaltime(&base_time, localtime_buf);
248 /* If we are not on the same day we need to move
249 * forward one interval and take the earliest time.
250 * XXX: This won't work with composite rules.
252 if (!SAME_DAY(&base_tm, &target_tm)) {
253 /* Add one interval to the base_time. */
254 base_time += 1 * (re->re_interval * daysec);
257 /* By moving ahead one day we might have crossed a dst border.*/
258 DoDSTAdjustment(base_time, &base_tm);
260 if (!InTimeRange(num_intervals, re->re_duration)) {
261 /* We hit the duration limit. */
266 /* Take into account any specific times that are a part
267 * of this daily repeating rule: e.g. D2 0100 1000 1400 #3.
268 * We walk through the times for this appointment looking for
269 * one later than the target time.
271 for (i = 0; i < ntime; i++) {
272 /* Add the hour that is to be tested to the normalized
273 * time and see if it is later than the target time.
275 base_tm.tm_min = time_list[i]%100;
276 base_tm.tm_hour = time_list[i]/100;
277 base_tm.tm_isdst = -1;
278 next_time = mktime(&base_tm);
279 if (next_time >= target_time) {
280 res->res_duration = re->re_duration -
282 RES_DSTATE(res).res_time = i;
287 /* The target time falls after the latest time on
288 * this appt day. We must move forward one interval
289 * and take the earliest time.
290 * XXX: Composite rules issue.
292 base_tm = *_XLocaltime(&base_time, localtime_buf);
293 /* Add one interval to the base_time. */
294 base_time += 1 * (re->re_interval * daysec);
297 if (!InTimeRange(num_intervals, re->re_duration)) {
298 /* We hit the duration limit. */
303 /* By moving ahead one day we might have crossed a dst border.*/
304 DoDSTAdjustment(base_time, &base_tm);
306 /* Add the hour that is to be tested to the normalized
307 * time and see if it is later than the target time.
309 base_tm.tm_min = time_list[0]%100;
310 base_tm.tm_hour = time_list[0]/100;
311 base_tm.tm_isdst = -1;
312 next_time = mktime(&base_tm);
314 res->res_duration = re->re_duration - (num_intervals + 1);
315 RES_DSTATE(res).res_time = 0;
322 * Example: W2 MO WE FR #4
326 const Tick _target_time,
327 const Tick _start_time,
328 const RepeatEvent *re,
329 RepeatEventState *res)
335 daysec = 60 * 60 * 24,
337 unsigned int ntime = RE_WEEKLY(re)->wd_ndaytime;
341 Tick target_time = _target_time,
342 start_time = _start_time,
347 DayTime *day_list = RE_WEEKLY(re)->wd_daytime;
348 RepeatEventState *unused;
349 _Xltimeparams localtime_buf;
351 /* Make sure the start time is on the first real event slot. */
353 if (!(unused = InitRepeatEventState(re)))
355 start_time = DoWeek(NULL, _start_time, re, unused);
357 if (_target_time < start_time)
358 target_time = start_time;
360 target_time = _start_time;
362 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
363 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
364 appt_time = start_tm.tm_hour * 100 + start_tm.tm_min;
365 dst_adj = DSTAdjustment(&start_tm, &target_tm);
367 /* Normalize start time to the beginning of the week. */
368 start_tm.tm_mday -= start_tm.tm_wday;
369 start_tm.tm_sec = start_tm.tm_min = start_tm.tm_hour = 0;
370 start_tm.tm_isdst = -1;
371 begin_time = mktime(&start_tm);
372 start_tm = *_XLocaltime((const time_t *)&begin_time, localtime_buf);
374 delta_seconds = target_time - begin_time + dst_adj;
376 /* The number of intervals required to span the time
377 * from the start_time to the target_time given the
378 * interval size. The interval size comes from the
379 * rule (e.g. W5 or 5 * wksec)
381 num_intervals = delta_seconds / (re->re_interval * wksec);
383 /* This brings us to the interval closest to the target
384 * time, although it may not actually be the right week.
386 base_time = begin_time + num_intervals * (re->re_interval * wksec);
387 base_tm = *_XLocaltime(&base_time, localtime_buf);
388 dst_adj = DSTAdjustment(&start_tm, &base_tm);
390 if (!InTimeRange(num_intervals, re->re_duration)) {
391 /* We hit the duration limit. */
396 base_time -= dst_adj;
397 base_tm = *_XLocaltime(&base_time, localtime_buf);
400 if (same_week(&target_tm, &base_tm)) {
403 /* Take the next event */
404 for (i = 0; i < ntime; i++) {
405 if (day_list[i].dt_day > target_tm.tm_wday) {
406 event_wday = day_list[i].dt_day;
408 } else if (day_list[i].dt_day == target_tm.tm_wday) {
409 /* If they are the same day, the day_list time
410 * must be later than the target time.
412 int day_time = (day_list[i].dt_time) ?
413 day_list[i].dt_time[0]:
415 /* XXX: Must walk the time list too. */
416 if (TIME_OF_DAY(&target_tm) <=
417 HOURTOSEC(day_time)) {
418 event_wday = day_list[i].dt_day;
424 RES_WSTATE(res).res_daytime = i;
425 RES_WSTATE(res).res_time = 0;
427 /* The target date is on the same week, but falls after the
428 * last weekday the event could happen on.
430 if (event_wday == -1) {
431 /* XXX: Lose the goto. */
434 base_tm.tm_mday += GetWDayDiff(base_tm.tm_wday, event_wday);
437 /* We will need to go one more interval */
438 if (!InTimeRange(++num_intervals, re->re_duration)) {
439 /* We hit the duration limit. */
443 /* Since the base_tm has been normalized to the beginning
444 * of the week, we can assume we are on Sunday.
446 base_tm.tm_mday += re->re_interval * 7;
447 /* If the target day is smaller than the base day then we
448 * can take the first day in the next event week.
450 if (base_tm.tm_mday > target_tm.tm_mday) {
451 base_tm.tm_mday += day_list[0].dt_day;
453 RES_WSTATE(res).res_daytime = 0;
454 RES_WSTATE(res).res_time = 0;
457 base_tm.tm_hour = appt_time / 100;
458 base_tm.tm_min = appt_time % 100;
459 base_tm.tm_isdst = -1;
460 res->res_duration = re->re_duration - num_intervals;
461 next_time = mktime(&base_tm);
468 * Example: MD2 1 10 20 30 #10
472 const Tick target_time,
473 const Tick start_time,
474 const RepeatEvent *re,
475 RepeatEventState *res)
480 unsigned int ndays = RE_MONTHLY(re)->md_nitems;
486 unsigned int *day_list = RE_MONTHLY(re)->md_days;
487 _Xltimeparams localtime_buf;
489 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
490 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
491 event_day = day_list[0];
492 num_intervals = MonthDayNumIntervals(&start_tm, &target_tm, re,
495 if (!InTimeRange(num_intervals, re->re_duration)) {
496 /* We hit the duration limit. */
500 if (SAME_MONTH(&base_tm, &target_tm)) {
501 int next_interval = TRUE,
504 for (i = 0; i < ndays; i++) {
507 day = DayOfMonth(day_list[i], base_tm.tm_mon,
510 if (day < target_tm.tm_mday)
512 if (day == target_tm.tm_mday)
513 /* If it is on the same day, the event time
514 * must be later than the target time.
516 if (TIME_OF_DAY(&target_tm)
517 > TIME_OF_DAY(&start_tm))
521 next_interval = FALSE;
524 if (day > target_tm.tm_mday) {
526 next_interval = FALSE;
530 /* We are on the right month and we found a time after the
533 if (!next_interval) {
534 base_tm.tm_mday = event_day;
535 base_tm.tm_isdst = -1;
536 next_time = mktime(&base_tm);
537 /* If the day exists (e.g. 31st in July) we're done */
538 if (DayExists(event_day, base_tm.tm_mon,
540 /* Update repeat state info */
541 res->res_duration = re->re_duration
543 RES_MSTATE(res).res_day = i;
544 next_time = mktime(&base_tm);
552 if (!InTimeRange(num_intervals, re->re_duration)) {
553 /* We hit the duration limit. */
558 /* Since we are moving to the next interval, use the first day */
559 event_day = day_list[0];
561 /* Event is in the next interval */
562 base_tm.tm_mon += 1 * re->re_interval;
564 base_tm.tm_isdst = -1;
565 base_time = mktime(&base_tm);
566 base_tm = *_XLocaltime(&base_time, localtime_buf);
568 /* Stop when the day exists in that month */
569 } while (!DayExists(event_day, base_tm.tm_mon, base_tm.tm_year));
571 base_tm.tm_mday = DayOfMonth(event_day, base_tm.tm_mon,
573 base_tm.tm_isdst = -1;
574 next_time = mktime(&base_tm);
576 res->res_duration = re->re_duration - num_intervals;
577 RES_MSTATE(res).res_day = 0;
585 * Example: MP2 1+ MO TU 2- TH #3
589 const Tick target_time,
590 const Tick start_time,
591 const RepeatEvent *re,
592 RepeatEventState *res)
596 unsigned int ndays = RE_MONTHLY(re)->md_nitems;
601 WeekDayTime *wdt_list = RE_MONTHLY(re)->md_weektime;
602 _Xltimeparams localtime_buf;
604 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
605 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
606 num_intervals = MonthPosNumIntervals(&start_tm, &target_tm, re,
607 wdt_list, ndays, &base_tm);
610 if (!InTimeRange(num_intervals, re->re_duration)) {
611 /* We hit the duration limit. */
615 base_tm.tm_isdst = -1;
617 if (SAME_MONTH(&target_tm, &base_tm)) {
618 base_time = mktime(&base_tm);
619 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
620 base_time = WeekNumberToDay(base_time,
621 wdt_list[0].wdt_week[0],
622 wdt_list[0].wdt_day[0]);
623 if (base_time >= target_time)
625 /* target_time came after the slot for this month */
630 base_tm.tm_mon += re->re_interval;
631 base_tm.tm_isdst = -1;
632 /* Move to the first interval after the target time */
633 base_time = mktime(&base_tm);
634 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
635 base_time = WeekNumberToDay(base_time,
636 wdt_list[0].wdt_week[0],
637 wdt_list[0].wdt_day[0]);
638 } while (!base_time);
642 /* Update repeat state info */
643 res->res_duration = re->re_duration - num_intervals;
644 RES_MSTATE(res).res_weektime = 0;
645 RES_MSTATE(res).res_wday = 0;
646 RES_MSTATE(res).res_wtime = 0;
647 RES_MSTATE(res).res_wweek = 0;
654 * Example: YM1 2 5 9 #4
658 const Tick _target_time,
659 const Tick _start_time,
660 const RepeatEvent *re,
661 RepeatEventState *res)
665 unsigned int nitems = RE_YEARLY(re)->yd_nitems;
670 start_time = _start_time,
671 target_time = _target_time;
672 unsigned int *month_list = RE_YEARLY(re)->yd_items;
673 RepeatEventState *unused;
674 _Xltimeparams localtime_buf;
676 /* Make sure the start time is on the first real event slot. */
678 if (!(unused = InitRepeatEventState(re)))
680 start_time = DoYearByMonth(NULL, _start_time, re, unused);
682 if (_target_time < start_time)
683 target_time = start_time;
685 target_time = _start_time;
687 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
688 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
689 nyears = target_tm.tm_year - start_tm.tm_year;
690 num_intervals = nyears / re->re_interval;
692 if (!InTimeRange(num_intervals, re->re_duration)) {
693 /* We hit the duration limit. */
698 base_tm.tm_year += num_intervals * re->re_interval;
699 base_tm.tm_isdst = -1;
701 base_time = mktime(&base_tm);
702 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
704 if (base_tm.tm_year == target_tm.tm_year) {
707 /* Look for a month that is >= the target month */
708 for (i = 0; i < nitems; i++) {
709 /* If the months are equal the target time has to be
710 * less than the next tick.
712 if (month_list[i] - 1 == target_tm.tm_mon) {
713 base_tm.tm_mon = month_list[i] - 1;
714 base_tm.tm_isdst = -1;
715 base_time = mktime(&base_tm);
716 base_tm = *_XLocaltime(&base_time, localtime_buf);
717 if (TIMEOFMONTH(&base_tm) >=
718 TIMEOFMONTH(&target_tm)){
719 res->res_duration = re->re_duration -
721 RES_YSTATE(res).res_daymonth = i;
724 } else if (month_list[i] - 1 >= target_tm.tm_mon) {
725 base_tm.tm_mon = month_list[i] - 1;
726 base_tm.tm_isdst = -1;
727 base_time = mktime(&base_tm);
728 res->res_duration = re->re_duration -
730 RES_YSTATE(res).res_daymonth = i;
737 * The base year is greater than the target year, take the first
740 if (!InTimeRange(++num_intervals, re->re_duration)) {
741 /* We hit the duration limit. */
746 base_tm.tm_year += re->re_interval;
747 base_tm.tm_mon = month_list[0] - 1;
748 base_tm.tm_isdst = -1;
749 base_time = mktime(&base_tm);
751 res->res_duration = re->re_duration - num_intervals;
752 RES_YSTATE(res).res_daymonth = 0;
758 * Example: YD1 100 200 300 #4
762 const Tick _target_time,
763 const Tick _start_time,
764 const RepeatEvent *re,
765 RepeatEventState *res)
769 unsigned int nitems = RE_YEARLY(re)->yd_nitems;
774 target_time = _target_time,
775 start_time = _start_time;
776 unsigned int *day_list = RE_YEARLY(re)->yd_items;
777 RepeatEventState *unused;
778 _Xltimeparams localtime_buf;
780 /* Make sure the start time is on the first real event slot. */
782 if (!(unused = InitRepeatEventState(re)))
784 start_time = DoYearByDay(NULL, _start_time, re, unused);
786 if (_target_time < start_time)
787 target_time = start_time;
789 target_time = _start_time;
791 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
792 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
793 nyears = target_tm.tm_year - start_tm.tm_year;
794 num_intervals = nyears / re->re_interval;
796 if (!InTimeRange(num_intervals, re->re_duration)) {
797 /* We hit the duration limit. */
802 * XXX: day_list == 366 is a special case...that is not supported
807 base_tm.tm_year += num_intervals * re->re_interval;
809 /* If the years are the same then go down the list of days looking
810 * for one later than the target time.
812 if (base_tm.tm_year == target_tm.tm_year) {
814 for (i = 0; i < nitems; i++) {
815 base_tm.tm_mday = day_list[i];
817 base_tm.tm_isdst = -1;
818 base_time = mktime(&base_tm);
820 /* We found the closest tick */
821 if (base_time >= target_time) {
822 res->res_duration = re->re_duration -
824 RES_YSTATE(res).res_daymonth = i;
831 * We either were not on the same year or the above fell through
832 * as we crossed into the next interval.
837 if (!InTimeRange(num_intervals, re->re_duration)) {
838 /* We hit the duration limit. */
843 base_tm.tm_year += 1 * re->re_interval;
844 base_tm.tm_mday = day_list[0];
846 base_tm.tm_isdst = -1;
847 base_time = mktime(&base_tm);
849 res->res_duration = re->re_duration - num_intervals;
850 RES_YSTATE(res).res_daymonth = 0;
856 /* Calculate the number of months between two dates */
857 /* 3/20/90 - 1/2/94 = 46 months */
860 const struct tm *start_tm,
861 const struct tm *end_tm)
863 return ((end_tm->tm_year - start_tm->tm_year + 1) * 12 -
864 (start_tm->tm_mon + 1) - (12 - (end_tm->tm_mon + 1)));
869 const struct tm *tm1,
870 const struct tm *tm2)
872 if (tm1->tm_isdst == -1 || tm2->tm_isdst == -1)
875 if (tm1->tm_isdst != tm2->tm_isdst) {
876 if (tm1->tm_isdst) /* From day light savings to standard */
878 else /* From standard to day light savings */
886 const Tick begin_time,
887 struct tm *end_time) /* Return */
891 _begin_time = begin_time;
892 _Xltimeparams localtime_buf;
894 /* By moving ahead one day we might have crossed a dst border.*/
895 next_day = *_XLocaltime(&begin_time, localtime_buf);
896 dst_adj = DSTAdjustment(end_time, &next_day);
898 _begin_time -= dst_adj;
899 *end_time = *_XLocaltime(&_begin_time, localtime_buf);
901 *end_time = next_day;
905 * Initialize the RepeatEventState struct.
907 static RepeatEventState *
908 InitRepeatEventState(
909 const RepeatEvent *re)
911 RepeatEventState *res;
913 if (!(res = (RepeatEventState *)calloc(1, sizeof(RepeatEventState))))
914 return (RepeatEventState *)NULL;
917 res->res_duration = re->re_duration;
924 * Determine the number of intervals between the start_tm and the target_tm,
925 * the base_tm which is returned is the last event generated before the
929 MonthDayNumIntervals(
931 struct tm *target_tm,
932 const RepeatEvent *re,
933 const unsigned int *md_days,
934 struct tm *base_tm) /* Return */
936 int num_intervals = 0;
942 _Xltimeparams localtime_buf;
944 /* The 28th - 31st may not exist in a given month thus if only these
945 * days are specified in a rule it is necessary to calculate the
946 * correct month by brute force versus using a mathematical calculation.
948 if (md_days[0] > 28) {
949 *base_tm = *start_tm;
952 cur_tm.tm_isdst = -1;
953 cur_time = mktime(&cur_tm);
954 target_tm->tm_isdst = -1;
955 target_time = mktime((struct tm*)target_tm);
956 last_time = cur_time;
958 while (cur_time < target_time) {
959 cur_tm.tm_mon += re->re_interval;
960 cur_tm.tm_isdst = -1;
961 cur_time = mktime(&cur_tm);
962 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
964 if (DayExists(md_days[0], cur_tm.tm_mon,
966 if (cur_time >= target_time) {
967 cur_time = last_time;
968 cur_tm = *_XLocaltime((const time_t *)
969 &last_time, localtime_buf);
972 /* Remember the last time in case we need to
973 * back up one interval.
975 last_time = cur_time;
980 if (!InTimeRange(num_intervals, re->re_duration))
983 if (SAME_MONTH(target_tm, &cur_tm)) break;
986 num_intervals = GetMonthDiff(start_tm, target_tm)
988 *base_tm = *start_tm;
989 base_tm->tm_isdst = -1;
990 /* Move to the closest interval before the target time */
991 base_tm->tm_mon += num_intervals * re->re_interval;
992 base_time = mktime(base_tm);
993 *base_tm = *_XLocaltime(&base_time, localtime_buf);
996 return (num_intervals);
1000 * Count the number of intervals up to, but before the target time. The
1001 * base time returned in the last valid interval before the target time.
1004 MonthPosNumIntervals(
1005 struct tm *start_tm,
1006 struct tm *target_tm,
1007 const RepeatEvent *re,
1008 const WeekDayTime *wdt_list,
1009 const unsigned int nwdt_list,
1010 struct tm *base_tm) /* Return */
1012 int num_intervals = 0,
1019 _Xltimeparams localtime_buf;
1021 for (i = 0; i < nwdt_list; i++) {
1022 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
1023 if ((wdt_list[i].wdt_week[j] != WK_F5) &&
1024 (wdt_list[i].wdt_week[j] != WK_L5)) {
1025 brute_force = FALSE;
1029 if (brute_force == FALSE) break;
1032 /* The weekday associated with +5 or -5 may not exist in a given
1033 * month thus if only these weekdays are specified in a rule it is
1034 * necessary to calculate the correct month by brute force versus
1035 * using a mathematical calculation.
1038 *base_tm = *start_tm;
1040 cur_tm.tm_isdst = -1;
1042 cur_time = mktime(&cur_tm);
1043 target_tm->tm_isdst = -1;
1044 target_time = mktime((struct tm *)target_tm);
1046 /* Count the start_time */
1047 if (cur_time < target_time)
1050 while (cur_time < target_time) {
1051 if (SAME_MONTH(target_tm, &cur_tm)) break;
1053 cur_tm.tm_mon += re->re_interval;
1054 cur_tm.tm_isdst = -1;
1055 cur_time = mktime(&cur_tm);
1056 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
1058 if (OccurenceExists(wdt_list, nwdt_list, cur_time)) {
1060 /* Only update the cur_tm if valid slot there */
1064 if (!InTimeRange(num_intervals, re->re_duration))
1069 num_intervals = GetMonthDiff(start_tm, target_tm)
1071 *base_tm = *start_tm;
1072 base_tm->tm_isdst = -1;
1073 /* Move to the closest interval before the target time */
1074 base_tm->tm_mon += num_intervals * re->re_interval;
1075 base_time = mktime(base_tm);
1076 *base_tm = *_XLocaltime(&base_time, localtime_buf);
1079 return (num_intervals);
1084 const Tick start_time,
1087 struct tm *start_tm;
1089 _Xltimeparams localtime_buf;
1091 start_tm = _XLocaltime(&start_time, localtime_buf);
1093 switch (re->re_type) {
1097 if (!RE_DAILY(re)->dd_ntime) {
1098 RE_DAILY(re)->dd_time = (Time *)calloc(1, sizeof(Time));
1099 RE_DAILY(re)->dd_time[0] = start_tm->tm_hour * 100 +
1101 RE_DAILY(re)->dd_ntime = 1;
1105 if (!RE_WEEKLY(re)->wd_ndaytime) {
1106 RE_WEEKLY(re)->wd_daytime =
1107 (DayTime *)calloc(1, sizeof(DayTime));
1108 RE_WEEKLY(re)->wd_daytime[0].dt_day = start_tm->tm_wday;
1109 RE_WEEKLY(re)->wd_daytime[0].dt_ntime = 1;
1110 RE_WEEKLY(re)->wd_daytime[0].dt_time =
1111 (Time *)calloc(1, sizeof(Time));
1112 RE_WEEKLY(re)->wd_daytime[0].dt_time[0] =
1113 start_tm->tm_hour * 100 +
1115 RE_WEEKLY(re)->wd_ndaytime = 1;
1118 for (i = 0; i < RE_WEEKLY(re)->wd_ndaytime; i++) {
1119 if (!RE_WEEKLY(re)->wd_daytime[i].dt_ntime) {
1120 RE_WEEKLY(re)->wd_daytime[i].dt_ntime =
1122 RE_WEEKLY(re)->wd_daytime[i].dt_time =
1123 (Time *)calloc(1, sizeof(Time));
1124 RE_WEEKLY(re)->wd_daytime[i].dt_time[0]=
1125 start_tm->tm_hour * 100 +
1131 case RT_MONTHLY_POSITION:
1132 if (!RE_MONTHLY(re)->md_nitems) {
1133 RE_MONTHLY(re)->md_weektime =
1134 (WeekDayTime *)calloc(1, sizeof(WeekDayTime));
1135 RE_MONTHLY(re)->md_weektime[0].wdt_nday = 1;
1136 RE_MONTHLY(re)->md_weektime[0].wdt_day =
1137 (WeekDay *)calloc(1, sizeof(WeekDay));
1138 RE_MONTHLY(re)->md_weektime[0].wdt_day[0] =
1140 RE_MONTHLY(re)->md_weektime[0].wdt_nweek = 1;
1141 RE_MONTHLY(re)->md_weektime[0].wdt_week =
1142 (WeekNumber *)calloc(1, sizeof(WeekNumber));
1143 RE_MONTHLY(re)->md_weektime[0].wdt_week[0] =
1144 GetWeekNumber(start_time);
1145 RE_MONTHLY(re)->md_nitems = 1;
1148 case RT_MONTHLY_DAY:
1149 if (!RE_MONTHLY(re)->md_nitems) {
1150 RE_MONTHLY(re)->md_days =
1151 (unsigned int *)calloc(1, sizeof(unsigned int));
1152 RE_MONTHLY(re)->md_days[0] = start_tm->tm_mday;
1153 RE_MONTHLY(re)->md_nitems = 1;
1156 case RT_YEARLY_MONTH:
1157 if (!RE_YEARLY(re)->yd_nitems) {
1158 RE_YEARLY(re)->yd_items =
1159 (unsigned int *)calloc(1, sizeof(unsigned int));
1160 RE_YEARLY(re)->yd_items[0] = start_tm->tm_mon + 1;
1161 RE_YEARLY(re)->yd_nitems = 1;
1165 if (!RE_YEARLY(re)->yd_nitems) {
1166 RE_YEARLY(re)->yd_items =
1167 (unsigned int *)calloc(1, sizeof(unsigned int));
1168 RE_YEARLY(re)->yd_items[0] = start_tm->tm_yday;
1169 RE_YEARLY(re)->yd_nitems = 1;