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
33 #include <X11/Xos_r.h>
40 static Tick DoMinute(const Tick, const Tick, const RepeatEvent *,
42 static Tick DoDay(const Tick, const Tick, const RepeatEvent *,
44 static Tick DoWeek(const Tick, const Tick, const RepeatEvent *,
46 static Tick DoMonthDay(const Tick, const Tick, const RepeatEvent *,
48 static Tick DoMonthPos(const Tick, const Tick, const RepeatEvent *,
50 static Tick DoYearByMonth(const Tick, const Tick, const RepeatEvent *,
52 static Tick DoYearByDay(const Tick, const Tick, const RepeatEvent *,
54 static void DoDSTAdjustment(const Tick, struct tm *);
55 static RepeatEventState *InitRepeatEventState(const RepeatEvent *);
56 static Tick DSTAdjustment(const struct tm *, const struct tm *);
57 static int GetMonthDiff(const struct tm *, const struct tm *);
58 static int MonthDayNumIntervals(struct tm *, struct tm *,
59 const RepeatEvent *, const unsigned int *,
61 static int MonthPosNumIntervals(struct tm *, struct tm *,
62 const RepeatEvent *, const WeekDayTime *,
63 const unsigned int, struct tm *);
64 void FillInRepeatEvent(const Tick, RepeatEvent *);
67 * Return the closest time following or equal to the target time given a
72 const Tick _target_time,
73 const Tick start_time,
75 RepeatEventState **res)
79 target_time = _target_time;
81 if (!re) return (Tick)NULL;
83 FillInRepeatEvent(start_time, re);
85 if (!(*res = InitRepeatEventState(re)))
88 if (target_time < start_time)
89 target_time = start_time;
91 switch (re->re_type) {
93 closest_tick = DoMinute(target_time, start_time, re, *res);
96 closest_tick = DoDay(target_time, start_time, re, *res);
99 closest_tick = DoWeek(target_time, start_time, re, *res);
101 case RT_MONTHLY_POSITION:
102 /* Establish the real start time */
103 real_start_time = DoMonthPos(start_time, start_time, re, *res);
104 if (target_time < real_start_time)
105 target_time = real_start_time;
106 if (target_time == real_start_time) {
107 (*res)->res_duration = re->re_duration - 1;
108 closest_tick = real_start_time;
110 closest_tick = DoMonthPos(target_time,
111 real_start_time, re, *res);
114 closest_tick = DoMonthDay(target_time, start_time, re, *res);
116 case RT_YEARLY_MONTH:
117 closest_tick = DoYearByMonth(target_time, start_time, re, *res);
120 closest_tick = DoYearByDay(target_time, start_time, re, *res);
125 * Make sure the closest time is not past the appt's end time.
127 if ((!closest_tick) ||
128 (re->re_end_date && re->re_end_date < closest_tick)) {
135 * If the duration was not set (thus strictly using the end date)
136 * reset the RepeatEventState duration back to not-set. This is
137 * cleaner than having conditionals through out the code checking
138 * to see if the duration needs to be updated.
140 if (re->re_duration == RE_NOTSET)
141 (*res)->res_duration == RE_NOTSET;
151 const Tick target_time,
152 const Tick start_time,
153 const RepeatEvent *re,
154 RepeatEventState *res)
160 delta_seconds = target_time - start_time;
162 /* The number of intervals required to span the time from the
163 * start_time to the target_time given the interval size.
164 * The interval size comes from the rule (e.g. M5 or 5 * 60)
166 num_intervals = delta_seconds / (re->re_interval * 60) + 1;
168 if (num_intervals > re->re_duration) {
169 /* The Minute portion of rule does not allow us to reach
170 * the target_time because of the duration limit.
172 closest_tick = re->re_duration * re->re_interval * 60;
174 } else if (num_intervals < re->re_duration) {
175 /* In this case we reached the target_time without using
176 * up all of the intervals allotted to us by the duration.
178 closest_tick = num_intervals * re->re_interval * 60;
179 /* res->res_duration -= (num_intervals + 1); */
181 closest_tick = num_intervals * re->re_interval * 60;
182 /* res->res_duration -= (num_intervals + 1); */
193 const Tick target_time,
194 const Tick start_time,
195 const RepeatEvent *re,
196 RepeatEventState *res)
201 daysec = 60 * 60 * 24,
206 unsigned int ntime = RE_DAILY(re)->dd_ntime;
209 Time *time_list = RE_DAILY(re)->dd_time;
210 _Xltimeparams localtime_buf;
212 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
213 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
214 dst_adj = DSTAdjustment(&start_tm, &target_tm);
216 /* Normalize time to 00:00 */
219 start_tm.tm_hour = 0;
220 start_tm.tm_isdst = -1;
221 base_time = mktime(&start_tm);
223 delta_seconds = target_time - base_time + dst_adj;
225 /* The number of intervals required to span the time
226 * from the start_time to the target_time given the
227 * interval size. The interval size comes from the
228 * rule (e.g. D5 or 5 * daysec)
230 num_intervals = delta_seconds / (re->re_interval * daysec);
232 /* This brings us to the interval closest to the target
233 * time, although it may not actually be the same day.
235 base_time += num_intervals * (re->re_interval * daysec) - dst_adj;
237 if (!InTimeRange(num_intervals, re->re_duration)) {
238 /* We hit the duration limit. */
243 base_tm = *_XLocaltime(&base_time, localtime_buf);
245 /* If we are not on the same day we need to move
246 * forward one interval and take the earliest time.
247 * XXX: This won't work with composite rules.
249 if (!SAME_DAY(&base_tm, &target_tm)) {
250 /* Add one interval to the base_time. */
251 base_time += 1 * (re->re_interval * daysec);
254 /* By moving ahead one day we might have crossed a dst border.*/
255 DoDSTAdjustment(base_time, &base_tm);
257 if (!InTimeRange(num_intervals, re->re_duration)) {
258 /* We hit the duration limit. */
263 /* Take into account any specific times that are a part
264 * of this daily repeating rule: e.g. D2 0100 1000 1400 #3.
265 * We walk through the times for this appointment looking for
266 * one later than the target time.
268 for (i = 0; i < ntime; i++) {
269 /* Add the hour that is to be tested to the normalized
270 * time and see if it is later than the target time.
272 base_tm.tm_min = time_list[i]%100;
273 base_tm.tm_hour = time_list[i]/100;
274 base_tm.tm_isdst = -1;
275 next_time = mktime(&base_tm);
276 if (next_time >= target_time) {
277 res->res_duration = re->re_duration -
279 RES_DSTATE(res).res_time = i;
284 /* The target time falls after the latest time on
285 * this appt day. We must move forward one interval
286 * and take the earliest time.
287 * XXX: Composite rules issue.
289 base_tm = *_XLocaltime(&base_time, localtime_buf);
290 /* Add one interval to the base_time. */
291 base_time += 1 * (re->re_interval * daysec);
294 if (!InTimeRange(num_intervals, re->re_duration)) {
295 /* We hit the duration limit. */
300 /* By moving ahead one day we might have crossed a dst border.*/
301 DoDSTAdjustment(base_time, &base_tm);
303 /* Add the hour that is to be tested to the normalized
304 * time and see if it is later than the target time.
306 base_tm.tm_min = time_list[0]%100;
307 base_tm.tm_hour = time_list[0]/100;
308 base_tm.tm_isdst = -1;
309 next_time = mktime(&base_tm);
311 res->res_duration = re->re_duration - (num_intervals + 1);
312 RES_DSTATE(res).res_time = 0;
319 * Example: W2 MO WE FR #4
323 const Tick _target_time,
324 const Tick _start_time,
325 const RepeatEvent *re,
326 RepeatEventState *res)
332 daysec = 60 * 60 * 24,
334 unsigned int ntime = RE_WEEKLY(re)->wd_ndaytime;
338 Tick target_time = _target_time,
339 start_time = _start_time,
344 DayTime *day_list = RE_WEEKLY(re)->wd_daytime;
345 RepeatEventState *unused;
346 _Xltimeparams localtime_buf;
348 /* Make sure the start time is on the first real event slot. */
350 if (!(unused = InitRepeatEventState(re)))
352 start_time = DoWeek(NULL, _start_time, re, unused);
354 if (_target_time < start_time)
355 target_time = start_time;
357 target_time = _start_time;
359 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
360 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
361 appt_time = start_tm.tm_hour * 100 + start_tm.tm_min;
362 dst_adj = DSTAdjustment(&start_tm, &target_tm);
364 /* Normalize start time to the beginning of the week. */
365 start_tm.tm_mday -= start_tm.tm_wday;
366 start_tm.tm_sec = start_tm.tm_min = start_tm.tm_hour = 0;
367 start_tm.tm_isdst = -1;
368 begin_time = mktime(&start_tm);
369 start_tm = *_XLocaltime((const time_t *)&begin_time, localtime_buf);
371 delta_seconds = target_time - begin_time + dst_adj;
373 /* The number of intervals required to span the time
374 * from the start_time to the target_time given the
375 * interval size. The interval size comes from the
376 * rule (e.g. W5 or 5 * wksec)
378 num_intervals = delta_seconds / (re->re_interval * wksec);
380 /* This brings us to the interval closest to the target
381 * time, although it may not actually be the right week.
383 base_time = begin_time + num_intervals * (re->re_interval * wksec);
384 base_tm = *_XLocaltime(&base_time, localtime_buf);
385 dst_adj = DSTAdjustment(&start_tm, &base_tm);
387 if (!InTimeRange(num_intervals, re->re_duration)) {
388 /* We hit the duration limit. */
393 base_time -= dst_adj;
394 base_tm = *_XLocaltime(&base_time, localtime_buf);
397 if (same_week(&target_tm, &base_tm)) {
400 /* Take the next event */
401 for (i = 0; i < ntime; i++) {
402 if (day_list[i].dt_day > target_tm.tm_wday) {
403 event_wday = day_list[i].dt_day;
405 } else if (day_list[i].dt_day == target_tm.tm_wday) {
406 /* If they are the same day, the day_list time
407 * must be later than the target time.
409 int day_time = (day_list[i].dt_time) ?
410 day_list[i].dt_time[0]:
412 /* XXX: Must walk the time list too. */
413 if (TIME_OF_DAY(&target_tm) <=
414 HOURTOSEC(day_time)) {
415 event_wday = day_list[i].dt_day;
421 RES_WSTATE(res).res_daytime = i;
422 RES_WSTATE(res).res_time = 0;
424 /* The target date is on the same week, but falls after the
425 * last weekday the event could happen on.
427 if (event_wday == -1) {
428 /* XXX: Lose the goto. */
431 base_tm.tm_mday += GetWDayDiff(base_tm.tm_wday, event_wday);
434 /* We will need to go one more interval */
435 if (!InTimeRange(++num_intervals, re->re_duration)) {
436 /* We hit the duration limit. */
440 /* Since the base_tm has been normalized to the beginning
441 * of the week, we can assume we are on Sunday.
443 base_tm.tm_mday += re->re_interval * 7;
444 /* If the target day is smaller than the base day then we
445 * can take the first day in the next event week.
447 if (base_tm.tm_mday > target_tm.tm_mday) {
448 base_tm.tm_mday += day_list[0].dt_day;
450 RES_WSTATE(res).res_daytime = 0;
451 RES_WSTATE(res).res_time = 0;
454 base_tm.tm_hour = appt_time / 100;
455 base_tm.tm_min = appt_time % 100;
456 base_tm.tm_isdst = -1;
457 res->res_duration = re->re_duration - num_intervals;
458 next_time = mktime(&base_tm);
465 * Example: MD2 1 10 20 30 #10
469 const Tick target_time,
470 const Tick start_time,
471 const RepeatEvent *re,
472 RepeatEventState *res)
477 unsigned int ndays = RE_MONTHLY(re)->md_nitems;
483 unsigned int *day_list = RE_MONTHLY(re)->md_days;
484 _Xltimeparams localtime_buf;
486 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
487 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
488 event_day = day_list[0];
489 num_intervals = MonthDayNumIntervals(&start_tm, &target_tm, re,
492 if (!InTimeRange(num_intervals, re->re_duration)) {
493 /* We hit the duration limit. */
497 if (SAME_MONTH(&base_tm, &target_tm)) {
498 int next_interval = TRUE,
501 for (i = 0; i < ndays; i++) {
504 day = DayOfMonth(day_list[i], base_tm.tm_mon,
507 if (day < target_tm.tm_mday)
509 if (day == target_tm.tm_mday)
510 /* If it is on the same day, the event time
511 * must be later than the target time.
513 if (TIME_OF_DAY(&target_tm)
514 > TIME_OF_DAY(&start_tm))
518 next_interval = FALSE;
521 if (day > target_tm.tm_mday) {
523 next_interval = FALSE;
527 /* We are on the right month and we found a time after the
530 if (!next_interval) {
531 base_tm.tm_mday = event_day;
532 base_tm.tm_isdst = -1;
533 next_time = mktime(&base_tm);
534 /* If the day exists (e.g. 31st in July) we're done */
535 if (DayExists(event_day, base_tm.tm_mon,
537 /* Update repeat state info */
538 res->res_duration = re->re_duration
540 RES_MSTATE(res).res_day = i;
541 next_time = mktime(&base_tm);
549 if (!InTimeRange(num_intervals, re->re_duration)) {
550 /* We hit the duration limit. */
555 /* Since we are moving to the next interval, use the first day */
556 event_day = day_list[0];
558 /* Event is in the next interval */
559 base_tm.tm_mon += 1 * re->re_interval;
561 base_tm.tm_isdst = -1;
562 base_time = mktime(&base_tm);
563 base_tm = *_XLocaltime(&base_time, localtime_buf);
565 /* Stop when the day exists in that month */
566 } while (!DayExists(event_day, base_tm.tm_mon, base_tm.tm_year));
568 base_tm.tm_mday = DayOfMonth(event_day, base_tm.tm_mon,
570 base_tm.tm_isdst = -1;
571 next_time = mktime(&base_tm);
573 res->res_duration = re->re_duration - num_intervals;
574 RES_MSTATE(res).res_day = 0;
582 * Example: MP2 1+ MO TU 2- TH #3
586 const Tick target_time,
587 const Tick start_time,
588 const RepeatEvent *re,
589 RepeatEventState *res)
593 unsigned int ndays = RE_MONTHLY(re)->md_nitems;
598 WeekDayTime *wdt_list = RE_MONTHLY(re)->md_weektime;
599 _Xltimeparams localtime_buf;
601 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
602 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
603 num_intervals = MonthPosNumIntervals(&start_tm, &target_tm, re,
604 wdt_list, ndays, &base_tm);
607 if (!InTimeRange(num_intervals, re->re_duration)) {
608 /* We hit the duration limit. */
612 base_tm.tm_isdst = -1;
614 if (SAME_MONTH(&target_tm, &base_tm)) {
615 base_time = mktime(&base_tm);
616 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
617 base_time = WeekNumberToDay(base_time,
618 wdt_list[0].wdt_week[0],
619 wdt_list[0].wdt_day[0]);
620 if (base_time >= target_time)
622 /* target_time came after the slot for this month */
627 base_tm.tm_mon += re->re_interval;
628 base_tm.tm_isdst = -1;
629 /* Move to the first interval after the target time */
630 base_time = mktime(&base_tm);
631 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
632 base_time = WeekNumberToDay(base_time,
633 wdt_list[0].wdt_week[0],
634 wdt_list[0].wdt_day[0]);
635 } while (!base_time);
639 /* Update repeat state info */
640 res->res_duration = re->re_duration - num_intervals;
641 RES_MSTATE(res).res_weektime = 0;
642 RES_MSTATE(res).res_wday = 0;
643 RES_MSTATE(res).res_wtime = 0;
644 RES_MSTATE(res).res_wweek = 0;
651 * Example: YM1 2 5 9 #4
655 const Tick _target_time,
656 const Tick _start_time,
657 const RepeatEvent *re,
658 RepeatEventState *res)
662 unsigned int nitems = RE_YEARLY(re)->yd_nitems;
667 start_time = _start_time,
668 target_time = _target_time;
669 unsigned int *month_list = RE_YEARLY(re)->yd_items;
670 RepeatEventState *unused;
671 _Xltimeparams localtime_buf;
673 /* Make sure the start time is on the first real event slot. */
675 if (!(unused = InitRepeatEventState(re)))
677 start_time = DoYearByMonth(NULL, _start_time, re, unused);
679 if (_target_time < start_time)
680 target_time = start_time;
682 target_time = _start_time;
684 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
685 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
686 nyears = target_tm.tm_year - start_tm.tm_year;
687 num_intervals = nyears / re->re_interval;
689 if (!InTimeRange(num_intervals, re->re_duration)) {
690 /* We hit the duration limit. */
695 base_tm.tm_year += num_intervals * re->re_interval;
696 base_tm.tm_isdst = -1;
698 base_time = mktime(&base_tm);
699 base_tm = *_XLocaltime((const time_t *)&base_time, localtime_buf);
701 if (base_tm.tm_year == target_tm.tm_year) {
704 /* Look for a month that is >= the target month */
705 for (i = 0; i < nitems; i++) {
706 /* If the months are equal the target time has to be
707 * less than the next tick.
709 if (month_list[i] - 1 == target_tm.tm_mon) {
710 base_tm.tm_mon = month_list[i] - 1;
711 base_tm.tm_isdst = -1;
712 base_time = mktime(&base_tm);
713 base_tm = *_XLocaltime(&base_time, localtime_buf);
714 if (TIMEOFMONTH(&base_tm) >=
715 TIMEOFMONTH(&target_tm)){
716 res->res_duration = re->re_duration -
718 RES_YSTATE(res).res_daymonth = i;
721 } else if (month_list[i] - 1 >= target_tm.tm_mon) {
722 base_tm.tm_mon = month_list[i] - 1;
723 base_tm.tm_isdst = -1;
724 base_time = mktime(&base_tm);
725 res->res_duration = re->re_duration -
727 RES_YSTATE(res).res_daymonth = i;
734 * The base year is greater than the target year, take the first
737 if (!InTimeRange(++num_intervals, re->re_duration)) {
738 /* We hit the duration limit. */
743 base_tm.tm_year += re->re_interval;
744 base_tm.tm_mon = month_list[0] - 1;
745 base_tm.tm_isdst = -1;
746 base_time = mktime(&base_tm);
748 res->res_duration = re->re_duration - num_intervals;
749 RES_YSTATE(res).res_daymonth = 0;
755 * Example: YD1 100 200 300 #4
759 const Tick _target_time,
760 const Tick _start_time,
761 const RepeatEvent *re,
762 RepeatEventState *res)
766 unsigned int nitems = RE_YEARLY(re)->yd_nitems;
771 target_time = _target_time,
772 start_time = _start_time;
773 unsigned int *day_list = RE_YEARLY(re)->yd_items;
774 RepeatEventState *unused;
775 _Xltimeparams localtime_buf;
777 /* Make sure the start time is on the first real event slot. */
779 if (!(unused = InitRepeatEventState(re)))
781 start_time = DoYearByDay(NULL, _start_time, re, unused);
783 if (_target_time < start_time)
784 target_time = start_time;
786 target_time = _start_time;
788 target_tm = *_XLocaltime((const time_t *)&target_time, localtime_buf);
789 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
790 nyears = target_tm.tm_year - start_tm.tm_year;
791 num_intervals = nyears / re->re_interval;
793 if (!InTimeRange(num_intervals, re->re_duration)) {
794 /* We hit the duration limit. */
799 * XXX: day_list == 366 is a special case...that is not supported
804 base_tm.tm_year += num_intervals * re->re_interval;
806 /* If the years are the same then go down the list of days looking
807 * for one later than the target time.
809 if (base_tm.tm_year == target_tm.tm_year) {
811 for (i = 0; i < nitems; i++) {
812 base_tm.tm_mday = day_list[i];
814 base_tm.tm_isdst = -1;
815 base_time = mktime(&base_tm);
817 /* We found the closest tick */
818 if (base_time >= target_time) {
819 res->res_duration = re->re_duration -
821 RES_YSTATE(res).res_daymonth = i;
828 * We either were not on the same year or the above fell through
829 * as we crossed into the next interval.
834 if (!InTimeRange(num_intervals, re->re_duration)) {
835 /* We hit the duration limit. */
840 base_tm.tm_year += 1 * re->re_interval;
841 base_tm.tm_mday = day_list[0];
843 base_tm.tm_isdst = -1;
844 base_time = mktime(&base_tm);
846 res->res_duration = re->re_duration - num_intervals;
847 RES_YSTATE(res).res_daymonth = 0;
853 /* Calculate the number of months between two dates */
854 /* 3/20/90 - 1/2/94 = 46 months */
857 const struct tm *start_tm,
858 const struct tm *end_tm)
860 return ((end_tm->tm_year - start_tm->tm_year + 1) * 12 -
861 (start_tm->tm_mon + 1) - (12 - (end_tm->tm_mon + 1)));
866 const struct tm *tm1,
867 const struct tm *tm2)
869 if (tm1->tm_isdst == -1 || tm2->tm_isdst == -1)
872 if (tm1->tm_isdst != tm2->tm_isdst) {
873 if (tm1->tm_isdst) /* From day light savings to standard */
875 else /* From standard to day light savings */
883 const Tick begin_time,
884 struct tm *end_time) /* Return */
888 _begin_time = begin_time;
889 _Xltimeparams localtime_buf;
891 /* By moving ahead one day we might have crossed a dst border.*/
892 next_day = *_XLocaltime(&begin_time, localtime_buf);
893 dst_adj = DSTAdjustment(end_time, &next_day);
895 _begin_time -= dst_adj;
896 *end_time = *_XLocaltime(&_begin_time, localtime_buf);
898 *end_time = next_day;
902 * Initialize the RepeatEventState struct.
904 static RepeatEventState *
905 InitRepeatEventState(
906 const RepeatEvent *re)
908 RepeatEventState *res;
910 if (!(res = (RepeatEventState *)calloc(1, sizeof(RepeatEventState))))
911 return (RepeatEventState *)NULL;
914 res->res_duration = re->re_duration;
921 * Determine the number of intervals between the start_tm and the target_tm,
922 * the base_tm which is returned is the last event generated before the
926 MonthDayNumIntervals(
928 struct tm *target_tm,
929 const RepeatEvent *re,
930 const unsigned int *md_days,
931 struct tm *base_tm) /* Return */
933 int num_intervals = 0;
939 _Xltimeparams localtime_buf;
941 /* The 28th - 31st may not exist in a given month thus if only these
942 * days are specified in a rule it is necessary to calculate the
943 * correct month by brute force versus using a mathematical calculation.
945 if (md_days[0] > 28) {
946 *base_tm = *start_tm;
949 cur_tm.tm_isdst = -1;
950 cur_time = mktime(&cur_tm);
951 target_tm->tm_isdst = -1;
952 target_time = mktime((struct tm*)target_tm);
953 last_time = cur_time;
955 while (cur_time < target_time) {
956 cur_tm.tm_mon += re->re_interval;
957 cur_tm.tm_isdst = -1;
958 cur_time = mktime(&cur_tm);
959 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
961 if (DayExists(md_days[0], cur_tm.tm_mon,
963 if (cur_time >= target_time) {
964 cur_time = last_time;
965 cur_tm = *_XLocaltime((const time_t *)
966 &last_time, localtime_buf);
969 /* Remember the last time in case we need to
970 * back up one interval.
972 last_time = cur_time;
977 if (!InTimeRange(num_intervals, re->re_duration))
980 if (SAME_MONTH(target_tm, &cur_tm)) break;
983 num_intervals = GetMonthDiff(start_tm, target_tm)
985 *base_tm = *start_tm;
986 base_tm->tm_isdst = -1;
987 /* Move to the closest interval before the target time */
988 base_tm->tm_mon += num_intervals * re->re_interval;
989 base_time = mktime(base_tm);
990 *base_tm = *_XLocaltime(&base_time, localtime_buf);
993 return (num_intervals);
997 * Count the number of intervals up to, but before the target time. The
998 * base time returned in the last valid interval before the target time.
1001 MonthPosNumIntervals(
1002 struct tm *start_tm,
1003 struct tm *target_tm,
1004 const RepeatEvent *re,
1005 const WeekDayTime *wdt_list,
1006 const unsigned int nwdt_list,
1007 struct tm *base_tm) /* Return */
1009 int num_intervals = 0,
1016 _Xltimeparams localtime_buf;
1018 for (i = 0; i < nwdt_list; i++) {
1019 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
1020 if ((wdt_list[i].wdt_week[j] != WK_F5) &&
1021 (wdt_list[i].wdt_week[j] != WK_L5)) {
1022 brute_force = FALSE;
1026 if (brute_force == FALSE) break;
1029 /* The weekday associated with +5 or -5 may not exist in a given
1030 * month thus if only these weekdays are specified in a rule it is
1031 * necessary to calculate the correct month by brute force versus
1032 * using a mathematical calculation.
1035 *base_tm = *start_tm;
1037 cur_tm.tm_isdst = -1;
1039 cur_time = mktime(&cur_tm);
1040 target_tm->tm_isdst = -1;
1041 target_time = mktime((struct tm *)target_tm);
1043 /* Count the start_time */
1044 if (cur_time < target_time)
1047 while (cur_time < target_time) {
1048 if (SAME_MONTH(target_tm, &cur_tm)) break;
1050 cur_tm.tm_mon += re->re_interval;
1051 cur_tm.tm_isdst = -1;
1052 cur_time = mktime(&cur_tm);
1053 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
1055 if (OccurenceExists(wdt_list, nwdt_list, cur_time)) {
1057 /* Only update the cur_tm if valid slot there */
1061 if (!InTimeRange(num_intervals, re->re_duration))
1066 num_intervals = GetMonthDiff(start_tm, target_tm)
1068 *base_tm = *start_tm;
1069 base_tm->tm_isdst = -1;
1070 /* Move to the closest interval before the target time */
1071 base_tm->tm_mon += num_intervals * re->re_interval;
1072 base_time = mktime(base_tm);
1073 *base_tm = *_XLocaltime(&base_time, localtime_buf);
1076 return (num_intervals);
1081 const Tick start_time,
1084 struct tm *start_tm;
1086 _Xltimeparams localtime_buf;
1088 start_tm = _XLocaltime(&start_time, localtime_buf);
1090 switch (re->re_type) {
1094 if (!RE_DAILY(re)->dd_ntime) {
1095 RE_DAILY(re)->dd_time = (Time *)calloc(1, sizeof(Time));
1096 RE_DAILY(re)->dd_time[0] = start_tm->tm_hour * 100 +
1098 RE_DAILY(re)->dd_ntime = 1;
1102 if (!RE_WEEKLY(re)->wd_ndaytime) {
1103 RE_WEEKLY(re)->wd_daytime =
1104 (DayTime *)calloc(1, sizeof(DayTime));
1105 RE_WEEKLY(re)->wd_daytime[0].dt_day = start_tm->tm_wday;
1106 RE_WEEKLY(re)->wd_daytime[0].dt_ntime = 1;
1107 RE_WEEKLY(re)->wd_daytime[0].dt_time =
1108 (Time *)calloc(1, sizeof(Time));
1109 RE_WEEKLY(re)->wd_daytime[0].dt_time[0] =
1110 start_tm->tm_hour * 100 +
1112 RE_WEEKLY(re)->wd_ndaytime = 1;
1115 for (i = 0; i < RE_WEEKLY(re)->wd_ndaytime; i++) {
1116 if (!RE_WEEKLY(re)->wd_daytime[i].dt_ntime) {
1117 RE_WEEKLY(re)->wd_daytime[i].dt_ntime =
1119 RE_WEEKLY(re)->wd_daytime[i].dt_time =
1120 (Time *)calloc(1, sizeof(Time));
1121 RE_WEEKLY(re)->wd_daytime[i].dt_time[0]=
1122 start_tm->tm_hour * 100 +
1128 case RT_MONTHLY_POSITION:
1129 if (!RE_MONTHLY(re)->md_nitems) {
1130 RE_MONTHLY(re)->md_weektime =
1131 (WeekDayTime *)calloc(1, sizeof(WeekDayTime));
1132 RE_MONTHLY(re)->md_weektime[0].wdt_nday = 1;
1133 RE_MONTHLY(re)->md_weektime[0].wdt_day =
1134 (WeekDay *)calloc(1, sizeof(WeekDay));
1135 RE_MONTHLY(re)->md_weektime[0].wdt_day[0] =
1137 RE_MONTHLY(re)->md_weektime[0].wdt_nweek = 1;
1138 RE_MONTHLY(re)->md_weektime[0].wdt_week =
1139 (WeekNumber *)calloc(1, sizeof(WeekNumber));
1140 RE_MONTHLY(re)->md_weektime[0].wdt_week[0] =
1141 GetWeekNumber(start_time);
1142 RE_MONTHLY(re)->md_nitems = 1;
1145 case RT_MONTHLY_DAY:
1146 if (!RE_MONTHLY(re)->md_nitems) {
1147 RE_MONTHLY(re)->md_days =
1148 (unsigned int *)calloc(1, sizeof(unsigned int));
1149 RE_MONTHLY(re)->md_days[0] = start_tm->tm_mday;
1150 RE_MONTHLY(re)->md_nitems = 1;
1153 case RT_YEARLY_MONTH:
1154 if (!RE_YEARLY(re)->yd_nitems) {
1155 RE_YEARLY(re)->yd_items =
1156 (unsigned int *)calloc(1, sizeof(unsigned int));
1157 RE_YEARLY(re)->yd_items[0] = start_tm->tm_mon + 1;
1158 RE_YEARLY(re)->yd_nitems = 1;
1162 if (!RE_YEARLY(re)->yd_nitems) {
1163 RE_YEARLY(re)->yd_items =
1164 (unsigned int *)calloc(1, sizeof(unsigned int));
1165 RE_YEARLY(re)->yd_items[0] = start_tm->tm_yday;
1166 RE_YEARLY(re)->yd_nitems = 1;