/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: relasttick.c /main/6 1996/11/21 19:46:04 drk $ */ /* * (c) Copyright 1993, 1994 Hewlett-Packard Company * (c) Copyright 1993, 1994 International Business Machines Corp. * (c) Copyright 1993, 1994 Novell, Inc. * (c) Copyright 1993, 1994 Sun Microsystems, Inc. */ #define XOS_USE_NO_LOCKING #define X_INCLUDE_TIME_H #if defined(__linux__) #undef SVR4 #endif #include #include #include "rerule.h" #include "repeat.h" #include "reutil.h" static Tick DoMinute(const Tick, const RepeatEvent *); static Tick DoDay(const Tick, const RepeatEvent *); static Tick DoWeek(const Tick, RepeatEvent *); static Tick DoMonthDay(const Tick, const RepeatEvent *); static Tick DoMonthPos(const Tick, RepeatEvent *); static Tick DoYearByMonth(const Tick, RepeatEvent *); static Tick DoYearByDay(const Tick, RepeatEvent *); static Tick LastOccurence(const Tick, const WeekDayTime *, const unsigned int); static Tick LastTickFromEndDate(const Tick, const RepeatEvent *); static int LastDayExists(const struct tm *, const unsigned int, const unsigned int *); extern void FillInRepeatEvent(const Tick, RepeatEvent *); extern Tick ClosestTick(const Tick, const Tick, RepeatEvent *, RepeatEventState **); extern Tick PrevTick(const Tick, const Tick, RepeatEvent *, RepeatEventState *); Tick LastTick( const Tick start_time, RepeatEvent *re) { Tick last_time = 0; if (!re) return (Tick)0; if (re->re_duration == RE_INFINITY) return EOT; FillInRepeatEvent(start_time, re); switch (re->re_type) { case RT_MINUTE: last_time = DoMinute(start_time, re); break; case RT_DAILY: last_time = DoDay(start_time, re); break; case RT_WEEKLY: last_time = DoWeek(start_time, re); break; case RT_MONTHLY_POSITION: last_time = DoMonthPos(start_time, re); break; case RT_MONTHLY_DAY: last_time = DoMonthDay(start_time, re); break; case RT_YEARLY_MONTH: last_time = DoYearByMonth(start_time, re); break; case RT_YEARLY_DAY: last_time = DoYearByDay(start_time, re); break; } return last_time; } static Tick DoMinute( const Tick start_time, const RepeatEvent *re) { return (Tick)0; } static Tick DoDay( const Tick start_time, const RepeatEvent *re) { Tick last_time1 = EOT, last_time2 = EOT; if (re->re_end_date) { last_time1 = LastTickFromEndDate(start_time, re); } if (re->re_duration != RE_NOTSET) { struct tm *start_tm; _Xltimeparams localtime_buf; start_tm = _XLocaltime((const time_t *)&start_time, localtime_buf); /* Go to the last day an event can happen on. */ start_tm->tm_mday += (re->re_duration - 1) * re->re_interval; start_tm->tm_isdst = -1; /* Go to the last time an event can happen on the last day. */ if (RE_DAILY(re)->dd_ntime) { start_tm->tm_hour = RE_DAILY(re)->dd_time[RE_DAILY(re)->dd_ntime - 1] / 100; start_tm->tm_min = RE_DAILY(re)->dd_time[RE_DAILY(re)->dd_ntime - 1] % 100; } last_time2 = mktime(start_tm); } return ((last_time1 < last_time2) ? last_time1 : last_time2); } static Tick DoWeek( const Tick start_time, RepeatEvent *re) { struct tm *start_tm; unsigned int wd_ndaytime = RE_WEEKLY(re)->wd_ndaytime, dt_ntime, start_hour, start_min; Tick _start_time = start_time, last_time1 = EOT, last_time2 = EOT; RepeatEventState *res; _Xltimeparams localtime_buf; if (re->re_end_date) { last_time1 = LastTickFromEndDate(start_time, re); if (re->re_duration == RE_NOTSET) return last_time1; } /* Find the real start time */ _start_time = ClosestTick(start_time, start_time, re, &res); free(res); start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf); start_hour = start_tm->tm_hour; start_min = start_tm->tm_min; /* Go to the last day an event can happen on. */ start_tm->tm_mday += (re->re_duration - 1) * re->re_interval * 7; start_tm->tm_isdst = -1; if (wd_ndaytime) { _start_time = mktime(start_tm); start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf); /* Normalize to the beginning of the week */ start_tm->tm_mday -= start_tm->tm_wday; start_tm->tm_sec = start_tm->tm_min = start_tm->tm_hour = 0; start_tm->tm_isdst = -1; _start_time = mktime(start_tm); start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf); /* Move forward to the proper week day */ start_tm->tm_mday += RE_WEEKLY(re)->wd_daytime[wd_ndaytime - 1].dt_day; dt_ntime = RE_WEEKLY(re)->wd_daytime[wd_ndaytime - 1].dt_ntime; /* Set the proper time */ if (dt_ntime) { start_tm->tm_hour = RE_WEEKLY(re)-> wd_daytime[wd_ndaytime - 1].dt_time[dt_ntime - 1] / 100; start_tm->tm_min = RE_WEEKLY(re)-> wd_daytime[wd_ndaytime - 1].dt_time[dt_ntime - 1] % 100; } else { start_tm->tm_hour = start_hour; start_tm->tm_min = start_min; } } start_tm->tm_isdst = -1; last_time2 = mktime(start_tm); return ((last_time1 < last_time2) ? last_time1 : last_time2); } static Tick DoMonthDay( const Tick start_time, const RepeatEvent *re) { struct tm start_tm, *cur_tm; unsigned int md_nitems = RE_MONTHLY(re)->md_nitems, *md_days = RE_MONTHLY(re)->md_days, interval = 1; Tick cur_time, last_time1 = EOT, last_time2 = EOT; _Xltimeparams localtime_buf; if (re->re_end_date) { last_time1 = LastTickFromEndDate(start_time, re); if (re->re_duration == RE_NOTSET) return last_time1; } start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf); cur_tm = _XLocaltime((const time_t *)&start_time, localtime_buf); start_tm.tm_isdst = -1; /* The 28th - 31st may not exist in a given month thus if only these * days are specified in a rule it is necessary to calculate the * correct month by brute force versus as a mathimatical calculation. */ if (md_days[0] > 28) { cur_tm->tm_mday = 1; /* Compute last event by brute force */ do { cur_tm->tm_mon += re->re_interval; cur_tm->tm_isdst = -1; cur_time = mktime(cur_tm); cur_tm = _XLocaltime((const time_t *)&cur_time, localtime_buf); if (DayExists( DayOfMonth(md_days[0], cur_tm->tm_mon, cur_tm->tm_year), cur_tm->tm_mon, cur_tm->tm_year)) interval++; } while (interval < re->re_duration); start_tm.tm_mon = cur_tm->tm_mon; start_tm.tm_year = cur_tm->tm_year; start_tm.tm_mday = LastDayExists(cur_tm, md_nitems, md_days); } else if (md_nitems) { start_tm.tm_mon += (re->re_duration - 1) * re->re_interval; start_tm.tm_mday = 1; /* Have the year and month updated */ cur_time = mktime(&start_tm); start_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf); /* Get the right day (LASTDAY converted to a real day) */ start_tm.tm_isdst = -1; start_tm.tm_mday = DayOfMonth( md_days[md_nitems - 1], start_tm.tm_mon, start_tm.tm_year); } else start_tm.tm_mon += (re->re_duration - 1) * re->re_interval; last_time2 = mktime(&start_tm); return ((last_time1 < last_time2) ? last_time1 : last_time2); } static Tick DoMonthPos( const Tick _start_time, RepeatEvent *re) { struct tm start_tm, cur_tm; Tick last_time1 = EOT, last_time2 = EOT, start_time = _start_time; unsigned int nwdt_list = RE_MONTHLY(re)->md_nitems, num_intervals = 1, i, j, brute_force = TRUE; WeekDayTime *wdt_list = RE_MONTHLY(re)->md_weektime; RepeatEventState *res; _Xltimeparams localtime_buf; if (re->re_end_date) { last_time1 = LastTickFromEndDate(start_time, re); if (re->re_duration == RE_NOTSET) return last_time1; } /* Find the real start time */ start_time = ClosestTick(start_time, start_time, re, &res); free(res); start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf); for (i = 0; i < nwdt_list; i++) { for (j = 0; j < wdt_list[i].wdt_nweek; j++) { if ((wdt_list[i].wdt_week[j] != WK_F5) && (wdt_list[i].wdt_week[j] != WK_L5)) { brute_force = FALSE; break; } } if (brute_force == FALSE) break; } if (brute_force) { cur_tm = start_tm; cur_tm.tm_mday = 1; while (num_intervals < re->re_duration) { cur_tm.tm_mon += re->re_interval; cur_tm.tm_isdst = -1; last_time2 = mktime(&cur_tm); cur_tm = *_XLocaltime((const time_t *)&last_time2, localtime_buf); if (OccurenceExists(wdt_list, nwdt_list, last_time2)) num_intervals++; if (!InTimeRange(num_intervals, re->re_duration)) break; } } else { start_tm.tm_mon += (re->re_duration - 1) * re->re_interval; start_tm.tm_isdst = -1; start_tm.tm_mday = 1; last_time2 = mktime(&start_tm); } /* * Given the occurence information, find the last valid day in the * month. */ last_time2 = LastOccurence(last_time2, wdt_list, nwdt_list); return ((last_time1 < last_time2) ? last_time1 : last_time2); } static Tick DoYearByMonth( const Tick start_time, RepeatEvent *re) { struct tm *start_tm; int start_day; Tick _start_time, last_time1 = EOT, last_time2 = EOT; RepeatEventState *res; _Xltimeparams localtime_buf; if (re->re_end_date) { last_time1 = LastTickFromEndDate(start_time, re); if (re->re_duration == RE_NOTSET) return last_time1; } /* Find the real start time */ _start_time = ClosestTick(start_time, start_time, re, &res); free(res); start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf); start_day = start_tm->tm_mday; /* Go to the last day an event can happen on. */ start_tm->tm_year += (re->re_duration - 1) * re->re_interval; start_tm->tm_isdst = -1; /* XXX: If the only month used is Feb and the date is the 29th, then * we must use a special case. */ /* Go to the last time an event can happen on on the last month. */ if (RE_YEARLY(re)->yd_nitems) { int i; _start_time = mktime(start_tm); start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf); for (i = RE_YEARLY(re)->yd_nitems - 1; i >= 0; i++) { if (DayExists(start_day, RE_YEARLY(re)->yd_items[i], start_tm->tm_year)) { start_tm->tm_mon = RE_YEARLY(re)->yd_items[i]-1; start_tm->tm_isdst = -1; return (mktime(start_tm)); } } /* No months have a day that can be used */ return (Tick)0; } last_time2 = mktime(start_tm); return ((last_time1 < last_time2) ? last_time1 : last_time2); } static Tick DoYearByDay( const Tick start_time, RepeatEvent *re) { struct tm *start_tm; int start_day; Tick _start_time, last_time1 = EOT, last_time2 = EOT; RepeatEventState *res; _Xltimeparams localtime_buf; if (re->re_end_date) { last_time1 = LastTickFromEndDate(start_time, re); if (re->re_duration == RE_NOTSET) return last_time1; } /* Find the real start time */ _start_time = ClosestTick(start_time, start_time, re, &res); free(res); start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf); /* Go to the last year an event can happen on. */ start_tm->tm_year += (re->re_duration - 1) * re->re_interval; start_tm->tm_isdst = -1; /* Go to the last time an event can happen on. */ if (RE_YEARLY(re)->yd_nitems) { start_tm->tm_mon = 0; start_tm->tm_mday = RE_YEARLY(re)->yd_items[RE_YEARLY(re)->yd_nitems - 1]; } last_time2 = mktime(start_tm); return ((last_time1 < last_time2) ? last_time1 : last_time2); } /* * Given a month/year (from cur_tm), and a list of days of the month * determine the last day in that list that is valid in that month. */ static int LastDayExists( const struct tm *cur_tm, const unsigned int md_nitems, const unsigned int *md_days) { int i; int day; for (i = md_nitems - 1; i >= 0; i--) { day = DayOfMonth(md_days[i], cur_tm->tm_mon, cur_tm->tm_year); if (DayExists(day, cur_tm->tm_mon, cur_tm->tm_year)) return(day); } return 0; } /* * Given a month/year (in cur_time) determine the last occurence of week/day/ * time in the month. */ static Tick LastOccurence( const Tick cur_time, const WeekDayTime *wdt_list, const unsigned int nwdt_list) { int i, j, k; Tick oldest_time = 0, current_time; for (i = 0; i < nwdt_list; i++) { for (j = 0; j < wdt_list[i].wdt_nweek; j++) { for (k = 0; k < wdt_list[i].wdt_nday; k++) { if (current_time = WeekNumberToDay(cur_time, wdt_list[i].wdt_week[j], wdt_list[i].wdt_day[k])) { if (current_time > oldest_time) oldest_time = current_time; } } } } return oldest_time; } /* * Given a time and a rule find the last tick before the end date. */ static Tick LastTickFromEndDate( const Tick cur_time, const RepeatEvent *re) { RepeatEventState *res; RepeatEvent *_re = (RepeatEvent *)re; Tick end_date = re->re_end_date, last_time; Duration duration = re->re_duration; /* Take the end date out of the equation. */ _re->re_end_date = 0; _re->re_duration = RE_INFINITY; /* Use the end date to get the closest tick after it, then * step back one tick to get the last tick before the * end date. */ last_time = ClosestTick(end_date, cur_time, _re, &res); /* * An event that occurs at the same time as the end_date is an * event. */ if (last_time != end_date) last_time = PrevTick(last_time, cur_time, _re, res); /* Return the re to its original state. */ _re->re_end_date = end_date; _re->re_duration = duration; free (res); return last_time; }