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