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: renexttick.c /main/5 1996/11/21 19:46:22 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.
32 #define XOS_USE_NO_LOCKING
33 #define X_INCLUDE_TIME_H
37 #include <X11/Xos_r.h>
48 static Tick DoMinute(const Tick, const Tick, const RepeatEvent *,
50 static Tick DoDay(const Tick, const Tick, const RepeatEvent *,
52 static Tick DoWeek(const Tick, const Tick, const RepeatEvent *,
54 static Tick DoMonthDay(const Tick, const Tick, const RepeatEvent *,
56 static Tick DoMonthPos(const Tick, const Tick, const RepeatEvent *,
58 static Tick DoYearByMonth(const Tick, const Tick, const RepeatEvent *,
60 static Tick DoYearByDay(const Tick, const Tick, const RepeatEvent *,
63 static Tick NextDayTick(const Tick, const Tick, const RepeatEvent *,
65 static Tick NextWeekTick(const struct tm *, const struct tm *,
66 const RepeatEvent *, const DayTime *,
67 const MoveIndicator, RepeatEventState *);
68 static Tick NextMonthTick(const struct tm *, const RepeatEvent *,
69 const WeekDayTime *, const MoveIndicator,
71 extern void FillInRepeatEvent(const Tick, RepeatEvent *);
76 const Tick start_time,
78 RepeatEventState *res)
82 if (!re) return (Tick)NULL;
84 FillInRepeatEvent(start_time, re);
86 switch (re->re_type) {
88 next_time = DoMinute(cur_time, start_time, re, res);
91 next_time = DoDay(cur_time, start_time, re, res);
94 next_time = DoWeek(cur_time, start_time, re, res);
96 case RT_MONTHLY_POSITION:
97 next_time = DoMonthPos(cur_time, start_time, re, res);
100 next_time = DoMonthDay(cur_time, start_time, re, res);
102 case RT_YEARLY_MONTH:
103 next_time = DoYearByMonth(cur_time, start_time, re, res);
106 next_time = DoYearByDay(cur_time, start_time, re, res);
111 * If the duration was not set (thus strictly using the end date)
112 * reset the RepeatEventState duration back to not-set. This is
113 * cleaner than having conditionals through out the code checking
114 * to see if the duration needs to be updated.
116 if (re->re_duration == RE_NOTSET)
117 res->res_duration = RE_NOTSET;
125 const Tick start_time,
126 const RepeatEvent *re,
127 RepeatEventState *res)
135 const Tick start_time,
136 const RepeatEvent *re,
137 RepeatEventState *res)
140 Duration res_duration = res->res_duration;
141 unsigned int res_time = RES_DSTATE(res).res_time;
143 if (RE_DAILY(re)->dd_ntime) {
144 /* Last event was on the last time for this day. Now must
145 * move to the next day/interval.
147 if (RES_DSTATE(res).res_time == RE_DAILY(re)->dd_ntime - 1) {
148 next_time = NextDayTick(cur_time, start_time, re, res);
149 RES_DSTATE(res).res_time = 0;
152 _Xltimeparams localtime_buf;
154 /* There is a later valid time on this day, use it */
155 tm = _XLocaltime(&cur_time, localtime_buf);
158 dd_time[++RES_DSTATE(res).res_time] / 100;
160 RE_DAILY(re)->dd_time[RES_DSTATE(res).res_time]
163 next_time = mktime(tm);
166 /* No alternate times for this day/move to the next interval. */
167 next_time = NextDayTick(cur_time, start_time, re, res);
170 /* If we went beyond the end date then restore the state info, ret 0 */
171 if (re->re_end_date && re->re_end_date < next_time) {
172 res->res_duration = res_duration;
173 RES_DSTATE(res).res_time = res_time;
183 const Tick start_time,
184 const RepeatEvent *re,
185 RepeatEventState *res)
187 unsigned int res_daytime = RES_WSTATE(res).res_daytime,
188 res_time = RES_WSTATE(res).res_time,
190 re_ndaytime = RE_WEEKLY(re)->wd_ndaytime;
191 Duration res_duration = res->res_duration;
194 DayTime *daytime = RE_WEEKLY(re)->wd_daytime;
196 _Xltimeparams localtime_buf;
198 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
199 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
201 re_ntime = daytime[0].dt_ntime;
204 * We want to stop when the duration == 1 and we have handled all
205 * the days in the rule for that week.
207 if ((res->res_duration == 0) || ((res->res_duration == 1) &&
208 ((re_ndaytime - 1) == RES_WSTATE(res).res_daytime)))
211 /* If these are equal then we are at the end of the time
212 * slots for this day */
213 if ((re_ntime - 1) == res_time) {
214 /* If these are equal then we used up all the
215 * days for this week */
216 if ((re_ndaytime - 1) == res_daytime) {
219 RES_WSTATE(res).res_daytime = 0;
220 RES_WSTATE(res).res_time = 0;
221 next_time = NextWeekTick(&cur_tm, &start_tm, re,
222 daytime, NextInterval, res);
224 /* Move to the next day event (same week), use
227 RES_WSTATE(res).res_time = 0;
228 RES_WSTATE(res).res_daytime++;
229 next_time = NextWeekTick(&cur_tm, &start_tm, re,
230 daytime, SameInterval, res);
233 /* Move to the next time, same day. */
234 RES_WSTATE(res).res_time++;
235 next_time = NextWeekTick(&cur_tm, &start_tm, re, daytime,
239 /* If we went beyond the end date then restore the state info, ret 0 */
240 if (re->re_end_date && re->re_end_date < next_time) {
241 res->res_duration = res_duration;
242 RES_WSTATE(res).res_time = res_time;
243 RES_WSTATE(res).res_daytime = res_daytime;
253 const Tick start_time,
254 const RepeatEvent *re,
255 RepeatEventState *res)
257 unsigned int *md_days = RE_MONTHLY(re)->md_days,
258 md_nitems = RE_MONTHLY(re)->md_nitems,
259 res_day = RES_MSTATE(res).res_day;
260 Duration res_duration = res->res_duration;
264 int next_interval = FALSE;
265 _Xltimeparams localtime_buf;
267 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
268 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
271 * We want to stop when the duration == 1 and we have handled all
272 * the days listed in the rule for that month.
274 if ((res->res_duration == 0) || ((res->res_duration == 1) &&
275 ((md_nitems - 1) == RES_MSTATE(res).res_day)))
278 /* Check each event day in the month, move to the next interval when
279 * we run out of event days for the month. Make sure the event day
280 * exists in that month. e.g. the 31st of June does not exist.
283 if ((md_nitems - 1) == RES_MSTATE(res).res_day) {
284 cur_tm.tm_mon += 1 * re->re_interval;
285 RES_MSTATE(res).res_day = 0;
286 next_interval = TRUE;
288 RES_MSTATE(res).res_day++;
290 cur_tm.tm_isdst = -1;
292 _cur_time = mktime(&cur_tm);
293 cur_tm = *_XLocaltime((const time_t *)&_cur_time, localtime_buf);
294 } while (!DayExists(md_days[RES_MSTATE(res).res_day], cur_tm.tm_mon,
297 cur_tm.tm_mday = DayOfMonth(md_days[RES_MSTATE(res).res_day],
298 cur_tm.tm_mon, cur_tm.tm_year);
299 cur_tm.tm_isdst = -1;
300 _cur_time = mktime(&cur_tm);
302 if (next_interval) res->res_duration--;
304 /* If we went beyond the end date then restore the state info, ret 0 */
305 if (re->re_end_date && re->re_end_date < _cur_time) {
306 res->res_duration = res_duration;
307 RES_MSTATE(res).res_day = res_day;
317 const Tick start_time,
318 const RepeatEvent *re,
319 RepeatEventState *res)
321 WeekDayTime *wdt_list = RE_MONTHLY(re)->md_weektime;
325 Duration res_duration = res->res_duration;
326 unsigned int md_nitems = RE_MONTHLY(re)->md_nitems,
327 wdt_nday = wdt_list[md_nitems-1].wdt_nday,
328 wdt_nweek = wdt_list[md_nitems-1].wdt_nweek,
329 res_weektime = RES_MSTATE(res).res_weektime,
330 res_wday = RES_MSTATE(res).res_wday,
331 res_wtime = RES_MSTATE(res).res_wtime,
332 res_wweek = RES_MSTATE(res).res_wweek;
333 _Xltimeparams localtime_buf;
335 if (res->res_duration == 0)
338 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
339 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
341 /* XXX: This assumes rules of this form only: MP<n> 1+ WE #4 */
342 if ((wdt_list[res_weektime].wdt_nday - 1) == res_wday) {
343 if ((wdt_list[res_weektime].wdt_nweek - 1) == res_wweek) {
344 if ((md_nitems - 1) == res_weektime) {
345 RES_MSTATE(res).res_weektime = 0;
346 RES_MSTATE(res).res_wday = 0;
347 RES_MSTATE(res).res_wtime = 0;
348 RES_MSTATE(res).res_wweek = 0;
349 _cur_time = NextMonthTick(&cur_tm, re, wdt_list,
361 /* If we went beyond the end date then restore the state info, ret 0 */
362 if (re->re_end_date && re->re_end_date < _cur_time) {
363 res->res_duration = res_duration;
364 RES_MSTATE(res).res_weektime = res_weektime;
365 RES_MSTATE(res).res_wday = res_wday;
366 RES_MSTATE(res).res_wtime = res_wtime;
367 RES_MSTATE(res).res_wweek = res_wweek;
377 const Tick start_time,
378 const RepeatEvent *re,
379 RepeatEventState *res)
384 Duration res_duration = res->res_duration;
385 unsigned int *month_list = RE_YEARLY(re)->yd_items,
386 nitems = RE_YEARLY(re)->yd_nitems,
387 res_month = RES_YSTATE(res).res_daymonth;
388 _Xltimeparams localtime_buf;
390 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
391 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
394 * We want to stop when the duration == 1 and we have handled all
395 * the months listed in the rule for that year.
397 if ((res->res_duration == 0) || ((res->res_duration == 1) &&
398 ((nitems - 1) == RES_YSTATE(res).res_daymonth)))
401 cur_tm.tm_mday = start_tm.tm_mday;
402 cur_tm.tm_hour = start_tm.tm_hour;
403 cur_tm.tm_min = start_tm.tm_min;
404 cur_tm.tm_sec = start_tm.tm_sec;
406 /* If these equal then we used up all the months for this year.
407 * We must now move to the next interval.
409 if ((nitems - 1) == res_month) {
410 cur_tm.tm_year += re->re_interval;
411 cur_tm.tm_mon = month_list[0] - 1; /* 0 = January */
412 RES_YSTATE(res).res_daymonth = 0;
415 /* Take the next month in the month_list, same year */
416 cur_tm.tm_mon = month_list[++RES_YSTATE(res).res_daymonth] - 1;
419 cur_tm.tm_isdst = -1;
420 _cur_time = mktime(&cur_tm);
422 /* If we went beyond the end date then restore the state info, ret 0 */
423 if (re->re_end_date && re->re_end_date < _cur_time) {
424 res->res_duration = res_duration;
425 RES_YSTATE(res).res_daymonth = res_month;
435 const Tick start_time,
436 const RepeatEvent *re,
437 RepeatEventState *res)
442 Duration res_duration = res->res_duration;
443 unsigned int *day_list = RE_YEARLY(re)->yd_items,
444 nitems = RE_YEARLY(re)->yd_nitems,
445 res_month = RES_YSTATE(res).res_daymonth;
446 _Xltimeparams localtime_buf;
448 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
449 cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
452 * We want to stop when the duration == 1 and we have handled all
453 * the days listed in the rule for that year.
455 if ((res->res_duration == 0) || ((res->res_duration == 1) &&
456 ((nitems - 1) == RES_YSTATE(res).res_daymonth)))
459 cur_tm.tm_mday = start_tm.tm_mday;
460 cur_tm.tm_hour = start_tm.tm_hour;
461 cur_tm.tm_min = start_tm.tm_min;
462 cur_tm.tm_sec = start_tm.tm_sec;
464 cur_tm.tm_isdst = -1;
466 /* If these equal then we used up all the days for this year.
467 * We must now move to the next interval.
469 if ((nitems - 1) == res_month) {
470 cur_tm.tm_year += re->re_interval;
471 cur_tm.tm_mday = day_list[0];
472 RES_YSTATE(res).res_daymonth = 0;
475 /* Take the next day in the day_list, same year */
476 cur_tm.tm_mday = day_list[++RES_YSTATE(res).res_daymonth];
479 _cur_time = mktime(&cur_tm);
481 /* If we went beyond the end date then restore the state info, ret 0 */
482 if (re->re_end_date && re->re_end_date < _cur_time) {
483 res->res_duration = res_duration;
484 RES_YSTATE(res).res_daymonth = res_month;
494 const Tick start_time,
495 const RepeatEvent *re,
496 RepeatEventState *res)
500 _Xltimeparams localtime_buf;
502 if (res->res_duration == 0) return (Tick)0;
506 start_tm = *_XLocaltime(&start_time, localtime_buf);
507 tm = _XLocaltime(&cur_time, localtime_buf);
509 tm->tm_mday += re->re_interval;
510 tm->tm_hour = start_tm.tm_hour;
511 tm->tm_min = start_tm.tm_min;
519 const struct tm *current_tm,
520 const struct tm *start_tm,
521 const RepeatEvent *re,
522 const DayTime *wd_daytime,
523 const MoveIndicator move,
524 RepeatEventState *res)
526 struct tm *cur_tm = (struct tm *)current_tm;
527 unsigned int res_daytime = RES_WSTATE(res).res_daytime;
528 unsigned int res_time = RES_WSTATE(res).res_time;
530 /* Move forward to the next interval (at least another week) */
531 if (move == (const MoveIndicator) NextInterval) {
532 /* Normalize the date to the beginning of the week. */
533 cur_tm->tm_mday -= cur_tm->tm_wday;
534 cur_tm->tm_sec = cur_tm->tm_min = cur_tm->tm_hour = 0;
535 cur_tm->tm_isdst = -1;
537 /* Add an interval */
538 cur_tm->tm_mday += re->re_interval * 7;
540 /* Put it on the correct day. */
541 cur_tm->tm_mday += wd_daytime[0].dt_day;
542 /* Put it on the correct time. */
543 cur_tm->tm_hour = wd_daytime[0].dt_time[0] / 100;
544 cur_tm->tm_min = wd_daytime[0].dt_time[0] % 100;
548 return (mktime(cur_tm));
553 /* Move the appropriate number of days forward */
554 cur_tm->tm_mday += GetWDayDiff(cur_tm->tm_wday,
555 wd_daytime[res_daytime].dt_day);
557 /* Use the indicated time if available */
558 if (RE_WEEKLY(re)->wd_daytime[res_daytime].dt_time) {
559 cur_tm->tm_hour = wd_daytime[res_daytime].dt_time[res_time]
561 cur_tm->tm_min = wd_daytime[res_daytime].dt_time[res_time]
564 /* Use the time from the first appt */
565 cur_tm->tm_hour = start_tm->tm_hour;
566 cur_tm->tm_min = start_tm->tm_min;
569 cur_tm->tm_isdst = -1;
570 return (mktime(cur_tm));
575 const struct tm *current_time,
576 const RepeatEvent *re,
577 const WeekDayTime *wdt_list,
578 const MoveIndicator move,
579 RepeatEventState *res)
581 struct tm *cur_tm = (struct tm *)current_time;
582 unsigned int res_weektime = RES_MSTATE(res).res_weektime,
583 res_wweek = RES_MSTATE(res).res_wweek,
584 res_wday = RES_MSTATE(res).res_wday;
587 /* Move forward to the next interval (at least another month) */
588 if (move == (const MoveIndicator) NextInterval) {
592 /* Add an interval */
593 cur_tm->tm_mon += re->re_interval;
594 cur_tm->tm_isdst = -1;
596 } while (!(next_time = WeekNumberToDay(mktime(cur_tm),
597 wdt_list[res_weektime].wdt_week[res_wweek],
598 wdt_list[res_weektime].wdt_day[res_wday])));