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: relasttick.c /main/6 1996/11/21 19:46:04 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 RepeatEvent *);
44 static Tick DoDay(const Tick, const RepeatEvent *);
45 static Tick DoWeek(const Tick, RepeatEvent *);
46 static Tick DoMonthDay(const Tick, const RepeatEvent *);
47 static Tick DoMonthPos(const Tick, RepeatEvent *);
48 static Tick DoYearByMonth(const Tick, RepeatEvent *);
49 static Tick DoYearByDay(const Tick, RepeatEvent *);
50 static Tick LastOccurence(const Tick, const WeekDayTime *, const unsigned int);
51 static Tick LastTickFromEndDate(const Tick, const RepeatEvent *);
52 static int LastDayExists(const struct tm *, const unsigned int,
53 const unsigned int *);
54 extern void FillInRepeatEvent(const Tick, RepeatEvent *);
55 extern Tick ClosestTick(const Tick, const Tick, RepeatEvent *,
57 extern Tick PrevTick(const Tick, const Tick, RepeatEvent *, RepeatEventState *);
61 const Tick start_time,
66 if (!re) return (Tick)NULL;
68 if (re->re_duration == RE_INFINITY) return EOT;
70 FillInRepeatEvent(start_time, re);
72 switch (re->re_type) {
74 last_time = DoMinute(start_time, re);
77 last_time = DoDay(start_time, re);
80 last_time = DoWeek(start_time, re);
82 case RT_MONTHLY_POSITION:
83 last_time = DoMonthPos(start_time, re);
86 last_time = DoMonthDay(start_time, re);
89 last_time = DoYearByMonth(start_time, re);
92 last_time = DoYearByDay(start_time, re);
101 const Tick start_time,
102 const RepeatEvent *re)
109 const Tick start_time,
110 const RepeatEvent *re)
112 Tick last_time1 = EOT,
115 if (re->re_end_date) {
116 last_time1 = LastTickFromEndDate(start_time, re);
119 if (re->re_duration != RE_NOTSET) {
121 _Xltimeparams localtime_buf;
123 start_tm = _XLocaltime((const time_t *)&start_time, localtime_buf);
125 /* Go to the last day an event can happen on. */
126 start_tm->tm_mday += (re->re_duration - 1) * re->re_interval;
127 start_tm->tm_isdst = -1;
129 /* Go to the last time an event can happen on the last day. */
130 if (RE_DAILY(re)->dd_ntime) {
132 RE_DAILY(re)->dd_time[RE_DAILY(re)->dd_ntime - 1] / 100;
134 RE_DAILY(re)->dd_time[RE_DAILY(re)->dd_ntime - 1] % 100;
136 last_time2 = mktime(start_tm);
139 return ((last_time1 < last_time2) ? last_time1 : last_time2);
144 const Tick start_time,
148 unsigned int wd_ndaytime = RE_WEEKLY(re)->wd_ndaytime,
152 Tick _start_time = start_time,
155 RepeatEventState *res;
156 _Xltimeparams localtime_buf;
158 if (re->re_end_date) {
159 last_time1 = LastTickFromEndDate(start_time, re);
161 if (re->re_duration == RE_NOTSET)
165 /* Find the real start time */
166 _start_time = ClosestTick(start_time, start_time, re, &res);
168 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
170 start_hour = start_tm->tm_hour;
171 start_min = start_tm->tm_min;
173 /* Go to the last day an event can happen on. */
174 start_tm->tm_mday += (re->re_duration - 1) * re->re_interval * 7;
175 start_tm->tm_isdst = -1;
178 _start_time = mktime(start_tm);
179 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
181 /* Normalize to the beginning of the week */
182 start_tm->tm_mday -= start_tm->tm_wday;
183 start_tm->tm_sec = start_tm->tm_min = start_tm->tm_hour = 0;
184 start_tm->tm_isdst = -1;
185 _start_time = mktime(start_tm);
186 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
188 /* Move forward to the proper week day */
190 RE_WEEKLY(re)->wd_daytime[wd_ndaytime - 1].dt_day;
192 dt_ntime = RE_WEEKLY(re)->wd_daytime[wd_ndaytime - 1].dt_ntime;
194 /* Set the proper time */
196 start_tm->tm_hour = RE_WEEKLY(re)->
197 wd_daytime[wd_ndaytime - 1].dt_time[dt_ntime - 1]
199 start_tm->tm_min = RE_WEEKLY(re)->
200 wd_daytime[wd_ndaytime - 1].dt_time[dt_ntime - 1]
203 start_tm->tm_hour = start_hour;
204 start_tm->tm_min = start_min;
208 start_tm->tm_isdst = -1;
209 last_time2 = mktime(start_tm);
211 return ((last_time1 < last_time2) ? last_time1 : last_time2);
216 const Tick start_time,
217 const RepeatEvent *re)
221 unsigned int md_nitems = RE_MONTHLY(re)->md_nitems,
222 *md_days = RE_MONTHLY(re)->md_days,
227 _Xltimeparams localtime_buf;
229 if (re->re_end_date) {
230 last_time1 = LastTickFromEndDate(start_time, re);
232 if (re->re_duration == RE_NOTSET)
236 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
237 cur_tm = _XLocaltime((const time_t *)&start_time, localtime_buf);
238 start_tm.tm_isdst = -1;
240 /* The 28th - 31st may not exist in a given month thus if only these
241 * days are specified in a rule it is necessary to calculate the
242 * correct month by brute force versus as a mathimatical calculation.
244 if (md_days[0] > 28) {
247 /* Compute last event by brute force */
249 cur_tm->tm_mon += re->re_interval;
250 cur_tm->tm_isdst = -1;
251 cur_time = mktime(cur_tm);
252 cur_tm = _XLocaltime((const time_t *)&cur_time, localtime_buf);
255 DayOfMonth(md_days[0], cur_tm->tm_mon,
257 cur_tm->tm_mon, cur_tm->tm_year))
259 } while (interval < re->re_duration);
261 start_tm.tm_mon = cur_tm->tm_mon;
262 start_tm.tm_year = cur_tm->tm_year;
263 start_tm.tm_mday = LastDayExists(cur_tm, md_nitems, md_days);
265 } else if (md_nitems) {
266 start_tm.tm_mon += (re->re_duration - 1) * re->re_interval;
267 start_tm.tm_mday = 1;
268 /* Have the year and month updated */
269 cur_time = mktime(&start_tm);
270 start_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
271 /* Get the right day (LASTDAY converted to a real day) */
272 start_tm.tm_isdst = -1;
273 start_tm.tm_mday = DayOfMonth(
274 md_days[md_nitems - 1],
275 start_tm.tm_mon, start_tm.tm_year);
277 start_tm.tm_mon += (re->re_duration - 1) * re->re_interval;
279 last_time2 = mktime(&start_tm);
280 return ((last_time1 < last_time2) ? last_time1 : last_time2);
285 const Tick _start_time,
290 Tick last_time1 = EOT,
292 start_time = _start_time;
293 unsigned int nwdt_list = RE_MONTHLY(re)->md_nitems,
297 WeekDayTime *wdt_list = RE_MONTHLY(re)->md_weektime;
298 RepeatEventState *res;
299 _Xltimeparams localtime_buf;
301 if (re->re_end_date) {
302 last_time1 = LastTickFromEndDate(start_time, re);
304 if (re->re_duration == RE_NOTSET)
308 /* Find the real start time */
309 start_time = ClosestTick(start_time, start_time, re, &res);
311 start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
313 for (i = 0; i < nwdt_list; i++) {
314 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
315 if ((wdt_list[i].wdt_week[j] != WK_F5) &&
316 (wdt_list[i].wdt_week[j] != WK_L5)) {
321 if (brute_force == FALSE) break;
328 while (num_intervals < re->re_duration) {
329 cur_tm.tm_mon += re->re_interval;
330 cur_tm.tm_isdst = -1;
331 last_time2 = mktime(&cur_tm);
332 cur_tm = *_XLocaltime((const time_t *)&last_time2,
335 if (OccurenceExists(wdt_list, nwdt_list, last_time2))
338 if (!InTimeRange(num_intervals, re->re_duration))
342 start_tm.tm_mon += (re->re_duration - 1) * re->re_interval;
343 start_tm.tm_isdst = -1;
344 start_tm.tm_mday = 1;
345 last_time2 = mktime(&start_tm);
349 * Given the occurence information, find the last valid day in the
352 last_time2 = LastOccurence(last_time2, wdt_list, nwdt_list);
354 return ((last_time1 < last_time2) ? last_time1 : last_time2);
359 const Tick start_time,
367 RepeatEventState *res;
368 _Xltimeparams localtime_buf;
370 if (re->re_end_date) {
371 last_time1 = LastTickFromEndDate(start_time, re);
373 if (re->re_duration == RE_NOTSET)
377 /* Find the real start time */
378 _start_time = ClosestTick(start_time, start_time, re, &res);
380 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
381 start_day = start_tm->tm_mday;
383 /* Go to the last day an event can happen on. */
384 start_tm->tm_year += (re->re_duration - 1) * re->re_interval;
385 start_tm->tm_isdst = -1;
387 /* XXX: If the only month used is Feb and the date is the 29th, then
388 * we must use a special case.
391 /* Go to the last time an event can happen on on the last month. */
392 if (RE_YEARLY(re)->yd_nitems) {
395 _start_time = mktime(start_tm);
396 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
398 for (i = RE_YEARLY(re)->yd_nitems - 1; i >= 0; i++) {
399 if (DayExists(start_day, RE_YEARLY(re)->yd_items[i],
400 start_tm->tm_year)) {
401 start_tm->tm_mon = RE_YEARLY(re)->yd_items[i]-1;
402 start_tm->tm_isdst = -1;
403 return (mktime(start_tm));
406 /* No months have a day that can be used */
410 last_time2 = mktime(start_tm);
411 return ((last_time1 < last_time2) ? last_time1 : last_time2);
416 const Tick start_time,
424 RepeatEventState *res;
425 _Xltimeparams localtime_buf;
427 if (re->re_end_date) {
428 last_time1 = LastTickFromEndDate(start_time, re);
430 if (re->re_duration == RE_NOTSET)
434 /* Find the real start time */
435 _start_time = ClosestTick(start_time, start_time, re, &res);
437 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
439 /* Go to the last year an event can happen on. */
440 start_tm->tm_year += (re->re_duration - 1) * re->re_interval;
441 start_tm->tm_isdst = -1;
443 /* Go to the last time an event can happen on. */
444 if (RE_YEARLY(re)->yd_nitems) {
445 start_tm->tm_mon = 0;
447 RE_YEARLY(re)->yd_items[RE_YEARLY(re)->yd_nitems - 1];
450 last_time2 = mktime(start_tm);
451 return ((last_time1 < last_time2) ? last_time1 : last_time2);
455 * Given a month/year (from cur_tm), and a list of days of the month
456 * determine the last day in that list that is valid in that month.
460 const struct tm *cur_tm,
461 const unsigned int md_nitems,
462 const unsigned int *md_days)
467 for (i = md_nitems - 1; i >= 0; i--) {
468 day = DayOfMonth(md_days[i], cur_tm->tm_mon, cur_tm->tm_year);
469 if (DayExists(day, cur_tm->tm_mon, cur_tm->tm_year))
477 * Given a month/year (in cur_time) determine the last occurence of week/day/
483 const WeekDayTime *wdt_list,
484 const unsigned int nwdt_list)
487 Tick oldest_time = 0,
490 for (i = 0; i < nwdt_list; i++) {
491 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
492 for (k = 0; k < wdt_list[i].wdt_nday; k++) {
493 if (current_time = WeekNumberToDay(cur_time,
494 wdt_list[i].wdt_week[j],
495 wdt_list[i].wdt_day[k])) {
496 if (current_time > oldest_time)
497 oldest_time = current_time;
508 * Given a time and a rule find the last tick before the end date.
513 const RepeatEvent *re)
515 RepeatEventState *res;
516 RepeatEvent *_re = (RepeatEvent *)re;
517 Tick end_date = re->re_end_date,
519 Duration duration = re->re_duration;
521 /* Take the end date out of the equation. */
522 _re->re_end_date = 0;
523 _re->re_duration = RE_INFINITY;
525 /* Use the end date to get the closest tick after it, then
526 * step back one tick to get the last tick before the
529 last_time = ClosestTick(end_date, cur_time, _re, &res);
531 * An event that occurs at the same time as the end_date is an
534 if (last_time != end_date)
535 last_time = PrevTick(last_time, cur_time, _re, res);
537 /* Return the re to its original state. */
538 _re->re_end_date = end_date;
539 _re->re_duration = duration;