Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtcm / server / repeat.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 /* $XConsortium: repeat.c /main/7 1996/11/21 19:46:39 drk $ */
24 /*
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.
29  */
30
31 #include <EUSCompat.h>
32 #include <stdio.h>
33 #define XOS_USE_NO_LOCKING
34 #define X_INCLUDE_TIME_H
35 #include <X11/Xos_r.h>
36 #include "csa.h"
37 #include "rtable4.h"
38 #include "cm.h"
39 #include "repeat.h"
40 #include "attr.h"
41 #include "iso8601.h"
42
43 #define EOT                     2147483647
44
45 extern  int     debug;
46
47 static time_t bot, eot;
48
49 typedef enum {
50         minsec          = 60,
51         fivemins        = 300,
52         hrsec           = 3600,
53         daysec          = 86400,
54         wksec           = 604800,
55         yrsec           = 31536000,
56         leapyrsec       = 31622400
57 } Unit;
58
59 static unsigned int weekdaymasks[] = {
60         0x1,    /* sunday */
61         0x2,    /* monday */
62         0x4,    /* tuesday */
63         0x8,    /* wednesday */
64         0x10,   /* thursday */
65         0x20,   /* friday */
66         0x40    /* saturday */
67 };
68
69 static int monthsecs[12] = {
70         31*daysec,      28*daysec,      31*daysec,
71         30*daysec,      31*daysec,      30*daysec,
72         31*daysec,      31*daysec,      30*daysec,
73         31*daysec,      30*daysec,      31*daysec
74 };
75
76 extern int monthdays[12] = {
77         31,     28,     31,
78         30,     31,     30,
79         31,     31,     30,
80         31,     30,     31
81 };
82
83 static int lastapptofweek(u_int mask);
84 static int ntimes_this_week(u_int weekmask, int firstday);
85 static boolean_t nthweekdayofmonth(time_t t, int *nth);
86 static time_t next_nmonth(time_t t, int n);
87 static int adjust_dst(time_t start, time_t next); 
88 static time_t prev_nmonth(time_t t, int n);
89 static time_t nextnyear(time_t t, int n);
90 static int timeok(time_t t);
91 static time_t prevnyear(time_t t, int n);
92 static time_t prevmonth_exactday(time_t t);
93 static time_t nextmonth_exactday(time_t t);
94 static time_t previousmonth(time_t t);
95 static int monthseconds(time_t t);
96 static int get_ndelta(time_t startdate, Period_4 period, int ntimes);
97 static time_t lastnthweekday(time_t t, int nth, int ntimes);
98 static time_t nextnthweekday(time_t t, int nth);
99 static time_t prevnthweekday(time_t t, int nth);
100 static time_t nextnday_exacttime(time_t t, int n);
101 static time_t prevnday_exacttime(time_t t, int n);
102 static time_t nextnwk_exacttime(time_t t, int n);
103 static time_t prevnwk_exacttime(time_t t, int n);
104 static time_t nextnmth_exactday(time_t t, int n);
105 static time_t prevnmth_exactday(time_t t, int n);
106 static time_t nextmonTofri(time_t t);
107 static time_t prevmonTofri(time_t t);
108 static time_t nextmonwedfri(time_t t);
109 static time_t prevmonwedfri(time_t t);
110 static time_t nexttuethur(time_t t);
111 static time_t prevtuethur(time_t t);
112 static time_t nextdaysofweek(time_t t, int weekmask);
113 static time_t prevdaysofweek(time_t t, int weekmask);
114
115 /* [ytso 1/26/93]
116  * cm now supports to up end of 1999.  This is due to the limitation
117  * of cm_getdate() which can only handle up to end of 1999.
118  * When cm_getdate() is improved to handle up to the system limit,
119  * definitions of eot and EOT need to be changed as well as some
120  * of the routines in this file and the caller of these routines.
121  */
122 extern void
123 init_time()
124 {
125         struct tm tm, gm;
126         time_t  t;
127         _Xltimeparams localtime_buf;
128         _Xgtimeparams gmtime_buf;
129
130 #ifdef __osf__
131         char *tzptr;
132 #endif
133
134 #ifdef __osf__
135         /* Fix for QAR 31607 */
136         if (getenv("TZ") == NULL){
137                 tzset();
138                 tzptr = malloc(strlen(tzname[0]) + strlen(tzname[1]) + 10);
139                 sprintf (tzptr,"TZ=%s%d%s", tzname[0], timezone/3600, tzname[1]);
140                 putenv(tzptr);
141                 tzset();
142         }
143         else
144                 tzset();
145 #endif
146
147         t               = time(0);
148         tm              = *_XLocaltime(&t, localtime_buf);
149         gm              = *_XGmtime(&t, gmtime_buf);
150
151         bot             = mktime(&gm) - mktime(&tm);
152
153         tm.tm_sec       =59;
154         tm.tm_min       =59;
155         tm.tm_hour      =23;
156         tm.tm_mday      =31;
157         tm.tm_mon       =11;
158         tm.tm_year      =137;                   /* Dec. 31, 2037 */
159         tm.tm_isdst = -1;
160         eot             =mktime(&tm);
161 }
162
163 extern void
164 _DtCms_adjust_appt_startdate(Appt_4 *appt)
165 {
166         struct tm *tm;
167         _Xltimeparams localtime_buf;
168
169         if (appt->period.period < monThruFri_4 ||
170             appt->period.period > tueThur_4)
171                 return;
172
173         tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
174         switch (appt->period.period) {
175         case monThruFri_4:
176                 if (tm->tm_wday < 1 || tm->tm_wday > 5)
177                         appt->appt_id.tick = _DtCms_next_tick_v4(
178                                                 appt->appt_id.tick,
179                                                 appt->period);
180                 break;
181         case monWedFri_4:
182                 if ((tm->tm_wday % 2) == 0)
183                         appt->appt_id.tick = _DtCms_next_tick_v4(
184                                                 appt->appt_id.tick,
185                                                 appt->period);
186                 break;
187         case tueThur_4:
188                 if (tm->tm_wday != 2 && tm->tm_wday != 4)
189                         appt->appt_id.tick = _DtCms_next_tick_v4(
190                                                 appt->appt_id.tick,
191                                                 appt->period);
192                 break;
193         }
194 }
195
196 /*
197  * Calculate the actual number of instances of the repeating event.
198  */
199 extern int
200 _DtCms_get_ninstance_v4(Appt_4 *appt)
201 {
202         struct tm *tm;
203         int i, pdelta, ndelta, ninstance, timesperweek;
204         double dninstance;
205         _Xltimeparams localtime_buf;
206
207         if (appt->ntimes == _DtCM_OLD_REPEAT_FOREVER)
208                 return(appt->ntimes);
209
210         switch (appt->period.period) {
211         case everyNthDay_4:
212         case everyNthWeek_4:
213         case everyNthMonth_4:
214                 ninstance = (appt->ntimes+(appt->period.nth-1))/appt->period.nth;
215                 break;
216
217         case monThruFri_4:
218                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
219                 pdelta = 6 - tm->tm_wday;
220                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
221                                 appt->ntimes);
222                 dninstance = (double)(appt->ntimes - 1) * 5 + pdelta - ndelta;
223                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ?
224                                 _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
225                 break;
226
227         case monWedFri_4:
228                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
229                 pdelta = (7 - tm->tm_wday) / 2;
230                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
231                                 appt->ntimes);
232                 dninstance = (double)(appt->ntimes - 1) * 3 + pdelta - ndelta;
233                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER)
234                                 ? _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
235                 break;
236
237         case tueThur_4:
238                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
239                 pdelta = (tm->tm_wday == 2) ? 2 : 1;
240                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
241                                 appt->ntimes);
242                 dninstance = (double)(appt->ntimes - 1) * 2 + pdelta - ndelta;
243                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ?
244                                 _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
245                 break;
246
247         case daysOfWeek_4:
248                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
249                 timesperweek = ntimes_this_week((u_int)appt->period.nth, 0);
250                 pdelta = ntimes_this_week((u_int)appt->period.nth, tm->tm_wday);
251                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
252                                 appt->ntimes);
253                 dninstance = (double)(appt->ntimes-1) * timesperweek +
254                                 pdelta - ndelta;
255                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ?
256                                 _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
257
258                 break;
259         default:
260                 ninstance = appt->ntimes;
261         }
262
263         return ninstance;
264 }
265
266 /*
267  * calculate the ntimes value which, depending on the
268  * repeating event type, may not be the same
269  * as the actual number of instances
270  */
271 extern int
272 _DtCms_get_new_ntimes_v4(Period_4 period, time_t tick, int ninstance)
273 {
274         struct tm *tm;
275         int ntimes;
276         int delta = 0, firstweek, timesperweek;
277         _Xltimeparams localtime_buf;
278
279         switch (period.period) {
280         case everyNthDay_4:
281         case everyNthWeek_4:
282         case everyNthMonth_4:
283                 ntimes = ninstance * period.nth;
284                 break;
285         case monThruFri_4:
286                 tm = _XLocaltime(&tick, localtime_buf);
287                 if (ninstance % 5)
288                         delta = ((ninstance % 5) > (6 - tm->tm_wday)) ? 2 : 1;
289                 else if (tm->tm_wday != 1)
290                         delta = 1;
291                 ntimes = (ninstance/5) + delta;
292                 break;
293         case monWedFri_4:
294                 tm = _XLocaltime(&tick, localtime_buf);
295                 if (ninstance % 3)
296                         delta = ((ninstance % 3) > ((7-tm->tm_wday)/2)) ? 2:1;
297                 else if (tm->tm_wday != 1)
298                         delta = 1;
299                 ntimes = (ninstance/3) + delta;
300                 break;
301         case tueThur_4:
302                 tm = _XLocaltime(&tick, localtime_buf);
303                 if (ninstance % 2 || tm->tm_wday != 2)
304                         delta = 1;
305                 ntimes = (ninstance/2) + delta;
306                 break;
307         case daysOfWeek_4:
308                 tm = _XLocaltime(&tick, localtime_buf);
309                 timesperweek = ntimes_this_week((u_int)period.nth, 0);
310                 firstweek=ntimes_this_week((u_int)period.nth,tm->tm_wday);
311                 if (ninstance % timesperweek)
312                         delta = ((ninstance % timesperweek) > firstweek) ? 2:1;
313                 else if (firstweek != timesperweek)
314                         delta = 1;
315                 ntimes = (ninstance/timesperweek) + delta;
316                 break;
317         default:
318                 ntimes = ninstance;
319                 break;
320         }
321
322         return ntimes;
323 }
324
325 extern time_t
326 _DtCms_first_tick_v4(time_t t, Period_4 period, int ordinal)
327 {
328         int i;
329         time_t ftick;
330
331         ftick = t;
332         for (i = 1; i < ordinal; i++)
333                 ftick = _DtCms_prev_tick_v4(ftick, period);
334
335         return(ftick);
336 }
337
338
339 /*
340  * Given a time, calculate the closest instance whose
341  * tick is later than the time.
342  * If the calculated tick does not pass timeok(), ftick is
343  * returned and ordinal set to 1.
344  */
345 extern time_t
346 _DtCms_closest_tick_v4(time_t target, time_t ftick, Period_4 period, int *ordinal)
347 {
348         time_t ctick;
349         int delta = 0;
350         int remainder = 0;
351         int ndays;
352         struct tm *tm;
353         struct tm tm1, tm2;
354         _Xltimeparams localtime_buf;
355
356         if (target <= ftick) {
357                 *ordinal = 1;
358                 return(ftick);
359         }
360
361         if (period.period < monthly_4 || period.period == everyNthDay_4 ||
362             period.period == everyNthWeek_4) {
363                 tm1 = *_XLocaltime(&ftick, localtime_buf);
364                 tm2 = *_XLocaltime(&target, localtime_buf);
365         }
366         switch(period.period) {
367         case daily_4:
368                 delta = (target - ftick) / daysec;
369                 remainder = target - ftick - daysec * delta;
370                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
371                         remainder -= hrsec;
372                 *ordinal = delta + (remainder>0?1:0) + 1;
373                 ctick = nextnday_exacttime(ftick, *ordinal - 1);
374                 break;
375         case weekly_4:
376                 delta = (target - ftick) / wksec;
377                 remainder = target - ftick - wksec * delta;
378                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
379                         remainder -= hrsec;
380                 *ordinal = delta + (remainder>0?1:0) + 1;
381                 ctick = nextnwk_exacttime(ftick, *ordinal - 1);
382                 break;
383         case biweekly_4:
384                 delta = (target - ftick) / (wksec * 2);
385                 remainder = target - ftick - wksec * 2 * delta;
386                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
387                         remainder -= hrsec;
388                 *ordinal = delta + (remainder>0?1:0) + 1;
389                 ctick = nextnwk_exacttime(ftick, 2 * (*ordinal - 1));
390                 break;
391         case monthly_4:
392                 tm = _XLocaltime(&ftick, localtime_buf);
393                 /*
394                  * Calculate the closest tick only if the date
395                  * is < 29; otherwise just return the first tick.
396                  * Use 32 to take care of dst time difference.
397                  * Without dst, we can use 31.
398                  */
399                 if (tm->tm_mday < 29) {
400                         delta = (target - ftick) / (daysec * 32);
401                         remainder = target - ftick - (daysec * 32) * delta;
402                         *ordinal = delta + (remainder>0?1:0) + 1;
403                         ctick = nextnmth_exactday(ftick, *ordinal - 1);
404                 } else {
405                         ctick = ftick;
406                         *ordinal = 1;
407                 }
408                 break;
409         case yearly_4:
410                 tm = _XLocaltime(&ftick, localtime_buf);
411                 if (tm->tm_mday == 29 && tm->tm_mon == 1) {
412                         delta = (target - ftick) / (yrsec * 4 + daysec);
413                         remainder = target - ftick - (yrsec * 4) * delta;
414                         *ordinal = delta + (remainder>0?1:0) + 1;
415                         ctick = nextnyear(ftick, (*ordinal - 1) * 4);
416                 } else {
417                         delta = (target - ftick) / yrsec;
418                         /* adjustment for leap year */
419                         remainder = tm->tm_year % 4;
420                         if (remainder == 0 || (remainder + delta) > 3)
421                                 delta--;
422                         remainder = target - ftick - yrsec * delta;
423                         *ordinal = delta + (remainder>0?1:0) + 1;
424                         ctick = nextnyear(ftick, *ordinal - 1);
425                 }
426                 break;
427         case nthWeekday_4:
428                 /* 36 is 5 weeks ==> maximum interval between 2 instances */
429                 delta = (target - ftick) / (daysec * 36);
430                 remainder = target - ftick - (daysec * 36) * delta;
431                 *ordinal = delta + (remainder>0?1:0) + 1;
432                 ctick = lastnthweekday(ftick, period.nth, *ordinal - 1);
433                 break;
434         case everyNthDay_4:
435                 delta = (target - ftick) / (daysec * period.nth);
436                 remainder = target - ftick - (daysec * period.nth) * delta;
437                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
438                         remainder -= hrsec;
439                 *ordinal = delta + (remainder>0?1:0) + 1;
440                 ctick = nextnday_exacttime(ftick,
441                                 period.nth * (*ordinal - 1));
442                 break;
443         case everyNthWeek_4:
444                 delta = (target - ftick) / (wksec * period.nth);
445                 remainder = target - ftick - (wksec * period.nth) * delta;
446                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
447                         remainder -= hrsec;
448                 *ordinal = delta + (remainder>0?1:0) + 1;
449                 ctick = nextnwk_exacttime(ftick,
450                                 period.nth * (*ordinal - 1));
451                 break;
452         case everyNthMonth_4:
453                 tm = _XLocaltime(&ftick, localtime_buf);
454                 if (tm->tm_mday < 29) {
455                         delta = (target - ftick) / (daysec * 32 * period.nth);
456                         remainder = target-ftick-(daysec*32*period.nth)*delta;
457                         *ordinal = delta + (remainder>0?1:0) + 1;
458                         ctick = nextnmth_exactday(ftick,
459                                         period.nth * (*ordinal - 1));
460                 } else {
461                         ctick = ftick;
462                         *ordinal = 1;
463                 }
464                 break;
465         case monThruFri_4:
466         case monWedFri_4:
467         case tueThur_4:
468         case daysOfWeek_4:
469                 delta = (target - ftick) / wksec;
470                 tm = _XLocaltime(&ftick, localtime_buf);
471
472                 switch (period.period) {
473                 case monThruFri_4:
474                         *ordinal = delta * 5 + 6 - tm->tm_wday;
475                         break;
476                 case monWedFri_4:
477                         *ordinal = delta * 3 + (7 - tm->tm_wday) / 2;
478                         break;
479                 case tueThur_4:
480                         *ordinal = delta * 2 + ((tm->tm_wday == 2) ? 2 : 1);
481                         break;
482                 case daysOfWeek_4:
483                         *ordinal = delta * ntimes_this_week((u_int)period.nth,0)
484                                         + ntimes_this_week((u_int)period.nth,
485                                                 tm->tm_wday);
486                 }
487
488                 /* delta*daysperweek+(lastapptofweek-firstday in first week) */
489                 if (period.period == daysOfWeek_4) {
490                         ndays = delta * 7 +
491                                 lastapptofweek((u_int)period.nth) - tm->tm_wday;
492                         ctick = ftick + ndays * daysec;
493                 } else if (period.period == tueThur_4) {
494                         ndays = delta * 7 + 4 - tm->tm_wday;
495                         ctick = ftick + ndays * daysec;
496                 } else {
497                         ndays = delta * 7 + 5 - tm->tm_wday;
498                         ctick = ftick + ndays * daysec;
499                 }
500
501                 if (ctick > target) { /* need to go back 1 week */
502                         ndays -= 7;
503                         if (ndays < 0) {
504                                 *ordinal = 1;
505                                 ctick = ftick;
506                         } else {
507                                 if (period.period == monThruFri_4)
508                                         *ordinal -= 5;
509                                 else if (period.period == monWedFri_4)
510                                         *ordinal -= 3;
511                                 else if (period.period == tueThur_4)
512                                         *ordinal -= 2;
513                                 ctick -= (7 * daysec);
514                         }
515                 }
516                 ctick = adjust_dst(ftick, ctick);
517                 break;
518         default:
519                 *ordinal = 1;
520                 ctick = ftick;
521         }
522
523         if (timeok(ctick))
524                 return(ctick);
525         else {
526                 *ordinal = 1;
527                 return(ftick);
528         }
529 }
530
531 /*
532  * Calculate the tick of the last instance of a repeating event.
533  * If the calculated tick does not pass timeok(), EOT is returned.
534  */
535 extern time_t
536 _DtCms_last_tick_v4(time_t ftick, Period_4 period, int ntimes)
537 {
538         struct tm *tm;
539         double dltick;
540         time_t ltick;
541         int i;
542         _Xltimeparams localtime_buf;
543
544         if (ntimes >= _DtCM_OLD_REPEAT_FOREVER)
545                 return(EOT);
546
547         if (period.enddate != 0)
548                 return(period.enddate);
549
550         switch(period.period) {
551         case weekly_4:
552                 ltick = nextnwk_exacttime(ftick, ntimes - 1);
553                 break;
554         case biweekly_4:
555                 /* 2 * (ntimes-1) won't overflow an integer since
556                  * we make sure ntimes is < EOT
557                  */
558                 ltick = nextnwk_exacttime(ftick, 2 * (ntimes - 1));
559                 break;
560         case daily_4:
561                 ltick = nextnday_exacttime(ftick, ntimes - 1);
562                 break;
563         case monthly_4:
564                 tm = _XLocaltime(&ftick, localtime_buf);
565                 /*
566                  * calculate the last tick only if the date
567                  * is < 29; otherwise return EOT to force calculation
568                  */
569                 if (tm->tm_mday < 29)
570                         ltick = nextnmth_exactday(ftick, ntimes - 1);
571                 else
572                         ltick = EOT;
573                 break;
574         case yearly_4:
575                 /* 2038 is the last year that can be represented.
576                  * this check is to prevent (ntimes-1)*4 from integer overflow
577                  */
578                 if (ntimes > 2038)
579                         ltick = EOT;
580                 else {
581                         tm = _XLocaltime(&ftick, localtime_buf);
582                         if (tm->tm_mday == 29 && tm->tm_mon == 1)
583                                 ltick = nextnyear(ftick, (ntimes - 1) * 4);
584                         else
585                                 ltick = nextnyear(ftick, ntimes - 1);
586                 }
587                 break;
588         case nthWeekday_4:
589                 ltick = lastnthweekday(ftick, period.nth, ntimes - 1);
590                 break;
591         case everyNthDay_4:
592                 ltick = nextnday_exacttime(ftick, period.nth * 
593                         (((ntimes+(period.nth-1))/period.nth) - 1));
594                 break;
595         case everyNthWeek_4:
596                 ltick = nextnwk_exacttime(ftick, period.nth *
597                         (((ntimes+(period.nth-1))/period.nth) - 1));
598                 break;
599         case everyNthMonth_4:
600                 tm = _XLocaltime(&ftick, localtime_buf);
601                 if (tm->tm_mday < 29)
602                         ltick = nextnmth_exactday(ftick, period.nth *
603                                 (((ntimes+(period.nth-1))/period.nth) -1));
604                 else
605                         ltick = EOT;
606                 break;
607         case monThruFri_4:
608         case monWedFri_4:
609         case tueThur_4:
610         case daysOfWeek_4:
611                 tm = _XLocaltime(&ftick, localtime_buf);
612
613                 /* (ntimes-1)*daysperweek+(lastapptofweek-fstapptofFstweek) */
614                 if (period.period == daysOfWeek_4)
615                         dltick = ftick +
616                                 ((double)(ntimes - 1) * 7 +
617                                 lastapptofweek((u_int)period.nth) - tm->tm_wday)
618                                 * daysec;
619                 else if (period.period == tueThur_4)
620                         dltick = ftick +
621                                 ((double)(ntimes - 1) * 7 + (4 - tm->tm_wday)) *
622                                 daysec;
623                 else
624                         dltick = ftick +
625                                 ((double)(ntimes - 1) * 7 + (5 - tm->tm_wday)) *
626                                 daysec;
627
628                 if (dltick >= EOT || dltick < 0)
629                         ltick = EOT;
630                 else
631                         ltick = adjust_dst(ftick, (time_t)dltick);
632                 break;
633         default:
634                 break;
635         }
636         if(timeok(ltick))
637                 return(ltick);
638         else
639                 return(EOT);
640 }
641
642 /*
643  * Calculate the tick of next instance.
644  * If the calculated tick does not pass timeok(), EOT is returned.
645  */
646 extern time_t
647 _DtCms_next_tick_v4(time_t tick, Period_4 period)
648 {
649         time_t next;
650         struct tm *tm;
651         _Xltimeparams localtime_buf;
652
653         switch(period.period) {
654                 case weekly_4:
655                         next = nextnwk_exacttime(tick, 1);
656                         break;
657                 case biweekly_4:
658                         next = nextnwk_exacttime(tick, 2);
659                         break;
660                 case daily_4:
661                         next = nextnday_exacttime(tick, 1);
662                         break;
663                 case monthly_4:
664                         next = nextmonth_exactday(tick);
665                         break;
666                 case yearly_4:
667                         tm = _XLocaltime(&tick, localtime_buf);
668                         if (tm->tm_mday == 29 && tm->tm_mon == 1)
669                                 next = nextnyear(tick, 4);
670                         else
671                                 next = nextnyear(tick, 1);
672                         break;
673                 case nthWeekday_4:
674                         next = nextnthweekday(tick, period.nth);
675                         break;
676                 case everyNthDay_4:
677                         next = nextnday_exacttime(tick, period.nth);
678                         break;
679                 case everyNthWeek_4:
680                         next = nextnwk_exacttime(tick, period.nth);
681                         break;
682                 case everyNthMonth_4:
683                         next = nextnmth_exactday(tick, period.nth);
684                         break;
685                 case monThruFri_4:
686                         next = nextmonTofri(tick);
687                         break;
688                 case monWedFri_4:
689                         next = nextmonwedfri(tick);
690                         break;
691                 case tueThur_4:
692                         next = nexttuethur(tick);
693                         break;
694                 case daysOfWeek_4:
695                         next = nextdaysofweek(tick, period.nth);
696                         break;
697                 default:
698                         break;
699         }
700         if(next != tick && timeok(next)) return(next);
701         else return(EOT);
702 }
703
704 /*
705  * Calculate the tick of previous instance.
706  * If the calculated tick does not pass timeok(), bot-1 is returned.
707  */
708 extern time_t
709 _DtCms_prev_tick_v4(time_t tick, Period_4 period)
710 {
711         time_t prev;
712         struct tm *tm;
713         _Xltimeparams localtime_buf;
714
715         switch(period.period) {
716                 case weekly_4:
717                         prev = prevnwk_exacttime(tick, 1);
718                         break;
719                 case biweekly_4:
720                         prev = prevnwk_exacttime(tick, 2);
721                         break;
722                 case daily_4:
723                         prev = prevnday_exacttime(tick, 1);
724                         break;
725                 case monthly_4:
726                         prev = prevmonth_exactday(tick);
727                         break;
728                 case yearly_4:
729                         tm = _XLocaltime(&tick, localtime_buf);
730                         if (tm->tm_mday == 29 && tm->tm_mon == 1)
731                                 prev = prevnyear(tick, 4);
732                         else
733                                 prev = prevnyear(tick, 1);
734                         break;
735                 case nthWeekday_4:
736                         prev = prevnthweekday(tick, period.nth);
737                         break;
738                 case everyNthDay_4:
739                         prev = prevnday_exacttime(tick, period.nth);
740                         break;
741                 case everyNthWeek_4:
742                         prev = prevnwk_exacttime(tick, period.nth);
743                         break;
744                 case everyNthMonth_4:
745                         prev = prevnmth_exactday(tick, period.nth);
746                         break;
747                 case monThruFri_4:
748                         prev = prevmonTofri(tick);
749                         break;
750                 case monWedFri_4:
751                         prev = prevmonwedfri(tick);
752                         break;
753                 case tueThur_4:
754                         prev = prevtuethur(tick);
755                         break;
756                 case daysOfWeek_4:
757                         prev = prevdaysofweek(tick, period.nth);
758                         break;
759                 default:
760                         break;
761         }
762         if(prev != tick && timeok(prev)) return(prev);
763         else return(bot-1);
764 }
765
766 /*
767  * dont_care_cancel:
768  *      TRUE - it is a match regard_DtCmsIsLess the event is cancelled, 
769  *      FALSE - it is not a match if the event is cancelled.
770  */
771 extern int
772 _DtCms_in_repeater(Id_4 *key, Appt_4 *p_appt, boolean_t dont_care_cancel)
773 {
774         Period_4 period;
775         int     ordinal;
776         int     ntimes;
777         time_t  tick;
778
779         ntimes = _DtCms_get_ninstance_v4(p_appt);
780         period = p_appt->period;
781         tick = _DtCms_closest_tick_v4(key->tick, p_appt->appt_id.tick, period, &ordinal);
782         ordinal--;
783         while (++ordinal <= ntimes)
784         {
785                 if (tick > key->tick)           /* out-of-bound */
786                         break;
787                 if (tick == key->tick)
788                 {
789                         if (dont_care_cancel)
790                                 return (ordinal);
791                         if (!_DtCms_marked_4_cancellation (p_appt, ordinal))
792                                 return (ordinal);
793                 }
794                 tick = _DtCms_next_tick_v4 (tick, period);
795         }
796
797         return (0);
798 }
799
800 extern int
801 _DtCms_marked_4_cancellation(Appt_4 *a, int i)
802 {
803         Except_4 *p;
804
805         if (a==NULL)
806                 return(0);
807
808         p = a->exception;       /* in descending order for faster access */
809         while (p!=NULL) {
810                 if (i > p->ordinal)
811                         break;
812                 if (i == p->ordinal)
813                         return(1);
814                 p = p->next;
815         }
816         return(0);
817 }
818
819 extern time_t
820 next_ndays(time_t t, int n)
821 {
822         time_t next;
823         struct tm tm;
824         _Xltimeparams localtime_buf;
825
826         tm              = *_XLocaltime(&t, localtime_buf);
827         tm.tm_sec       = 0;
828         tm.tm_min       = 0;
829         tm.tm_hour      = 0;
830
831 #ifdef SVR4
832         next            = mktime(&tm);
833 #else
834         next            = timelocal(&tm);
835 #endif /* SVR4 */
836         next            = next + n * daysec;
837         next            = adjust_dst(t, next);
838         return(next);
839 }
840
841 extern time_t
842 next_nmins(time_t t, int m)
843 {
844         time_t next;
845         struct tm tm;
846         _Xltimeparams localtime_buf;
847
848         tm              = *_XLocaltime(&t, localtime_buf);
849         tm.tm_sec       = 0;
850         tm.tm_min       = 0;
851  
852         next            = mktime(&tm);
853         next            = next + m * minsec;
854         next            = adjust_dst(t, next);
855         return(next);
856 }
857
858 extern time_t
859 _DtCmsBeginOfDay(time_t t)
860 {
861         struct tm tm;
862         _Xltimeparams localtime_buf;
863
864         tm              =  *_XLocaltime(&t, localtime_buf);
865         tm.tm_sec       =  0;
866         tm.tm_min       =  0;
867         tm.tm_hour      =  0;
868         return(mktime(&tm));
869 }
870
871 extern time_t
872 _DtCmsTimeOfDay(time_t t)
873 {
874         struct tm tm;
875         _Xltimeparams localtime_buf;
876
877         tm              =  *_XLocaltime(&t, localtime_buf);
878         tm.tm_sec       =  0;
879         tm.tm_min       =  0;
880         tm.tm_hour      =  0;
881         return(t - mktime(&tm));
882 }
883
884 /*
885  * Given a weekmask, find the last appointment in the week
886  */
887 static int
888 lastapptofweek(u_int mask)
889 {
890         int n;
891
892         if (mask == 0)
893                 return -1;
894
895         for (n = -1; mask != 0; n++, mask = mask >> 1);
896
897         return n;
898 }
899
900 /*
901  * Given a weekmask and the first day of the week, calculate
902  * the number of times outstanding in the week.
903  */
904 static int
905 ntimes_this_week(u_int weekmask, int firstday)
906 {
907         int i, ntimes, weekdaymask = 1 << firstday;
908
909         if (weekmask == 0)
910                 return 0;
911
912         for (i=firstday, ntimes=0; i < 7; i++, weekdaymask <<= 1) {
913                 if (weekdaymask & weekmask)
914                         ntimes++;
915         }
916         return ntimes;
917 }
918
919 static boolean_t
920 nthweekdayofmonth(time_t t, int *nth)
921 {
922         struct tm tm, tm2, tmfirstday;
923         time_t  firstday;
924         _Xltimeparams localtime_buf;
925
926         tmfirstday = tm = *_XLocaltime(&t, localtime_buf);
927
928         *nth = (12 + tm.tm_mday - tm.tm_wday)/7;
929
930         tmfirstday.tm_hour = 0;
931         tmfirstday.tm_min = 0;
932         tmfirstday.tm_sec = 0;
933         tmfirstday.tm_mday = 1;
934         firstday = mktime(&tmfirstday);
935         tmfirstday = *_XLocaltime(&firstday, localtime_buf);
936
937         if (tm.tm_wday < tmfirstday.tm_wday)
938                 (*nth)--;
939
940         if (*nth < 4)
941                 return B_FALSE;
942         else {
943                 t += (7 * daysec);
944                 tm2 = *_XLocaltime(&t, localtime_buf);
945
946                 return((tm.tm_mon == tm2.tm_mon) ? B_FALSE : B_TRUE);
947         }
948 }
949
950 /*
951  * If the result falls beyond the system limit, -1 is returned by mktime().
952  */
953 static time_t
954 next_nmonth(time_t t, int n)
955 {
956         struct tm tm;
957         int     n12;
958         _Xltimeparams localtime_buf;
959
960         n12 = n/12;
961         n = n%12;
962
963         tm = *_XLocaltime(&t, localtime_buf);
964         tm.tm_hour=0;
965         tm.tm_min=0;
966         tm.tm_sec=0;
967         tm.tm_mday=1;
968         if (n12 > 0)
969                 tm.tm_year += n12;
970
971         if ((tm.tm_mon = tm.tm_mon + n) > 11) {
972                 tm.tm_mon -= 12;
973                 tm.tm_year++;
974         }
975
976         tm.tm_isdst = -1;
977         return(mktime(&tm));
978 }
979
980 static int
981 adjust_dst(time_t start, time_t next) 
982 {
983         struct tm oldt;
984         struct tm newt;
985         _Xltimeparams localtime_buf;
986
987         oldt = *_XLocaltime(&start, localtime_buf);
988         newt = *_XLocaltime(&next, localtime_buf);
989
990         if (oldt.tm_isdst == newt.tm_isdst) {
991                 return (next);
992         } else if (oldt.tm_isdst == 1) {
993                 return (next + (int)hrsec);
994         } else {
995                 return (next - (int)hrsec);
996         }
997 }
998
999 static time_t
1000 prev_nmonth(time_t t, int n)
1001 {
1002         struct tm tm;
1003         int     n12;
1004         _Xltimeparams localtime_buf;
1005
1006         n12 = n/12;
1007         n = n%12;
1008
1009         tm = *_XLocaltime(&t, localtime_buf);
1010         tm.tm_hour=0;
1011         tm.tm_min=0;
1012         tm.tm_sec=0;
1013         tm.tm_mday=1;
1014         if (n12 > 0)
1015                 tm.tm_year -= n12;
1016
1017         if ((tm.tm_mon = tm.tm_mon - n) < 0) {
1018                 tm.tm_mon += 12;
1019                 tm.tm_year--;
1020         }
1021 #ifdef SVR4
1022         tm.tm_isdst = -1;
1023         return(mktime(&tm));
1024 #else
1025         return(timelocal(&tm));
1026 #endif /* SVR4 */
1027 }
1028
1029 extern int
1030 leapyr(int y)
1031 {
1032         return
1033          (y % 4 == 0 && y % 100 !=0 || y % 400 == 0);
1034 }
1035
1036 extern int
1037 monthlength(Tick t)
1038 {
1039         int mon;
1040         struct tm tm;
1041         _Xltimeparams localtime_buf;
1042
1043         tm = *_XLocaltime(&t, localtime_buf);
1044         mon = tm.tm_mon;
1045         return(((mon==1) && leapyr(tm.tm_year+1900))? 29 : monthdays[mon]);
1046 }
1047
1048 extern int /* find dow(0-6) that 1st dom falls on */
1049 fdom(Tick t)
1050 {
1051         struct tm tm;
1052         _Xltimeparams localtime_buf;
1053
1054         tm              = *_XLocaltime(&t, localtime_buf);
1055         tm.tm_mday      = 1;
1056         tm.tm_isdst     = -1;
1057         t               = mktime(&tm);
1058         tm              = *_XLocaltime(&t, localtime_buf);
1059         return(tm.tm_wday);
1060 }
1061
1062 extern int
1063 ldom(Tick t /* find dow(0-6) that last dom falls on */ )
1064 {
1065         struct tm tm;
1066         _Xltimeparams localtime_buf;
1067
1068         tm              = *_XLocaltime(&t, localtime_buf);
1069         tm.tm_mday      = monthlength(t);
1070         tm.tm_isdst     = -1;
1071         t               = mktime(&tm);
1072         tm              = *_XLocaltime(&t, localtime_buf);
1073         return(tm.tm_wday);
1074 }
1075
1076 extern boolean_t
1077 _DtCmsInExceptionList(cms_entry *eptr, time_t tick)
1078 {
1079         CSA_date_time_entry     *dt = NULL;
1080         time_t                  time;
1081
1082         if (eptr->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value)
1083                 dt = eptr->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->\
1084                         item.date_time_list_value;
1085
1086         for (; dt != NULL; dt = dt->next) {
1087                 if (_csa_iso8601_to_tick(dt->date_time, &time))
1088                         continue;
1089
1090                 if (time == tick)
1091                         return (B_TRUE);
1092         }
1093
1094         return (B_FALSE);
1095 }
1096
1097 static time_t
1098 nextnyear(time_t t, int n)
1099 {
1100         struct tm tm;
1101         _Xltimeparams localtime_buf;
1102
1103         tm      = *_XLocaltime(&t, localtime_buf);
1104         tm.tm_year += n;
1105 #ifdef SVR4
1106         return(mktime(&tm));
1107 #else
1108         return(timelocal(&tm));
1109 #endif /* SVR4 */
1110 }
1111
1112 static int
1113 timeok(time_t t)
1114 {
1115         int r =((t >= bot) &&(t <= eot));
1116         return(r);
1117 }
1118
1119 static time_t
1120 prevnyear(time_t t, int n)
1121 {
1122         struct tm tm;
1123         _Xltimeparams localtime_buf;
1124
1125         tm = *_XLocaltime(&t, localtime_buf);
1126         tm.tm_year -= n;
1127 #ifdef SVR4
1128         return(mktime(&tm));
1129 #else
1130         return(timelocal(&tm));
1131 #endif /* SVR4 */
1132 }
1133
1134 static time_t
1135 prevmonth_exactday(time_t t)
1136 {
1137         time_t prev; int day;
1138         struct tm tm;
1139         int sdelta;
1140         _Xltimeparams localtime_buf;
1141
1142         tm = *_XLocaltime(&t, localtime_buf);
1143         sdelta = tm.tm_hour * hrsec + tm.tm_min * minsec + tm.tm_sec; 
1144         day = tm.tm_mday;
1145         if((tm.tm_mday < 31 && tm.tm_mon != 0) ||       /* at least 30 days everywhere, except Feb.*/
1146            (tm.tm_mday==31 && tm.tm_mon==6)    ||       /* two 31s -- Jul./Aug.         */
1147            (tm.tm_mday==31 && tm.tm_mon==11)   ||       /* two 31s -- Dec./Jan.         */
1148            (tm.tm_mon == 0 &&(tm.tm_mday < 29  ||(tm.tm_mday==29 && leapyr(tm.tm_year+1900))))) {       
1149                 prev = t-monthseconds(previousmonth(t));
1150                 prev = adjust_dst(t, prev);
1151         }
1152         else {  /* brute force */
1153                 prev = previousmonth(previousmonth(t));         /* hop over the month */
1154                 tm = *_XLocaltime(&prev, localtime_buf);
1155                 tm.tm_mday = day;
1156 #ifdef SVR4
1157                 tm.tm_isdst = -1;
1158                 prev =(mktime(&tm)) + sdelta;
1159 #else
1160                 prev =(timelocal(&tm)) + sdelta;
1161 #endif /* SVR4 */
1162
1163         }
1164         return(prev);
1165 }
1166
1167 static time_t
1168 nextmonth_exactday(time_t t)
1169 {
1170         time_t next; int day;
1171         struct tm tm;
1172         int sdelta;
1173         _Xltimeparams localtime_buf;
1174
1175         tm = *_XLocaltime(&t, localtime_buf);
1176         sdelta = tm.tm_hour * hrsec + tm.tm_min * minsec + tm.tm_sec; 
1177         day = tm.tm_mday;
1178         if((tm.tm_mday < 31 && tm.tm_mon != 0) ||       /* at least 30 days everywhere, except Feb.*/
1179            (tm.tm_mday==31 && tm.tm_mon==6)    ||       /* two 31s -- Jul./Aug.         */
1180            (tm.tm_mday==31 && tm.tm_mon==11)   ||       /* two 31s -- Dec./Jan.         */
1181            (tm.tm_mon == 0 &&(tm.tm_mday < 29  ||(tm.tm_mday==29 && leapyr(tm.tm_year+1900))))) {       
1182                 next = t+monthseconds(t);
1183                 next = adjust_dst(t, next);
1184         }
1185         else {  /* brute force */
1186                 next = next_nmonth(t, 2);               /* hop over the month */
1187                 tm = *_XLocaltime(&next, localtime_buf);
1188                 tm.tm_mday = day;
1189 #ifdef SVR4
1190                 tm.tm_isdst = -1;
1191                 next = mktime(&tm) + sdelta;
1192 #else
1193                 next =(timelocal(&tm)) + sdelta;
1194 #endif /* SVR4 */
1195         }
1196         return(next);
1197 }
1198
1199 static time_t
1200 previousmonth(time_t t)
1201 {
1202         struct tm tm;
1203         _Xltimeparams localtime_buf;
1204
1205         tm = *_XLocaltime(&t, localtime_buf);
1206         tm.tm_hour=0;
1207         tm.tm_min=0;
1208         tm.tm_sec=0;
1209         if(tm.tm_mon==0) {
1210                 tm.tm_mon=11;
1211                 tm.tm_mday=1;
1212                 tm.tm_year--;
1213         }
1214         else {
1215                 tm.tm_mday=1;
1216                 tm.tm_mon--;
1217         }
1218 #ifdef SVR4
1219         tm.tm_isdst = -1;
1220         return(mktime(&tm));
1221 #else
1222         return(timelocal(&tm));
1223 #endif /* SVR4 */
1224 }
1225
1226 static int
1227 monthseconds(time_t t)
1228 {
1229         int mon;
1230         struct tm tm;
1231         _Xltimeparams localtime_buf;
1232         
1233         tm = *_XLocaltime(&t, localtime_buf);
1234         mon = tm.tm_mon;
1235         return(((mon==1) && leapyr(tm.tm_year+1900)) ?
1236                 29*daysec : monthsecs[mon]);
1237 }
1238
1239 /*
1240  * find the number of instances to be subtracted
1241  */
1242 static int
1243 get_ndelta(time_t startdate, Period_4 period, int ntimes)
1244 {
1245         struct tm *tm;
1246         int ndelta = 0;
1247         time_t lastdate;
1248         double dlastdate;
1249         _Xltimeparams localtime_buf;
1250
1251         if (period.enddate == 0)
1252                 return(ndelta);
1253
1254         /* find last day of the series */
1255         dlastdate = startdate + (double)wksec * (ntimes - 1); /* last week */
1256         if (dlastdate > EOT)
1257                 return(ndelta);
1258         else
1259                 lastdate = (time_t)dlastdate;
1260
1261         tm = _XLocaltime(&lastdate, localtime_buf);
1262         if (period.period == monThruFri_4 || period.period == monWedFri_4)
1263                 lastdate = lastdate + daysec * (5 - tm->tm_wday);
1264         else if (period.period == tueThur_4)
1265                 lastdate = lastdate + daysec * (4 - tm->tm_wday);
1266         else if (period.period == daysOfWeek_4)
1267                 lastdate = lastdate + daysec *
1268                         (lastapptofweek((u_int)period.nth) - tm->tm_wday);
1269
1270         if (period.enddate > lastdate)
1271                 return(ndelta);
1272
1273         tm = _XLocaltime(&period.enddate, localtime_buf);
1274         switch (period.period) {
1275         case monThruFri_4:
1276                 ndelta = 5 - tm->tm_wday;
1277                 break;
1278         case monWedFri_4:
1279                 if (tm->tm_wday < 3)
1280                         ndelta = 2;
1281                 else if (tm->tm_wday < 5)
1282                         ndelta = 1;
1283                 break;
1284         case tueThur_4:
1285                 if (tm->tm_wday < 2)
1286                         ndelta = 2;
1287                 else if (tm->tm_wday < 4)
1288                         ndelta = 1;
1289                 break;
1290         case daysOfWeek_4:
1291                 ndelta = ntimes_this_week((u_int)period.nth, tm->tm_wday) - 1;
1292                 break;
1293         }
1294         return(ndelta);
1295 }
1296
1297 static time_t
1298 lastnthweekday(time_t t, int nth, int ntimes)
1299 {
1300         struct tm tm1, tm2;
1301         time_t tick, ntick;
1302         int delta;
1303         int sdelta;
1304         _Xltimeparams localtime_buf;
1305
1306         /*
1307          * if nth is not specified, assume it's the
1308          * 4th week for the ambiguous case.
1309          */
1310         if (nth == 0) {
1311                 nthweekdayofmonth(t, &nth);
1312                 if (nth > 4)
1313                         nth = -1;
1314         }
1315
1316         tm1 = *_XLocaltime(&t, localtime_buf);
1317         sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; 
1318
1319         if (nth > 0) {
1320                 if ((tick = next_nmonth(t, ntimes)) == EOT || tick < 0)
1321                         return(EOT);
1322
1323                 tm2 = *_XLocaltime(&tick, localtime_buf);
1324
1325                 delta = tm1.tm_wday - tm2.tm_wday;
1326                 if (delta < 0)
1327                         delta += 7;
1328
1329                 ntick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta;
1330         } else {
1331                 if ((tick = next_nmonth(t, ntimes + 1)) == EOT || tick < 0)
1332                         return(EOT);
1333
1334                 tm2 = *_XLocaltime(&tick, localtime_buf);
1335
1336                 delta = tm2.tm_wday - tm1.tm_wday;
1337                 if (tm1.tm_wday >= tm2.tm_wday)
1338                         delta += 7;
1339
1340                 ntick = tick - (delta * daysec) + sdelta;
1341         }
1342         ntick = adjust_dst(tick, ntick);
1343
1344         return (ntick);
1345 }
1346
1347 static time_t
1348 nextnthweekday(time_t t, int nth)
1349 {
1350         struct tm tm1, tm2;
1351         time_t tick, ntick;
1352         int delta;
1353         int sdelta;
1354         _Xltimeparams localtime_buf;
1355
1356         /*
1357          * if nth is not specified, assume it's the
1358          * 4th week for the ambiguous case.
1359          */
1360         if (nth == 0) {
1361                 nthweekdayofmonth(t, &nth);
1362                 if (nth > 4)
1363                         nth = -1;
1364         }
1365
1366         tm1 = *_XLocaltime(&t, localtime_buf);
1367         sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; 
1368
1369         if (nth > 0) {
1370                 tick = next_nmonth(t, 1);
1371                 tm2 = *_XLocaltime(&tick, localtime_buf);
1372
1373                 delta = tm1.tm_wday - tm2.tm_wday;
1374                 if (delta < 0)
1375                         delta += 7;
1376
1377                 ntick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta;
1378         } else {
1379                 tick = next_nmonth(t, 2);
1380                 tm2 = *_XLocaltime(&tick, localtime_buf);
1381
1382                 delta = tm2.tm_wday - tm1.tm_wday;
1383                 if (tm1.tm_wday >= tm2.tm_wday)
1384                         delta += 7;
1385
1386                 ntick = tick - (delta * daysec) + sdelta;
1387         }
1388         ntick = adjust_dst(tick, ntick);
1389
1390         return (ntick);
1391 }
1392
1393 static time_t
1394 prevnthweekday(time_t t, int nth)
1395 {
1396         struct tm tm1, tm2;
1397         time_t tick, ptick;
1398         int delta;
1399         int sdelta;
1400         _Xltimeparams localtime_buf;
1401
1402         /*
1403          * if nth is not specified, assume it's the
1404          * 4th week for the ambiguous case.
1405          */
1406         if (nth == 0) {
1407                 nthweekdayofmonth(t, &nth);
1408                 if (nth > 4)
1409                         nth = -1;
1410         }
1411
1412         tm1 = *_XLocaltime(&t, localtime_buf);
1413         sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; 
1414
1415         if (nth > 0) {
1416                 tick = prev_nmonth(t, 1);
1417                 tm2 = *_XLocaltime(&tick, localtime_buf);
1418
1419                 delta = tm1.tm_wday - tm2.tm_wday;
1420                 if (delta < 0)
1421                         delta += 7;
1422
1423                 ptick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta;
1424         } else {
1425                 tick = prev_nmonth(next_nmonth(t, 1), 1);
1426                 tm2 = *_XLocaltime(&tick, localtime_buf);
1427
1428                 delta = tm2.tm_wday - tm1.tm_wday;
1429                 if (tm1.tm_wday >= tm2.tm_wday)
1430                         delta += 7;
1431
1432                 ptick = tick - (delta * daysec) + sdelta;
1433         }
1434         ptick = adjust_dst(tick, ptick);
1435
1436         return (ptick);
1437 }
1438
1439 /* use double in this routine to avoid integer overflow
1440  * in case n is very large.
1441  */
1442 static time_t
1443 nextnday_exacttime(time_t t, int n)
1444 {
1445         double next;
1446
1447         next    = t + (double)n * daysec;
1448         if (next >= EOT || next < 0)
1449                 return(EOT);
1450         else {
1451                 next = adjust_dst(t, (time_t)next);
1452                 return((time_t)next);
1453         }
1454 }
1455
1456 /*
1457  * This is defined in the private library and is used also by the front
1458  * end -- should it be here?
1459  */
1460 static time_t
1461 prevnday_exacttime(time_t t, int n)
1462 {
1463         time_t prev;
1464
1465         prev    = t - (n * daysec);
1466         prev    = adjust_dst(t, prev);
1467         return(prev);
1468 }
1469
1470 /* use double in this routine to avoid integer overflow
1471  * in case n is very large.
1472  */
1473 static time_t
1474 nextnwk_exacttime(time_t t, int n)
1475 {
1476         double next;
1477
1478         next    = t + (double)n * 7 * daysec;
1479         if (next >= EOT || next < 0)
1480                 return(EOT);
1481         else {
1482                 next = adjust_dst(t, (time_t)next);
1483                 return((time_t)next);
1484         }
1485 }
1486
1487 static time_t
1488 prevnwk_exacttime(time_t t, int n)
1489 {
1490         time_t prev;
1491
1492         prev    = t - n * 7 * daysec;
1493         prev    = adjust_dst(t, prev);
1494         return(prev);
1495 }
1496
1497 static time_t
1498 nextnmth_exactday(time_t t, int n)
1499 {
1500         struct tm tm1, tm2;
1501         boolean_t done = B_FALSE;
1502         time_t next;
1503         _Xltimeparams localtime_buf;
1504
1505         tm1 = *_XLocaltime(&t, localtime_buf);
1506         while (!done) {
1507                 if ((next = next_nmonth(t, n)) == EOT || next < 0)
1508                         return(EOT);
1509
1510                 tm2 = *_XLocaltime(&next, localtime_buf);
1511
1512                 /* 1. at least 30 days except feb
1513                  * 2. 2/29 on leap year
1514                  * 3. 31st on the appropriate month
1515                  */
1516                 if ((tm1.tm_mday < 31 && tm2.tm_mon != 1) ||
1517                     (tm2.tm_mon == 1 && (tm1.tm_mday < 29 ||
1518                       (tm1.tm_mday == 29 && leapyr(tm2.tm_year + 1900)))) ||
1519                     (tm1.tm_mday == 31 && ((tm2.tm_mon > 6 && tm2.tm_mon % 2) ||
1520                       ((tm2.tm_mon <= 6 && (tm2.tm_mon % 2 == 0)))))) {
1521                         tm2.tm_sec = tm1.tm_sec;
1522                         tm2.tm_min = tm1.tm_min;
1523                         tm2.tm_hour = tm1.tm_hour;
1524                         tm2.tm_mday = tm1.tm_mday;
1525                         done = B_TRUE;
1526                 } else
1527                         t = next;
1528         }
1529
1530 #ifdef SVR4
1531         tm2.tm_isdst = -1;
1532         next = mktime(&tm2);
1533 #else
1534         next = (timelocal(&tm2));
1535 #endif
1536         return(next);
1537 }
1538
1539 static time_t
1540 prevnmth_exactday(time_t t, int n)
1541 {
1542         struct tm tm1, tm2;
1543         boolean_t done = B_FALSE;
1544         time_t prev;
1545         _Xltimeparams localtime_buf;
1546
1547         tm1 = *_XLocaltime(&t, localtime_buf);
1548         while (!done) {
1549                 prev = prev_nmonth(t, n);
1550                 tm2 = *_XLocaltime(&prev, localtime_buf);
1551
1552                 if ((tm1.tm_mday < 30 && tm2.tm_mon != 1) ||
1553                     (tm2.tm_mon == 1 && (tm1.tm_mday < 29 ||
1554                       (tm1.tm_mday == 29 && leapyr(tm2.tm_year + 1900)))) ||
1555                     (tm1.tm_mday == 31 && ((tm2.tm_mon > 6 && tm2.tm_mon % 2) ||
1556                       ((tm2.tm_mon <= 6 && (tm2.tm_mon % 2 == 0)))))) {
1557                         tm2.tm_sec = tm1.tm_sec;
1558                         tm2.tm_min = tm1.tm_min;
1559                         tm2.tm_hour = tm1.tm_hour;
1560                         tm2.tm_mday = tm1.tm_mday;
1561                         done = B_TRUE;
1562                 } else
1563                         t = prev;
1564         }
1565
1566 #ifdef SVR4
1567         tm2.tm_isdst = -1;
1568         prev = mktime(&tm2);
1569 #else
1570         prev = (timelocal(&tm2));
1571 #endif
1572         return(prev);
1573 }
1574
1575 static time_t
1576 nextmonTofri(time_t t)
1577 {
1578         struct tm *tm;
1579         time_t next;
1580         _Xltimeparams localtime_buf;
1581
1582         tm = _XLocaltime(&t, localtime_buf);
1583
1584         if (tm->tm_wday < 5)
1585                 next = t + (int)daysec;
1586         else
1587                 next = t + (int)daysec * (8 - tm->tm_wday);
1588
1589         next = adjust_dst(t, next);
1590         return(next);
1591 }
1592
1593 static time_t
1594 prevmonTofri(time_t t)
1595 {
1596         struct tm *tm;
1597         time_t prev;
1598         _Xltimeparams localtime_buf;
1599
1600         tm = _XLocaltime(&t, localtime_buf);
1601
1602         if (tm->tm_wday > 1)
1603                 prev = t - (int)daysec;
1604         else
1605                 prev = t - (int)daysec * (2 + tm->tm_wday);
1606
1607         prev = adjust_dst(t, prev);
1608         return(prev);
1609 }
1610
1611 static time_t
1612 nextmonwedfri(time_t t)
1613 {
1614         struct tm *tm;
1615         time_t next;
1616         _Xltimeparams localtime_buf;
1617
1618         tm = _XLocaltime(&t, localtime_buf);
1619
1620         if (tm->tm_wday == 5)
1621                 next = t + (int)daysec * 3;
1622         else if (tm->tm_wday % 2 || tm->tm_wday == 6)
1623                 next = t + (int)daysec * 2;
1624         else
1625                 next = t + (int)daysec;
1626
1627         next = adjust_dst(t, next);
1628         return(next);
1629 }
1630
1631 static time_t
1632 prevmonwedfri(time_t t)
1633 {
1634         struct tm *tm;
1635         time_t prev;
1636         _Xltimeparams localtime_buf;
1637
1638         tm = _XLocaltime(&t, localtime_buf);
1639
1640         if (tm->tm_wday == 1)
1641                 prev = t - (int)daysec * 3;
1642         else if (tm->tm_wday % 2 || tm->tm_wday == 0)
1643                 prev = t - (int)daysec * 2;
1644         else
1645                 prev = t - (int)daysec;
1646
1647         prev = adjust_dst(t, prev);
1648         return(prev);
1649 }
1650
1651 static time_t
1652 nexttuethur(time_t t)
1653 {
1654         struct tm *tm;
1655         time_t next;
1656         _Xltimeparams localtime_buf;
1657
1658         tm = _XLocaltime(&t, localtime_buf);
1659
1660         if (tm->tm_wday < 4) {
1661                 if (tm->tm_wday % 2)
1662                         next = t + (int)daysec;
1663                 else
1664                         next = t + (int)daysec * 2;
1665         } else
1666                 next = t + (int)daysec * (9 - tm->tm_wday);
1667
1668         next = adjust_dst(t, next);
1669         return(next);
1670 }
1671
1672 static time_t
1673 prevtuethur(time_t t)
1674 {
1675         struct tm *tm;
1676         time_t prev;
1677         _Xltimeparams localtime_buf;
1678
1679         tm = _XLocaltime(&t, localtime_buf);
1680
1681         if (tm->tm_wday > 2) {
1682                 if (tm->tm_wday % 2)
1683                         prev = t - (int)daysec;
1684                 else
1685                         prev = t - (int)daysec * 2;
1686         } else
1687                 prev = t - (int)daysec * (3 + tm->tm_wday);
1688
1689         prev = adjust_dst(t, prev);
1690         return(prev);
1691 }
1692
1693 /*
1694  * the 7-bit mask should be put in the last 7 bits of the int
1695  */
1696 static time_t
1697 nextdaysofweek(time_t t, int weekmask)
1698 {
1699         unsigned int doublemask;
1700         struct tm *tm; 
1701         int i, ndays, daymask;
1702         time_t next;
1703         _Xltimeparams localtime_buf;
1704
1705         doublemask = weekmask | (weekmask << 7);
1706         tm = _XLocaltime(&t, localtime_buf);
1707         daymask = weekdaymasks[tm->tm_wday] << 1;
1708
1709         for (i = 0, ndays = 1; i < 7; i++) {
1710                 if (daymask & doublemask)
1711                         break;
1712                 else {
1713                         ndays++;
1714                         doublemask >>= 1;
1715                 }
1716         }
1717
1718         next = t + (int)daysec * ndays;
1719         next = adjust_dst(t, next);
1720         return(next);
1721 }
1722
1723 static time_t
1724 prevdaysofweek(time_t t, int weekmask)
1725 {
1726         unsigned int doublemask, daymask;
1727         struct tm *tm; 
1728         int i, ndays;
1729         time_t prev;
1730         _Xltimeparams localtime_buf;
1731
1732         doublemask = weekmask | (weekmask << 7);
1733         tm = _XLocaltime(&t, localtime_buf);
1734         daymask = weekdaymasks[tm->tm_wday] << 6; 
1735
1736         for (i = 0, ndays = 1; i < 7; i++) {
1737                 if (daymask & doublemask)
1738                         break;
1739                 else {
1740                         ndays++;
1741                         doublemask <<= 1;
1742                 }
1743         }
1744
1745         prev = t - (int)daysec * ndays;
1746         prev = adjust_dst(t, prev);
1747         return(prev);
1748 }
1749