ptimes.c (398faf12050b17c407eb9db71cb67166cdb43aff) ptimes.c (2f8d7c56da1ce2c5069fab78876cc6a90b343cfa)
1/*-
2 * ------+---------+---------+---------+---------+---------+---------+---------*
3 * Initial version of parse8601 was originally added to newsyslog.c in
4 * FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>.
5 * Initial version of parseDWM was originally added to newsyslog.c in
6 * FreeBSD on Apr 4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>.
7 *
8 * Copyright (c) 2003 - Garance Alistair Drosehn <gad@FreeBSD.org>.

--- 20 unchanged lines hidden (view full) ---

29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * The views and conclusions contained in the software and documentation
33 * are those of the authors and should not be interpreted as representing
34 * official policies, either expressed or implied, of the FreeBSD Project.
35 *
36 * ------+---------+---------+---------+---------+---------+---------+---------*
1/*-
2 * ------+---------+---------+---------+---------+---------+---------+---------*
3 * Initial version of parse8601 was originally added to newsyslog.c in
4 * FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>.
5 * Initial version of parseDWM was originally added to newsyslog.c in
6 * FreeBSD on Apr 4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>.
7 *
8 * Copyright (c) 2003 - Garance Alistair Drosehn <gad@FreeBSD.org>.

--- 20 unchanged lines hidden (view full) ---

29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * The views and conclusions contained in the software and documentation
33 * are those of the authors and should not be interpreted as representing
34 * official policies, either expressed or implied, of the FreeBSD Project.
35 *
36 * ------+---------+---------+---------+---------+---------+---------+---------*
37 * This is intended to be a set of general-purpose routines to process times.
38 * Right now it probably still has a number of assumptions in it, such that
39 * it works fine for newsyslog but might not work for other uses.
40 * ------+---------+---------+---------+---------+---------+---------+---------*
41 */
42
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD$");
45
46#include <ctype.h>
47#include <limits.h>
48#include <stdio.h>
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD$");
41
42#include <ctype.h>
43#include <limits.h>
44#include <stdio.h>
45#include <stdint.h>
49#include <stdlib.h>
46#include <stdlib.h>
50#include <string.h>
51#include <time.h>
52
53#include "extern.h"
54
47#include <time.h>
48
49#include "extern.h"
50
55#define SECS_PER_HOUR 3600
56
57/*
58 * Bit-values which indicate which components of time were specified
59 * by the string given to parse8601 or parseDWM. These are needed to
60 * calculate what time-in-the-future will match that string.
61 */
62#define TSPEC_YEAR 0x0001
63#define TSPEC_MONTHOFYEAR 0x0002
64#define TSPEC_LDAYOFMONTH 0x0004
65#define TSPEC_DAYOFMONTH 0x0008
66#define TSPEC_DAYOFWEEK 0x0010
67#define TSPEC_HOUROFDAY 0x0020
68
69#define TNYET_ADJ4DST -10 /* DST has "not yet" been adjusted */
70
71struct ptime_data {
72 time_t basesecs; /* Base point for relative times */
73 time_t tsecs; /* Time in seconds */
74 struct tm basetm; /* Base Time expanded into fields */
75 struct tm tm; /* Time expanded into fields */
76 int did_adj4dst; /* Track calls to ptime_adjust4dst */
77 int parseopts; /* Options given for parsing */
78 int tmspec; /* Indicates which time fields had
79 * been specified by the user */
80};
81
82static int days_pmonth(int month, int year);
83static int parse8601(struct ptime_data *ptime, const char *str);
84static int parseDWM(struct ptime_data *ptime, const char *str);
85
86/*
87 * Simple routine to calculate the number of days in a given month.
88 */
89static int
90days_pmonth(int month, int year)
91{
92 static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31,
93 30, 31, 30, 31};
94 int ndays;
95
96 ndays = mtab[month];
97
98 if (month == 1) {
99 /*
100 * We are usually called with a 'tm-year' value
101 * (ie, the value = the number of years past 1900).
102 */
103 if (year < 1900)
104 year += 1900;
105 if (year % 4 == 0) {
106 /*
107 * This is a leap year, as long as it is not a
108 * multiple of 100, or if it is a multiple of
109 * both 100 and 400.
110 */
111 if (year % 100 != 0)
112 ndays++; /* not multiple of 100 */
113 else if (year % 400 == 0)
114 ndays++; /* is multiple of 100 and 400 */
115 }
116 }
117 return (ndays);
118}
119
120/*-
121 * Parse a limited subset of ISO 8601. The specific format is as follows:
122 *
123 * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter)
124 *
125 * We don't accept a timezone specification; missing fields (including timezone)
126 * are defaulted to the current date but time zero.
127 */
51/*-
52 * Parse a limited subset of ISO 8601. The specific format is as follows:
53 *
54 * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter)
55 *
56 * We don't accept a timezone specification; missing fields (including timezone)
57 * are defaulted to the current date but time zero.
58 */
128static int
129parse8601(struct ptime_data *ptime, const char *s)
59time_t
60parse8601(const char *s, time_t *next_time)
130{
131 char *t;
61{
62 char *t;
63 time_t tsecs;
64 struct tm tm, *tmp;
132 long l;
65 long l;
133 struct tm tm;
134
66
67 tmp = localtime(&timenow);
68 tm = *tmp;
69 if (next_time != NULL)
70 *next_time = (time_t)-1;
71
72 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
73
135 l = strtol(s, &t, 10);
136 if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T'))
137 return (-1);
138
139 /*
140 * Now t points either to the end of the string (if no time was
141 * provided) or to the letter `T' which separates date and time in
142 * ISO 8601. The pointer arithmetic is the same for either case.
143 */
74 l = strtol(s, &t, 10);
75 if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T'))
76 return (-1);
77
78 /*
79 * Now t points either to the end of the string (if no time was
80 * provided) or to the letter `T' which separates date and time in
81 * ISO 8601. The pointer arithmetic is the same for either case.
82 */
144 tm = ptime->tm;
145 ptime->tmspec = TSPEC_HOUROFDAY;
146 switch (t - s) {
147 case 8:
148 tm.tm_year = ((l / 1000000) - 19) * 100;
149 l = l % 1000000;
150 case 6:
83 switch (t - s) {
84 case 8:
85 tm.tm_year = ((l / 1000000) - 19) * 100;
86 l = l % 1000000;
87 case 6:
151 ptime->tmspec |= TSPEC_YEAR;
152 tm.tm_year -= tm.tm_year % 100;
153 tm.tm_year += l / 10000;
154 l = l % 10000;
155 case 4:
88 tm.tm_year -= tm.tm_year % 100;
89 tm.tm_year += l / 10000;
90 l = l % 10000;
91 case 4:
156 ptime->tmspec |= TSPEC_MONTHOFYEAR;
157 tm.tm_mon = (l / 100) - 1;
158 l = l % 100;
159 case 2:
92 tm.tm_mon = (l / 100) - 1;
93 l = l % 100;
94 case 2:
160 ptime->tmspec |= TSPEC_DAYOFMONTH;
161 tm.tm_mday = l;
162 case 0:
163 break;
164 default:
165 return (-1);
166 }
167
168 /* sanity check */

--- 10 unchanged lines hidden (view full) ---

179 switch (t - s) {
180 case 6:
181 tm.tm_sec = l % 100;
182 l /= 100;
183 case 4:
184 tm.tm_min = l % 100;
185 l /= 100;
186 case 2:
95 tm.tm_mday = l;
96 case 0:
97 break;
98 default:
99 return (-1);
100 }
101
102 /* sanity check */

--- 10 unchanged lines hidden (view full) ---

113 switch (t - s) {
114 case 6:
115 tm.tm_sec = l % 100;
116 l /= 100;
117 case 4:
118 tm.tm_min = l % 100;
119 l /= 100;
120 case 2:
187 ptime->tmspec |= TSPEC_HOUROFDAY;
188 tm.tm_hour = l;
189 case 0:
190 break;
191 default:
192 return (-1);
193 }
194
195 /* sanity check */
196 if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0
197 || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23)
198 return (-1);
199 }
200
121 tm.tm_hour = l;
122 case 0:
123 break;
124 default:
125 return (-1);
126 }
127
128 /* sanity check */
129 if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0
130 || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23)
131 return (-1);
132 }
133
201 ptime->tm = tm;
202 return (0);
134 tsecs = mktime(&tm);
135 /*
136 * Check for invalid times, including things like the missing
137 * hour when switching from "standard time" to "daylight saving".
138 */
139 if (tsecs == (time_t)-1)
140 tsecs = (time_t)-2;
141 return (tsecs);
203}
204
205/*-
206 * Parse a cyclic time specification, the format is as follows:
207 *
208 * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]
209 *
210 * to rotate a logfile cyclic at
211 *
212 * - every day (D) within a specific hour (hh) (hh = 0...23)
213 * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday)
214 * - once a month (M) at a specific day (d) (d = 1..31,l|L)
215 *
216 * We don't accept a timezone specification; missing fields
217 * are defaulted to the current date but time zero.
218 */
142}
143
144/*-
145 * Parse a cyclic time specification, the format is as follows:
146 *
147 * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]
148 *
149 * to rotate a logfile cyclic at
150 *
151 * - every day (D) within a specific hour (hh) (hh = 0...23)
152 * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday)
153 * - once a month (M) at a specific day (d) (d = 1..31,l|L)
154 *
155 * We don't accept a timezone specification; missing fields
156 * are defaulted to the current date but time zero.
157 */
219static int
220parseDWM(struct ptime_data *ptime, const char *s)
158time_t
159parseDWM(char *s, time_t *next_time)
221{
160{
222 int daysmon, Dseen, WMseen;
223 const char *endval;
224 char *tmp;
161 char *t;
162 time_t tsecs;
163 struct tm tm, *tmp;
225 long l;
164 long l;
226 struct tm tm;
165 int nd;
166 static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
167 int WMseen = 0;
168 int Dseen = 0;
227
169
228 /* Save away the number of days in this month */
229 tm = ptime->tm;
230 daysmon = days_pmonth(tm.tm_mon, tm.tm_year);
170 tmp = localtime(&timenow);
171 tm = *tmp;
172 if (next_time != NULL)
173 *next_time = (time_t)-1;
231
174
232 WMseen = Dseen = 0;
233 ptime->tmspec = TSPEC_HOUROFDAY;
175 /* set no. of days per month */
176
177 nd = mtab[tm.tm_mon];
178
179 if (tm.tm_mon == 1) {
180 if (((tm.tm_year + 1900) % 4 == 0) &&
181 ((tm.tm_year + 1900) % 100 != 0) &&
182 ((tm.tm_year + 1900) % 400 == 0)) {
183 nd++; /* leap year, 29 days in february */
184 }
185 }
186 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
187
234 for (;;) {
188 for (;;) {
235 endval = NULL;
236 switch (*s) {
237 case 'D':
238 if (Dseen)
239 return (-1);
240 Dseen++;
189 switch (*s) {
190 case 'D':
191 if (Dseen)
192 return (-1);
193 Dseen++;
241 ptime->tmspec |= TSPEC_HOUROFDAY;
242 s++;
194 s++;
243 l = strtol(s, &tmp, 10);
195 l = strtol(s, &t, 10);
244 if (l < 0 || l > 23)
245 return (-1);
196 if (l < 0 || l > 23)
197 return (-1);
246 endval = tmp;
247 tm.tm_hour = l;
248 break;
249
250 case 'W':
251 if (WMseen)
252 return (-1);
253 WMseen++;
198 tm.tm_hour = l;
199 break;
200
201 case 'W':
202 if (WMseen)
203 return (-1);
204 WMseen++;
254 ptime->tmspec |= TSPEC_DAYOFWEEK;
255 s++;
205 s++;
256 l = strtol(s, &tmp, 10);
206 l = strtol(s, &t, 10);
257 if (l < 0 || l > 6)
258 return (-1);
207 if (l < 0 || l > 6)
208 return (-1);
259 endval = tmp;
260 if (l != tm.tm_wday) {
261 int save;
262
263 if (l < tm.tm_wday) {
264 save = 6 - tm.tm_wday;
265 save += (l + 1);
266 } else {
267 save = l - tm.tm_wday;
268 }
269
270 tm.tm_mday += save;
271
209 if (l != tm.tm_wday) {
210 int save;
211
212 if (l < tm.tm_wday) {
213 save = 6 - tm.tm_wday;
214 save += (l + 1);
215 } else {
216 save = l - tm.tm_wday;
217 }
218
219 tm.tm_mday += save;
220
272 if (tm.tm_mday > daysmon) {
221 if (tm.tm_mday > nd) {
273 tm.tm_mon++;
222 tm.tm_mon++;
274 tm.tm_mday = tm.tm_mday - daysmon;
223 tm.tm_mday = tm.tm_mday - nd;
275 }
276 }
277 break;
278
279 case 'M':
280 if (WMseen)
281 return (-1);
282 WMseen++;
224 }
225 }
226 break;
227
228 case 'M':
229 if (WMseen)
230 return (-1);
231 WMseen++;
283 ptime->tmspec |= TSPEC_DAYOFMONTH;
284 s++;
285 if (tolower(*s) == 'l') {
232 s++;
233 if (tolower(*s) == 'l') {
286 /* User wants the last day of the month. */
287 ptime->tmspec |= TSPEC_LDAYOFMONTH;
288 tm.tm_mday = daysmon;
289 endval = s + 1;
234 tm.tm_mday = nd;
235 s++;
236 t = s;
290 } else {
237 } else {
291 l = strtol(s, &tmp, 10);
238 l = strtol(s, &t, 10);
292 if (l < 1 || l > 31)
293 return (-1);
294
239 if (l < 1 || l > 31)
240 return (-1);
241
295 if (l > daysmon)
242 if (l > nd)
296 return (-1);
243 return (-1);
297 endval = tmp;
298 tm.tm_mday = l;
299 }
300 break;
301
302 default:
303 return (-1);
304 break;
305 }
306
244 tm.tm_mday = l;
245 }
246 break;
247
248 default:
249 return (-1);
250 break;
251 }
252
307 if (endval == NULL)
308 return (-1);
309 else if (*endval == '\0' || isspace(*endval))
253 if (*t == '\0' || isspace(*t))
310 break;
311 else
254 break;
255 else
312 s = endval;
256 s = t;
313 }
314
257 }
258
315 ptime->tm = tm;
316 return (0);
317}
318
319/*
320 * Initialize a new ptime-related data area.
321 */
322struct ptime_data *
323ptime_init(const struct ptime_data *optsrc)
324{
325 struct ptime_data *newdata;
326
327 newdata = malloc(sizeof(struct ptime_data));
328 if (optsrc != NULL) {
329 memcpy(newdata, optsrc, sizeof(struct ptime_data));
330 } else {
331 memset(newdata, '\0', sizeof(struct ptime_data));
332 newdata->did_adj4dst = TNYET_ADJ4DST;
333 }
334
335 return (newdata);
336}
337
338/*
339 * Adjust a given time if that time is in a different timezone than
340 * some other time.
341 */
342int
343ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc)
344{
345 struct ptime_data adjtime;
346
347 if (ptime == NULL)
348 return (-1);
349
259 tsecs = mktime(&tm);
350 /*
260 /*
351 * Changes are not made to the given time until after all
352 * of the calculations have been successful.
261 * Check for invalid times, including things like the missing
262 * hour when switching from "standard time" to "daylight saving".
353 */
263 */
354 adjtime = *ptime;
355
356 /* Check to see if this adjustment was already made */
357 if ((adjtime.did_adj4dst != TNYET_ADJ4DST) &&
358 (adjtime.did_adj4dst == dstsrc->tm.tm_isdst))
359 return (0); /* yes, so don't make it twice */
360
361 /* See if daylight-saving has changed between the two times. */
362 if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) {
363 if (adjtime.tm.tm_isdst == 1)
364 adjtime.tsecs -= SECS_PER_HOUR;
365 else if (adjtime.tm.tm_isdst == 0)
366 adjtime.tsecs += SECS_PER_HOUR;
367 adjtime.tm = *(localtime(&adjtime.tsecs));
368 /* Remember that this adjustment has been made */
369 adjtime.did_adj4dst = dstsrc->tm.tm_isdst;
370 /*
371 * XXX - Should probably check to see if changing the
372 * hour also changed the value of is_dst. What
373 * should we do in that case?
374 */
375 }
376
377 *ptime = adjtime;
378 return (0);
264 if (tsecs == (time_t)-1)
265 tsecs = (time_t)-2;
266 return (tsecs);
379}
267}
380
381int
382ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime,
383 const char *str)
384{
385 int dpm, pres;
386 struct tm temp_tm;
387
388 ptime->parseopts = parseopts;
389 ptime->basesecs = basetime;
390 ptime->basetm = *(localtime(&ptime->basesecs));
391 ptime->tm = ptime->basetm;
392 ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0;
393
394 /*
395 * Call a routine which sets ptime.tm and ptime.tspecs based
396 * on the given string and parsing-options. Note that the
397 * routine should not call mktime to set ptime.tsecs.
398 */
399 if (parseopts & PTM_PARSE_DWM)
400 pres = parseDWM(ptime, str);
401 else
402 pres = parse8601(ptime, str);
403 if (pres < 0) {
404 ptime->tsecs = (time_t)pres;
405 return (pres);
406 }
407
408 /*
409 * Before calling mktime, check to see if we ended up with a
410 * "day-of-month" that does not exist in the selected month.
411 * If we did call mktime with that info, then mktime will
412 * make it look like the user specifically requested a day
413 * in the following month (eg: Feb 31 turns into Mar 3rd).
414 */
415 dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
416 if ((parseopts & PTM_PARSE_MATCHDOM) &&
417 (ptime->tmspec & TSPEC_DAYOFMONTH) &&
418 (ptime->tm.tm_mday> dpm)) {
419 /*
420 * ptime_nxtime() will want a ptime->tsecs value,
421 * but we need to avoid mktime resetting all the
422 * ptime->tm values.
423 */
424 if (verbose && dbg_at_times > 1)
425 fprintf(stderr,
426 "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)",
427 ptime->tm.tm_year, ptime->tm.tm_mon,
428 ptime->tm.tm_mday, ptime->tm.tm_hour,
429 ptime->tm.tm_min, dpm);
430 temp_tm = ptime->tm;
431 ptime->tsecs = mktime(&temp_tm);
432 if (ptime->tsecs > (time_t)-1)
433 ptimeset_nxtime(ptime);
434 if (verbose && dbg_at_times > 1)
435 fprintf(stderr,
436 " to: %4d/%02d/%02d %02d:%02d\n",
437 ptime->tm.tm_year, ptime->tm.tm_mon,
438 ptime->tm.tm_mday, ptime->tm.tm_hour,
439 ptime->tm.tm_min);
440 }
441
442 /*
443 * Convert the ptime.tm into standard time_t seconds. Check
444 * for invalid times, which includes things like the hour lost
445 * when switching from "standard time" to "daylight saving".
446 */
447 ptime->tsecs = mktime(&ptime->tm);
448 if (ptime->tsecs == (time_t)-1) {
449 ptime->tsecs = (time_t)-2;
450 return (-2);
451 }
452
453 return (0);
454}
455
456int
457ptime_free(struct ptime_data *ptime)
458{
459
460 if (ptime == NULL)
461 return (-1);
462
463 free(ptime);
464 return (0);
465}
466
467/*
468 * Some trivial routines so ptime_data can remain a completely
469 * opaque type.
470 */
471const char *
472ptimeget_ctime(const struct ptime_data *ptime)
473{
474
475 if (ptime == NULL)
476 return ("Null time in ptimeget_ctime()\n");
477
478 return (ctime(&ptime->tsecs));
479}
480
481double
482ptimeget_diff(const struct ptime_data *minuend, const struct
483 ptime_data *subtrahend)
484{
485
486 /* Just like difftime(), we have no good error-return */
487 if (minuend == NULL || subtrahend == NULL)
488 return (0.0);
489
490 return (difftime(minuend->tsecs, subtrahend->tsecs));
491}
492
493time_t
494ptimeget_secs(const struct ptime_data *ptime)
495{
496
497 if (ptime == NULL)
498 return (-1);
499
500 return (ptime->tsecs);
501}
502
503/*
504 * Generate an approximate timestamp for the next event, based on
505 * what parts of time were specified by the original parameter to
506 * ptime_relparse(). The result may be -1 if there is no obvious
507 * "next time" which will work.
508 */
509int
510ptimeset_nxtime(struct ptime_data *ptime)
511{
512 int moredays, tdpm, tmon, tyear;
513 struct ptime_data nextmatch;
514
515 if (ptime == NULL)
516 return (-1);
517
518 /*
519 * Changes are not made to the given time until after all
520 * of the calculations have been successful.
521 */
522 nextmatch = *ptime;
523 /*
524 * If the user specified a year and we're already past that
525 * time, then there will never be another one!
526 */
527 if (ptime->tmspec & TSPEC_YEAR)
528 return (-1);
529
530 /*
531 * The caller gave us a time in the past. Calculate how much
532 * time is needed to go from that valid rotate time to the
533 * next valid rotate time. We only need to get to the nearest
534 * hour, because newsyslog is only run once per hour.
535 */
536 moredays = 0;
537 if (ptime->tmspec & TSPEC_MONTHOFYEAR) {
538 /* Special case: Feb 29th does not happen every year. */
539 if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) {
540 nextmatch.tm.tm_year += 4;
541 if (days_pmonth(1, nextmatch.tm.tm_year) < 29)
542 nextmatch.tm.tm_year += 4;
543 } else {
544 nextmatch.tm.tm_year += 1;
545 }
546 nextmatch.tm.tm_isdst = -1;
547 nextmatch.tsecs = mktime(&nextmatch.tm);
548
549 } else if (ptime->tmspec & TSPEC_LDAYOFMONTH) {
550 /*
551 * Need to get to the last day of next month. Origtm is
552 * already at the last day of this month, so just add to
553 * it number of days in the next month.
554 */
555 if (ptime->tm.tm_mon < 11)
556 moredays = days_pmonth(ptime->tm.tm_mon + 1,
557 ptime->tm.tm_year);
558 else
559 moredays = days_pmonth(0, ptime->tm.tm_year + 1);
560
561 } else if (ptime->tmspec & TSPEC_DAYOFMONTH) {
562 /* Jump to the same day in the next month */
563 moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
564 /*
565 * In some cases, the next month may not *have* the
566 * desired day-of-the-month. If that happens, then
567 * move to the next month that does have enough days.
568 */
569 tmon = ptime->tm.tm_mon;
570 tyear = ptime->tm.tm_year;
571 for (;;) {
572 if (tmon < 11)
573 tmon += 1;
574 else {
575 tmon = 0;
576 tyear += 1;
577 }
578 tdpm = days_pmonth(tmon, tyear);
579 if (tdpm >= ptime->tm.tm_mday)
580 break;
581 moredays += tdpm;
582 }
583
584 } else if (ptime->tmspec & TSPEC_DAYOFWEEK) {
585 moredays = 7;
586 } else if (ptime->tmspec & TSPEC_HOUROFDAY) {
587 moredays = 1;
588 }
589
590 if (moredays != 0) {
591 nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays;
592 nextmatch.tm = *(localtime(&nextmatch.tsecs));
593 }
594
595 /*
596 * The new time will need to be adjusted if the setting of
597 * daylight-saving has changed between the two times.
598 */
599 ptime_adjust4dst(&nextmatch, ptime);
600
601 /* Everything worked. Update the given time and return. */
602 *ptime = nextmatch;
603 return (0);
604}
605
606int
607ptimeset_time(struct ptime_data *ptime, time_t secs)
608{
609
610 if (ptime == NULL)
611 return (-1);
612
613 ptime->tsecs = secs;
614 ptime->tm = *(localtime(&ptime->tsecs));
615 ptime->parseopts = 0;
616 /* ptime->tmspec = ? */
617 return (0);
618}