/* * 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: repeat.c /main/7 1996/11/21 19:46:39 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. */ #include #include #define XOS_USE_NO_LOCKING #define X_INCLUDE_TIME_H #if defined(__linux__) #undef SVR4 #endif #include #include "csa.h" #include "rtable4.h" #include "cm.h" #include "repeat.h" #include "attr.h" #include "iso8601.h" #define EOT 2147483647 extern int debug; static time_t bot, eot; typedef enum { minsec = 60, fivemins = 300, hrsec = 3600, daysec = 86400, wksec = 604800, yrsec = 31536000, leapyrsec = 31622400 } Unit; static unsigned int weekdaymasks[] = { 0x1, /* sunday */ 0x2, /* monday */ 0x4, /* tuesday */ 0x8, /* wednesday */ 0x10, /* thursday */ 0x20, /* friday */ 0x40 /* saturday */ }; static int monthsecs[12] = { 31*daysec, 28*daysec, 31*daysec, 30*daysec, 31*daysec, 30*daysec, 31*daysec, 31*daysec, 30*daysec, 31*daysec, 30*daysec, 31*daysec }; int monthdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static int lastapptofweek(u_int mask); static int ntimes_this_week(u_int weekmask, int firstday); static boolean_t nthweekdayofmonth(time_t t, int *nth); static time_t next_nmonth(time_t t, int n); static int adjust_dst(time_t start, time_t next); static time_t prev_nmonth(time_t t, int n); static time_t nextnyear(time_t t, int n); static int timeok(time_t t); static time_t prevnyear(time_t t, int n); static time_t prevmonth_exactday(time_t t); static time_t nextmonth_exactday(time_t t); static time_t previousmonth(time_t t); static int monthseconds(time_t t); static int get_ndelta(time_t startdate, Period_4 period, int ntimes); static time_t lastnthweekday(time_t t, int nth, int ntimes); static time_t nextnthweekday(time_t t, int nth); static time_t prevnthweekday(time_t t, int nth); static time_t nextnday_exacttime(time_t t, int n); static time_t prevnday_exacttime(time_t t, int n); static time_t nextnwk_exacttime(time_t t, int n); static time_t prevnwk_exacttime(time_t t, int n); static time_t nextnmth_exactday(time_t t, int n); static time_t prevnmth_exactday(time_t t, int n); static time_t nextmonTofri(time_t t); static time_t prevmonTofri(time_t t); static time_t nextmonwedfri(time_t t); static time_t prevmonwedfri(time_t t); static time_t nexttuethur(time_t t); static time_t prevtuethur(time_t t); static time_t nextdaysofweek(time_t t, int weekmask); static time_t prevdaysofweek(time_t t, int weekmask); /* [ytso 1/26/93] * cm now supports to up end of 1999. This is due to the limitation * of cm_getdate() which can only handle up to end of 1999. * When cm_getdate() is improved to handle up to the system limit, * definitions of eot and EOT need to be changed as well as some * of the routines in this file and the caller of these routines. */ extern void init_time(void) { struct tm tm, gm; time_t t; _Xltimeparams localtime_buf; _Xgtimeparams gmtime_buf; t = time(0); tm = *_XLocaltime(&t, localtime_buf); gm = *_XGmtime(&t, gmtime_buf); bot = mktime(&gm) - mktime(&tm); tm.tm_sec =59; tm.tm_min =59; tm.tm_hour =23; tm.tm_mday =31; tm.tm_mon =11; tm.tm_year =137; /* Dec. 31, 2037 */ tm.tm_isdst = -1; eot =mktime(&tm); } extern void _DtCms_adjust_appt_startdate(Appt_4 *appt) { struct tm *tm; _Xltimeparams localtime_buf; if (appt->period.period < monThruFri_4 || appt->period.period > tueThur_4) return; tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf); switch (appt->period.period) { case monThruFri_4: if (tm->tm_wday < 1 || tm->tm_wday > 5) appt->appt_id.tick = _DtCms_next_tick_v4( appt->appt_id.tick, appt->period); break; case monWedFri_4: if ((tm->tm_wday % 2) == 0) appt->appt_id.tick = _DtCms_next_tick_v4( appt->appt_id.tick, appt->period); break; case tueThur_4: if (tm->tm_wday != 2 && tm->tm_wday != 4) appt->appt_id.tick = _DtCms_next_tick_v4( appt->appt_id.tick, appt->period); break; } } /* * Calculate the actual number of instances of the repeating event. */ extern int _DtCms_get_ninstance_v4(Appt_4 *appt) { struct tm *tm; int i, pdelta, ndelta, ninstance, timesperweek; double dninstance; _Xltimeparams localtime_buf; if (appt->ntimes == _DtCM_OLD_REPEAT_FOREVER) return(appt->ntimes); switch (appt->period.period) { case everyNthDay_4: case everyNthWeek_4: case everyNthMonth_4: ninstance = (appt->ntimes+(appt->period.nth-1))/appt->period.nth; break; case monThruFri_4: tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf); pdelta = 6 - tm->tm_wday; ndelta = get_ndelta(appt->appt_id.tick, appt->period, appt->ntimes); dninstance = (double)(appt->ntimes - 1) * 5 + pdelta - ndelta; ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ? _DtCM_OLD_REPEAT_FOREVER : (int)dninstance; break; case monWedFri_4: tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf); pdelta = (7 - tm->tm_wday) / 2; ndelta = get_ndelta(appt->appt_id.tick, appt->period, appt->ntimes); dninstance = (double)(appt->ntimes - 1) * 3 + pdelta - ndelta; ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ? _DtCM_OLD_REPEAT_FOREVER : (int)dninstance; break; case tueThur_4: tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf); pdelta = (tm->tm_wday == 2) ? 2 : 1; ndelta = get_ndelta(appt->appt_id.tick, appt->period, appt->ntimes); dninstance = (double)(appt->ntimes - 1) * 2 + pdelta - ndelta; ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ? _DtCM_OLD_REPEAT_FOREVER : (int)dninstance; break; case daysOfWeek_4: tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf); timesperweek = ntimes_this_week((u_int)appt->period.nth, 0); pdelta = ntimes_this_week((u_int)appt->period.nth, tm->tm_wday); ndelta = get_ndelta(appt->appt_id.tick, appt->period, appt->ntimes); dninstance = (double)(appt->ntimes-1) * timesperweek + pdelta - ndelta; ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ? _DtCM_OLD_REPEAT_FOREVER : (int)dninstance; break; default: ninstance = appt->ntimes; } return ninstance; } /* * calculate the ntimes value which, depending on the * repeating event type, may not be the same * as the actual number of instances */ extern int _DtCms_get_new_ntimes_v4(Period_4 period, time_t tick, int ninstance) { struct tm *tm; int ntimes; int delta = 0, firstweek, timesperweek; _Xltimeparams localtime_buf; switch (period.period) { case everyNthDay_4: case everyNthWeek_4: case everyNthMonth_4: ntimes = ninstance * period.nth; break; case monThruFri_4: tm = _XLocaltime(&tick, localtime_buf); if (ninstance % 5) delta = ((ninstance % 5) > (6 - tm->tm_wday)) ? 2 : 1; else if (tm->tm_wday != 1) delta = 1; ntimes = (ninstance/5) + delta; break; case monWedFri_4: tm = _XLocaltime(&tick, localtime_buf); if (ninstance % 3) delta = ((ninstance % 3) > ((7-tm->tm_wday)/2)) ? 2:1; else if (tm->tm_wday != 1) delta = 1; ntimes = (ninstance/3) + delta; break; case tueThur_4: tm = _XLocaltime(&tick, localtime_buf); if (ninstance % 2 || tm->tm_wday != 2) delta = 1; ntimes = (ninstance/2) + delta; break; case daysOfWeek_4: tm = _XLocaltime(&tick, localtime_buf); timesperweek = ntimes_this_week((u_int)period.nth, 0); firstweek=ntimes_this_week((u_int)period.nth,tm->tm_wday); if (ninstance % timesperweek) delta = ((ninstance % timesperweek) > firstweek) ? 2:1; else if (firstweek != timesperweek) delta = 1; ntimes = (ninstance/timesperweek) + delta; break; default: ntimes = ninstance; break; } return ntimes; } extern time_t _DtCms_first_tick_v4(time_t t, Period_4 period, int ordinal) { int i; time_t ftick; ftick = t; for (i = 1; i < ordinal; i++) ftick = _DtCms_prev_tick_v4(ftick, period); return(ftick); } /* * Given a time, calculate the closest instance whose * tick is later than the time. * If the calculated tick does not pass timeok(), ftick is * returned and ordinal set to 1. */ extern time_t _DtCms_closest_tick_v4(time_t target, time_t ftick, Period_4 period, int *ordinal) { time_t ctick; int delta = 0; int remainder = 0; int ndays; struct tm *tm; struct tm tm1, tm2; _Xltimeparams localtime_buf; tm1.tm_isdst = 0; tm2.tm_isdst = 0; if (target <= ftick) { *ordinal = 1; return(ftick); } if (period.period < monthly_4 || period.period == everyNthDay_4 || period.period == everyNthWeek_4) { tm1 = *_XLocaltime(&ftick, localtime_buf); tm2 = *_XLocaltime(&target, localtime_buf); } switch(period.period) { case daily_4: delta = (target - ftick) / daysec; remainder = target - ftick - daysec * delta; if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst) remainder -= hrsec; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnday_exacttime(ftick, *ordinal - 1); break; case weekly_4: delta = (target - ftick) / wksec; remainder = target - ftick - wksec * delta; if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst) remainder -= hrsec; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnwk_exacttime(ftick, *ordinal - 1); break; case biweekly_4: delta = (target - ftick) / (wksec * 2); remainder = target - ftick - wksec * 2 * delta; if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst) remainder -= hrsec; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnwk_exacttime(ftick, 2 * (*ordinal - 1)); break; case monthly_4: tm = _XLocaltime(&ftick, localtime_buf); /* * Calculate the closest tick only if the date * is < 29; otherwise just return the first tick. * Use 32 to take care of dst time difference. * Without dst, we can use 31. */ if (tm->tm_mday < 29) { delta = (target - ftick) / (daysec * 32); remainder = target - ftick - (daysec * 32) * delta; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnmth_exactday(ftick, *ordinal - 1); } else { ctick = ftick; *ordinal = 1; } break; case yearly_4: tm = _XLocaltime(&ftick, localtime_buf); if (tm->tm_mday == 29 && tm->tm_mon == 1) { delta = (target - ftick) / (yrsec * 4 + daysec); remainder = target - ftick - (yrsec * 4) * delta; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnyear(ftick, (*ordinal - 1) * 4); } else { delta = (target - ftick) / yrsec; /* adjustment for leap year */ remainder = tm->tm_year % 4; if (remainder == 0 || (remainder + delta) > 3) delta--; remainder = target - ftick - yrsec * delta; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnyear(ftick, *ordinal - 1); } break; case nthWeekday_4: /* 36 is 5 weeks ==> maximum interval between 2 instances */ delta = (target - ftick) / (daysec * 36); remainder = target - ftick - (daysec * 36) * delta; *ordinal = delta + (remainder>0?1:0) + 1; ctick = lastnthweekday(ftick, period.nth, *ordinal - 1); break; case everyNthDay_4: delta = (target - ftick) / (daysec * period.nth); remainder = target - ftick - (daysec * period.nth) * delta; if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst) remainder -= hrsec; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnday_exacttime(ftick, period.nth * (*ordinal - 1)); break; case everyNthWeek_4: delta = (target - ftick) / (wksec * period.nth); remainder = target - ftick - (wksec * period.nth) * delta; if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst) remainder -= hrsec; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnwk_exacttime(ftick, period.nth * (*ordinal - 1)); break; case everyNthMonth_4: tm = _XLocaltime(&ftick, localtime_buf); if (tm->tm_mday < 29) { delta = (target - ftick) / (daysec * 32 * period.nth); remainder = target-ftick-(daysec*32*period.nth)*delta; *ordinal = delta + (remainder>0?1:0) + 1; ctick = nextnmth_exactday(ftick, period.nth * (*ordinal - 1)); } else { ctick = ftick; *ordinal = 1; } break; case monThruFri_4: case monWedFri_4: case tueThur_4: case daysOfWeek_4: delta = (target - ftick) / wksec; tm = _XLocaltime(&ftick, localtime_buf); switch (period.period) { case monThruFri_4: *ordinal = delta * 5 + 6 - tm->tm_wday; break; case monWedFri_4: *ordinal = delta * 3 + (7 - tm->tm_wday) / 2; break; case tueThur_4: *ordinal = delta * 2 + ((tm->tm_wday == 2) ? 2 : 1); break; case daysOfWeek_4: *ordinal = delta * ntimes_this_week((u_int)period.nth,0) + ntimes_this_week((u_int)period.nth, tm->tm_wday); } /* delta*daysperweek+(lastapptofweek-firstday in first week) */ if (period.period == daysOfWeek_4) { ndays = delta * 7 + lastapptofweek((u_int)period.nth) - tm->tm_wday; ctick = ftick + ndays * daysec; } else if (period.period == tueThur_4) { ndays = delta * 7 + 4 - tm->tm_wday; ctick = ftick + ndays * daysec; } else { ndays = delta * 7 + 5 - tm->tm_wday; ctick = ftick + ndays * daysec; } if (ctick > target) { /* need to go back 1 week */ ndays -= 7; if (ndays < 0) { *ordinal = 1; ctick = ftick; } else { if (period.period == monThruFri_4) *ordinal -= 5; else if (period.period == monWedFri_4) *ordinal -= 3; else if (period.period == tueThur_4) *ordinal -= 2; ctick -= (7 * daysec); } } ctick = adjust_dst(ftick, ctick); break; default: *ordinal = 1; ctick = ftick; } if (timeok(ctick)) return(ctick); else { *ordinal = 1; return(ftick); } } /* * Calculate the tick of the last instance of a repeating event. * If the calculated tick does not pass timeok(), EOT is returned. */ extern time_t _DtCms_last_tick_v4(time_t ftick, Period_4 period, int ntimes) { struct tm *tm; double dltick; time_t ltick = 0; int i; _Xltimeparams localtime_buf; if (ntimes >= _DtCM_OLD_REPEAT_FOREVER) return(EOT); if (period.enddate != 0) return(period.enddate); switch(period.period) { case weekly_4: ltick = nextnwk_exacttime(ftick, ntimes - 1); break; case biweekly_4: /* 2 * (ntimes-1) won't overflow an integer since * we make sure ntimes is < EOT */ ltick = nextnwk_exacttime(ftick, 2 * (ntimes - 1)); break; case daily_4: ltick = nextnday_exacttime(ftick, ntimes - 1); break; case monthly_4: tm = _XLocaltime(&ftick, localtime_buf); /* * calculate the last tick only if the date * is < 29; otherwise return EOT to force calculation */ if (tm->tm_mday < 29) ltick = nextnmth_exactday(ftick, ntimes - 1); else ltick = EOT; break; case yearly_4: /* 2038 is the last year that can be represented. * this check is to prevent (ntimes-1)*4 from integer overflow */ if (ntimes > 2038) ltick = EOT; else { tm = _XLocaltime(&ftick, localtime_buf); if (tm->tm_mday == 29 && tm->tm_mon == 1) ltick = nextnyear(ftick, (ntimes - 1) * 4); else ltick = nextnyear(ftick, ntimes - 1); } break; case nthWeekday_4: ltick = lastnthweekday(ftick, period.nth, ntimes - 1); break; case everyNthDay_4: ltick = nextnday_exacttime(ftick, period.nth * (((ntimes+(period.nth-1))/period.nth) - 1)); break; case everyNthWeek_4: ltick = nextnwk_exacttime(ftick, period.nth * (((ntimes+(period.nth-1))/period.nth) - 1)); break; case everyNthMonth_4: tm = _XLocaltime(&ftick, localtime_buf); if (tm->tm_mday < 29) ltick = nextnmth_exactday(ftick, period.nth * (((ntimes+(period.nth-1))/period.nth) -1)); else ltick = EOT; break; case monThruFri_4: case monWedFri_4: case tueThur_4: case daysOfWeek_4: tm = _XLocaltime(&ftick, localtime_buf); /* (ntimes-1)*daysperweek+(lastapptofweek-fstapptofFstweek) */ if (period.period == daysOfWeek_4) dltick = ftick + ((double)(ntimes - 1) * 7 + lastapptofweek((u_int)period.nth) - tm->tm_wday) * daysec; else if (period.period == tueThur_4) dltick = ftick + ((double)(ntimes - 1) * 7 + (4 - tm->tm_wday)) * daysec; else dltick = ftick + ((double)(ntimes - 1) * 7 + (5 - tm->tm_wday)) * daysec; if (dltick >= EOT || dltick < 0) ltick = EOT; else ltick = adjust_dst(ftick, (time_t)dltick); break; default: break; } if(timeok(ltick)) return(ltick); else return(EOT); } /* * Calculate the tick of next instance. * If the calculated tick does not pass timeok(), EOT is returned. */ extern time_t _DtCms_next_tick_v4(time_t tick, Period_4 period) { time_t next = 0; struct tm *tm; _Xltimeparams localtime_buf; switch(period.period) { case weekly_4: next = nextnwk_exacttime(tick, 1); break; case biweekly_4: next = nextnwk_exacttime(tick, 2); break; case daily_4: next = nextnday_exacttime(tick, 1); break; case monthly_4: next = nextmonth_exactday(tick); break; case yearly_4: tm = _XLocaltime(&tick, localtime_buf); if (tm->tm_mday == 29 && tm->tm_mon == 1) next = nextnyear(tick, 4); else next = nextnyear(tick, 1); break; case nthWeekday_4: next = nextnthweekday(tick, period.nth); break; case everyNthDay_4: next = nextnday_exacttime(tick, period.nth); break; case everyNthWeek_4: next = nextnwk_exacttime(tick, period.nth); break; case everyNthMonth_4: next = nextnmth_exactday(tick, period.nth); break; case monThruFri_4: next = nextmonTofri(tick); break; case monWedFri_4: next = nextmonwedfri(tick); break; case tueThur_4: next = nexttuethur(tick); break; case daysOfWeek_4: next = nextdaysofweek(tick, period.nth); break; default: break; } if(next != tick && timeok(next)) return(next); else return(EOT); } /* * Calculate the tick of previous instance. * If the calculated tick does not pass timeok(), bot-1 is returned. */ extern time_t _DtCms_prev_tick_v4(time_t tick, Period_4 period) { time_t prev = 0; struct tm *tm; _Xltimeparams localtime_buf; switch(period.period) { case weekly_4: prev = prevnwk_exacttime(tick, 1); break; case biweekly_4: prev = prevnwk_exacttime(tick, 2); break; case daily_4: prev = prevnday_exacttime(tick, 1); break; case monthly_4: prev = prevmonth_exactday(tick); break; case yearly_4: tm = _XLocaltime(&tick, localtime_buf); if (tm->tm_mday == 29 && tm->tm_mon == 1) prev = prevnyear(tick, 4); else prev = prevnyear(tick, 1); break; case nthWeekday_4: prev = prevnthweekday(tick, period.nth); break; case everyNthDay_4: prev = prevnday_exacttime(tick, period.nth); break; case everyNthWeek_4: prev = prevnwk_exacttime(tick, period.nth); break; case everyNthMonth_4: prev = prevnmth_exactday(tick, period.nth); break; case monThruFri_4: prev = prevmonTofri(tick); break; case monWedFri_4: prev = prevmonwedfri(tick); break; case tueThur_4: prev = prevtuethur(tick); break; case daysOfWeek_4: prev = prevdaysofweek(tick, period.nth); break; default: break; } if(prev != tick && timeok(prev)) return(prev); else return(bot-1); } /* * dont_care_cancel: * TRUE - it is a match regard_DtCmsIsLess the event is cancelled, * FALSE - it is not a match if the event is cancelled. */ extern int _DtCms_in_repeater(Id_4 *key, Appt_4 *p_appt, boolean_t dont_care_cancel) { Period_4 period; int ordinal; int ntimes; time_t tick; ntimes = _DtCms_get_ninstance_v4(p_appt); period = p_appt->period; tick = _DtCms_closest_tick_v4(key->tick, p_appt->appt_id.tick, period, &ordinal); ordinal--; while (++ordinal <= ntimes) { if (tick > key->tick) /* out-of-bound */ break; if (tick == key->tick) { if (dont_care_cancel) return (ordinal); if (!_DtCms_marked_4_cancellation (p_appt, ordinal)) return (ordinal); } tick = _DtCms_next_tick_v4 (tick, period); } return (0); } extern int _DtCms_marked_4_cancellation(Appt_4 *a, int i) { Except_4 *p; if (a==NULL) return(0); p = a->exception; /* in descending order for faster access */ while (p!=NULL) { if (i > p->ordinal) break; if (i == p->ordinal) return(1); p = p->next; } return(0); } extern time_t next_ndays(time_t t, int n) { time_t next; struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0; #if defined(SVR4) || defined(__linux__) next = mktime(&tm); #else next = timelocal(&tm); #endif /* SVR4 */ next = next + n * daysec; next = adjust_dst(t, next); return(next); } extern time_t next_nmins(time_t t, int m) { time_t next; struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_sec = 0; tm.tm_min = 0; next = mktime(&tm); next = next + m * minsec; next = adjust_dst(t, next); return(next); } extern time_t _DtCmsBeginOfDay(time_t t) { struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0; return(mktime(&tm)); } extern time_t _DtCmsTimeOfDay(time_t t) { struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0; return(t - mktime(&tm)); } /* * Given a weekmask, find the last appointment in the week */ static int lastapptofweek(u_int mask) { int n; if (mask == 0) return -1; for (n = -1; mask != 0; n++, mask = mask >> 1); return n; } /* * Given a weekmask and the first day of the week, calculate * the number of times outstanding in the week. */ static int ntimes_this_week(u_int weekmask, int firstday) { int i, ntimes, weekdaymask = 1 << firstday; if (weekmask == 0) return 0; for (i=firstday, ntimes=0; i < 7; i++, weekdaymask <<= 1) { if (weekdaymask & weekmask) ntimes++; } return ntimes; } static boolean_t nthweekdayofmonth(time_t t, int *nth) { struct tm tm, tm2, tmfirstday; time_t firstday; _Xltimeparams localtime_buf; tmfirstday = tm = *_XLocaltime(&t, localtime_buf); *nth = (12 + tm.tm_mday - tm.tm_wday)/7; tmfirstday.tm_hour = 0; tmfirstday.tm_min = 0; tmfirstday.tm_sec = 0; tmfirstday.tm_mday = 1; firstday = mktime(&tmfirstday); tmfirstday = *_XLocaltime(&firstday, localtime_buf); if (tm.tm_wday < tmfirstday.tm_wday) (*nth)--; if (*nth < 4) return B_FALSE; else { t += (7 * daysec); tm2 = *_XLocaltime(&t, localtime_buf); return((tm.tm_mon == tm2.tm_mon) ? B_FALSE : B_TRUE); } } /* * If the result falls beyond the system limit, -1 is returned by mktime(). */ static time_t next_nmonth(time_t t, int n) { struct tm tm; int n12; _Xltimeparams localtime_buf; n12 = n/12; n = n%12; tm = *_XLocaltime(&t, localtime_buf); tm.tm_hour=0; tm.tm_min=0; tm.tm_sec=0; tm.tm_mday=1; if (n12 > 0) tm.tm_year += n12; if ((tm.tm_mon = tm.tm_mon + n) > 11) { tm.tm_mon -= 12; tm.tm_year++; } tm.tm_isdst = -1; return(mktime(&tm)); } static int adjust_dst(time_t start, time_t next) { struct tm oldt; struct tm newt; _Xltimeparams localtime_buf; oldt = *_XLocaltime(&start, localtime_buf); newt = *_XLocaltime(&next, localtime_buf); if (oldt.tm_isdst == newt.tm_isdst) { return (next); } else if (oldt.tm_isdst == 1) { return (next + (int)hrsec); } else { return (next - (int)hrsec); } } static time_t prev_nmonth(time_t t, int n) { struct tm tm; int n12; _Xltimeparams localtime_buf; n12 = n/12; n = n%12; tm = *_XLocaltime(&t, localtime_buf); tm.tm_hour=0; tm.tm_min=0; tm.tm_sec=0; tm.tm_mday=1; if (n12 > 0) tm.tm_year -= n12; if ((tm.tm_mon = tm.tm_mon - n) < 0) { tm.tm_mon += 12; tm.tm_year--; } #if defined(SVR4) || defined(__linux__) tm.tm_isdst = -1; return(mktime(&tm)); #else return(timelocal(&tm)); #endif /* SVR4 */ } extern int leapyr(int y) { return (y % 4 == 0 && y % 100 !=0 || y % 400 == 0); } extern int monthlength(Tick t) { int mon; struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); mon = tm.tm_mon; return(((mon==1) && leapyr(tm.tm_year+1900))? 29 : monthdays[mon]); } extern int /* find dow(0-6) that 1st dom falls on */ fdom(Tick t) { struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_mday = 1; tm.tm_isdst = -1; t = mktime(&tm); tm = *_XLocaltime(&t, localtime_buf); return(tm.tm_wday); } extern int ldom(Tick t /* find dow(0-6) that last dom falls on */ ) { struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_mday = monthlength(t); tm.tm_isdst = -1; t = mktime(&tm); tm = *_XLocaltime(&t, localtime_buf); return(tm.tm_wday); } extern boolean_t _DtCmsInExceptionList(cms_entry *eptr, time_t tick) { CSA_date_time_entry *dt = NULL; time_t time; if (eptr->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value) dt = eptr->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->\ item.date_time_list_value; for (; dt != NULL; dt = dt->next) { if (_csa_iso8601_to_tick(dt->date_time, &time)) continue; if (time == tick) return (B_TRUE); } return (B_FALSE); } static time_t nextnyear(time_t t, int n) { struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_year += n; #if defined(SVR4) || defined(__linux__) return(mktime(&tm)); #else return(timelocal(&tm)); #endif /* SVR4 */ } static int timeok(time_t t) { int r =((t >= bot) &&(t <= eot)); return(r); } static time_t prevnyear(time_t t, int n) { struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_year -= n; #if defined(SVR4) || defined(__linux__) return(mktime(&tm)); #else return(timelocal(&tm)); #endif /* SVR4 */ } static time_t prevmonth_exactday(time_t t) { time_t prev; int day; struct tm tm; int sdelta; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); sdelta = tm.tm_hour * hrsec + tm.tm_min * minsec + tm.tm_sec; day = tm.tm_mday; if((tm.tm_mday < 31 && tm.tm_mon != 0) || /* at least 30 days everywhere, except Feb.*/ (tm.tm_mday==31 && tm.tm_mon==6) || /* two 31s -- Jul./Aug. */ (tm.tm_mday==31 && tm.tm_mon==11) || /* two 31s -- Dec./Jan. */ (tm.tm_mon == 0 &&(tm.tm_mday < 29 ||(tm.tm_mday==29 && leapyr(tm.tm_year+1900))))) { prev = t-monthseconds(previousmonth(t)); prev = adjust_dst(t, prev); } else { /* brute force */ prev = previousmonth(previousmonth(t)); /* hop over the month */ tm = *_XLocaltime(&prev, localtime_buf); tm.tm_mday = day; #if defined(SVR4) || defined(__linux__) tm.tm_isdst = -1; prev =(mktime(&tm)) + sdelta; #else prev =(timelocal(&tm)) + sdelta; #endif /* SVR4 */ } return(prev); } static time_t nextmonth_exactday(time_t t) { time_t next; int day; struct tm tm; int sdelta; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); sdelta = tm.tm_hour * hrsec + tm.tm_min * minsec + tm.tm_sec; day = tm.tm_mday; if((tm.tm_mday < 31 && tm.tm_mon != 0) || /* at least 30 days everywhere, except Feb.*/ (tm.tm_mday==31 && tm.tm_mon==6) || /* two 31s -- Jul./Aug. */ (tm.tm_mday==31 && tm.tm_mon==11) || /* two 31s -- Dec./Jan. */ (tm.tm_mon == 0 &&(tm.tm_mday < 29 ||(tm.tm_mday==29 && leapyr(tm.tm_year+1900))))) { next = t+monthseconds(t); next = adjust_dst(t, next); } else { /* brute force */ next = next_nmonth(t, 2); /* hop over the month */ tm = *_XLocaltime(&next, localtime_buf); tm.tm_mday = day; #if defined(SVR4) || defined(__linux__) tm.tm_isdst = -1; next = mktime(&tm) + sdelta; #else next =(timelocal(&tm)) + sdelta; #endif /* SVR4 */ } return(next); } static time_t previousmonth(time_t t) { struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); tm.tm_hour=0; tm.tm_min=0; tm.tm_sec=0; if(tm.tm_mon==0) { tm.tm_mon=11; tm.tm_mday=1; tm.tm_year--; } else { tm.tm_mday=1; tm.tm_mon--; } #if defined(SVR4) || defined(__linux__) tm.tm_isdst = -1; return(mktime(&tm)); #else return(timelocal(&tm)); #endif /* SVR4 */ } static int monthseconds(time_t t) { int mon; struct tm tm; _Xltimeparams localtime_buf; tm = *_XLocaltime(&t, localtime_buf); mon = tm.tm_mon; return(((mon==1) && leapyr(tm.tm_year+1900)) ? 29*daysec : monthsecs[mon]); } /* * find the number of instances to be subtracted */ static int get_ndelta(time_t startdate, Period_4 period, int ntimes) { struct tm *tm; int ndelta = 0; time_t lastdate; double dlastdate; _Xltimeparams localtime_buf; if (period.enddate == 0) return(ndelta); /* find last day of the series */ dlastdate = startdate + (double)wksec * (ntimes - 1); /* last week */ if (dlastdate > EOT) return(ndelta); else lastdate = (time_t)dlastdate; tm = _XLocaltime(&lastdate, localtime_buf); if (period.period == monThruFri_4 || period.period == monWedFri_4) lastdate = lastdate + daysec * (5 - tm->tm_wday); else if (period.period == tueThur_4) lastdate = lastdate + daysec * (4 - tm->tm_wday); else if (period.period == daysOfWeek_4) lastdate = lastdate + daysec * (lastapptofweek((u_int)period.nth) - tm->tm_wday); if (period.enddate > lastdate) return(ndelta); tm = _XLocaltime(&period.enddate, localtime_buf); switch (period.period) { case monThruFri_4: ndelta = 5 - tm->tm_wday; break; case monWedFri_4: if (tm->tm_wday < 3) ndelta = 2; else if (tm->tm_wday < 5) ndelta = 1; break; case tueThur_4: if (tm->tm_wday < 2) ndelta = 2; else if (tm->tm_wday < 4) ndelta = 1; break; case daysOfWeek_4: ndelta = ntimes_this_week((u_int)period.nth, tm->tm_wday) - 1; break; } return(ndelta); } static time_t lastnthweekday(time_t t, int nth, int ntimes) { struct tm tm1, tm2; time_t tick, ntick; int delta; int sdelta; _Xltimeparams localtime_buf; /* * if nth is not specified, assume it's the * 4th week for the ambiguous case. */ if (nth == 0) { nthweekdayofmonth(t, &nth); if (nth > 4) nth = -1; } tm1 = *_XLocaltime(&t, localtime_buf); sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; if (nth > 0) { if ((tick = next_nmonth(t, ntimes)) == EOT || tick < 0) return(EOT); tm2 = *_XLocaltime(&tick, localtime_buf); delta = tm1.tm_wday - tm2.tm_wday; if (delta < 0) delta += 7; ntick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta; } else { if ((tick = next_nmonth(t, ntimes + 1)) == EOT || tick < 0) return(EOT); tm2 = *_XLocaltime(&tick, localtime_buf); delta = tm2.tm_wday - tm1.tm_wday; if (tm1.tm_wday >= tm2.tm_wday) delta += 7; ntick = tick - (delta * daysec) + sdelta; } ntick = adjust_dst(tick, ntick); return (ntick); } static time_t nextnthweekday(time_t t, int nth) { struct tm tm1, tm2; time_t tick, ntick; int delta; int sdelta; _Xltimeparams localtime_buf; /* * if nth is not specified, assume it's the * 4th week for the ambiguous case. */ if (nth == 0) { nthweekdayofmonth(t, &nth); if (nth > 4) nth = -1; } tm1 = *_XLocaltime(&t, localtime_buf); sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; if (nth > 0) { tick = next_nmonth(t, 1); tm2 = *_XLocaltime(&tick, localtime_buf); delta = tm1.tm_wday - tm2.tm_wday; if (delta < 0) delta += 7; ntick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta; } else { tick = next_nmonth(t, 2); tm2 = *_XLocaltime(&tick, localtime_buf); delta = tm2.tm_wday - tm1.tm_wday; if (tm1.tm_wday >= tm2.tm_wday) delta += 7; ntick = tick - (delta * daysec) + sdelta; } ntick = adjust_dst(tick, ntick); return (ntick); } static time_t prevnthweekday(time_t t, int nth) { struct tm tm1, tm2; time_t tick, ptick; int delta; int sdelta; _Xltimeparams localtime_buf; /* * if nth is not specified, assume it's the * 4th week for the ambiguous case. */ if (nth == 0) { nthweekdayofmonth(t, &nth); if (nth > 4) nth = -1; } tm1 = *_XLocaltime(&t, localtime_buf); sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; if (nth > 0) { tick = prev_nmonth(t, 1); tm2 = *_XLocaltime(&tick, localtime_buf); delta = tm1.tm_wday - tm2.tm_wday; if (delta < 0) delta += 7; ptick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta; } else { tick = prev_nmonth(next_nmonth(t, 1), 1); tm2 = *_XLocaltime(&tick, localtime_buf); delta = tm2.tm_wday - tm1.tm_wday; if (tm1.tm_wday >= tm2.tm_wday) delta += 7; ptick = tick - (delta * daysec) + sdelta; } ptick = adjust_dst(tick, ptick); return (ptick); } /* use double in this routine to avoid integer overflow * in case n is very large. */ static time_t nextnday_exacttime(time_t t, int n) { double next; next = t + (double)n * daysec; if (next >= EOT || next < 0) return(EOT); else { next = adjust_dst(t, (time_t)next); return((time_t)next); } } /* * This is defined in the private library and is used also by the front * end -- should it be here? */ static time_t prevnday_exacttime(time_t t, int n) { time_t prev; prev = t - (n * daysec); prev = adjust_dst(t, prev); return(prev); } /* use double in this routine to avoid integer overflow * in case n is very large. */ static time_t nextnwk_exacttime(time_t t, int n) { double next; next = t + (double)n * 7 * daysec; if (next >= EOT || next < 0) return(EOT); else { next = adjust_dst(t, (time_t)next); return((time_t)next); } } static time_t prevnwk_exacttime(time_t t, int n) { time_t prev; prev = t - n * 7 * daysec; prev = adjust_dst(t, prev); return(prev); } static time_t nextnmth_exactday(time_t t, int n) { struct tm tm1, tm2; boolean_t done = B_FALSE; time_t next; _Xltimeparams localtime_buf; tm1 = *_XLocaltime(&t, localtime_buf); while (!done) { if ((next = next_nmonth(t, n)) == EOT || next < 0) return(EOT); tm2 = *_XLocaltime(&next, localtime_buf); /* 1. at least 30 days except feb * 2. 2/29 on leap year * 3. 31st on the appropriate month */ if ((tm1.tm_mday < 31 && tm2.tm_mon != 1) || (tm2.tm_mon == 1 && (tm1.tm_mday < 29 || (tm1.tm_mday == 29 && leapyr(tm2.tm_year + 1900)))) || (tm1.tm_mday == 31 && ((tm2.tm_mon > 6 && tm2.tm_mon % 2) || ((tm2.tm_mon <= 6 && (tm2.tm_mon % 2 == 0)))))) { tm2.tm_sec = tm1.tm_sec; tm2.tm_min = tm1.tm_min; tm2.tm_hour = tm1.tm_hour; tm2.tm_mday = tm1.tm_mday; done = B_TRUE; } else t = next; } #if defined(SVR4) || defined(__linux__) tm2.tm_isdst = -1; next = mktime(&tm2); #else next = (timelocal(&tm2)); #endif return(next); } static time_t prevnmth_exactday(time_t t, int n) { struct tm tm1, tm2; boolean_t done = B_FALSE; time_t prev; _Xltimeparams localtime_buf; tm1 = *_XLocaltime(&t, localtime_buf); while (!done) { prev = prev_nmonth(t, n); tm2 = *_XLocaltime(&prev, localtime_buf); if ((tm1.tm_mday < 30 && tm2.tm_mon != 1) || (tm2.tm_mon == 1 && (tm1.tm_mday < 29 || (tm1.tm_mday == 29 && leapyr(tm2.tm_year + 1900)))) || (tm1.tm_mday == 31 && ((tm2.tm_mon > 6 && tm2.tm_mon % 2) || ((tm2.tm_mon <= 6 && (tm2.tm_mon % 2 == 0)))))) { tm2.tm_sec = tm1.tm_sec; tm2.tm_min = tm1.tm_min; tm2.tm_hour = tm1.tm_hour; tm2.tm_mday = tm1.tm_mday; done = B_TRUE; } else t = prev; } #if defined(SVR4) || defined(__linux__) tm2.tm_isdst = -1; prev = mktime(&tm2); #else prev = (timelocal(&tm2)); #endif return(prev); } static time_t nextmonTofri(time_t t) { struct tm *tm; time_t next; _Xltimeparams localtime_buf; tm = _XLocaltime(&t, localtime_buf); if (tm->tm_wday < 5) next = t + (int)daysec; else next = t + (int)daysec * (8 - tm->tm_wday); next = adjust_dst(t, next); return(next); } static time_t prevmonTofri(time_t t) { struct tm *tm; time_t prev; _Xltimeparams localtime_buf; tm = _XLocaltime(&t, localtime_buf); if (tm->tm_wday > 1) prev = t - (int)daysec; else prev = t - (int)daysec * (2 + tm->tm_wday); prev = adjust_dst(t, prev); return(prev); } static time_t nextmonwedfri(time_t t) { struct tm *tm; time_t next; _Xltimeparams localtime_buf; tm = _XLocaltime(&t, localtime_buf); if (tm->tm_wday == 5) next = t + (int)daysec * 3; else if (tm->tm_wday % 2 || tm->tm_wday == 6) next = t + (int)daysec * 2; else next = t + (int)daysec; next = adjust_dst(t, next); return(next); } static time_t prevmonwedfri(time_t t) { struct tm *tm; time_t prev; _Xltimeparams localtime_buf; tm = _XLocaltime(&t, localtime_buf); if (tm->tm_wday == 1) prev = t - (int)daysec * 3; else if (tm->tm_wday % 2 || tm->tm_wday == 0) prev = t - (int)daysec * 2; else prev = t - (int)daysec; prev = adjust_dst(t, prev); return(prev); } static time_t nexttuethur(time_t t) { struct tm *tm; time_t next; _Xltimeparams localtime_buf; tm = _XLocaltime(&t, localtime_buf); if (tm->tm_wday < 4) { if (tm->tm_wday % 2) next = t + (int)daysec; else next = t + (int)daysec * 2; } else next = t + (int)daysec * (9 - tm->tm_wday); next = adjust_dst(t, next); return(next); } static time_t prevtuethur(time_t t) { struct tm *tm; time_t prev; _Xltimeparams localtime_buf; tm = _XLocaltime(&t, localtime_buf); if (tm->tm_wday > 2) { if (tm->tm_wday % 2) prev = t - (int)daysec; else prev = t - (int)daysec * 2; } else prev = t - (int)daysec * (3 + tm->tm_wday); prev = adjust_dst(t, prev); return(prev); } /* * the 7-bit mask should be put in the last 7 bits of the int */ static time_t nextdaysofweek(time_t t, int weekmask) { unsigned int doublemask; struct tm *tm; int i, ndays, daymask; time_t next; _Xltimeparams localtime_buf; doublemask = weekmask | (weekmask << 7); tm = _XLocaltime(&t, localtime_buf); daymask = weekdaymasks[tm->tm_wday] << 1; for (i = 0, ndays = 1; i < 7; i++) { if (daymask & doublemask) break; else { ndays++; doublemask >>= 1; } } next = t + (int)daysec * ndays; next = adjust_dst(t, next); return(next); } static time_t prevdaysofweek(time_t t, int weekmask) { unsigned int doublemask, daymask; struct tm *tm; int i, ndays; time_t prev; _Xltimeparams localtime_buf; doublemask = weekmask | (weekmask << 7); tm = _XLocaltime(&t, localtime_buf); daymask = weekdaymasks[tm->tm_wday] << 6; for (i = 0, ndays = 1; i < 7; i++) { if (daymask & doublemask) break; else { ndays++; doublemask <<= 1; } } prev = t - (int)daysec * ndays; prev = adjust_dst(t, prev); return(prev); }