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