xref: /titanic_50/usr/src/lib/libast/common/tm/tmxdate.c (revision 015a6ef6781cc3ceba8ad3bfbae98449b6002a1f)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2008 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24  * Glenn Fowler
25  * AT&T Research
26  *
27  * Time_t conversion support
28  *
29  * relative times inspired by Steve Bellovin's netnews getdate(3)
30  */
31 
32 #include <tmx.h>
33 #include <ctype.h>
34 #include <debug.h>
35 
36 #define dig1(s,n)	((n)=((*(s)++)-'0'))
37 #define dig2(s,n)	((n)=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
38 #define dig3(s,n)	((n)=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
39 #define dig4(s,n)	((n)=((*(s)++)-'0')*1000,(n)+=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
40 
41 #define BREAK		(1<<0)
42 #define CCYYMMDDHHMMSS	(1<<1)
43 #define CRON		(1<<2)
44 #define DAY		(1<<3)
45 #define EXACT		(1<<4)
46 #define FINAL		(1<<5)
47 #define HOLD		(1<<6)
48 #define HOUR		(1<<7)
49 #define LAST		(1<<8)
50 #define MDAY		(1<<9)
51 #define MINUTE		(1<<10)
52 #define MONTH		(1<<11)
53 #define NEXT		(1<<12)
54 #define NSEC		(1<<13)
55 #define ORDINAL		(1<<14)
56 #define SECOND		(1<<15)
57 #define THIS		(1L<<16)
58 #define WDAY		(1L<<17)
59 #define YEAR		(1L<<18)
60 #define ZONE		(1L<<19)
61 
62 /*
63  * parse cron range into set
64  * return: -1:error 0:* 1:some
65  */
66 
67 static int
68 range(register char* s, char** e, char* set, int lo, int hi)
69 {
70 	int	n;
71 	int	m;
72 	int	i;
73 	char*	t;
74 
75 	while (isspace(*s) || *s == '_')
76 		s++;
77 	if (*s == '*')
78 	{
79 		*e = s + 1;
80 		return 0;
81 	}
82 	memset(set, 0, hi + 1);
83 	for (;;)
84 	{
85 		n = strtol(s, &t, 10);
86 		if (s == t || n < lo || n > hi)
87 			return -1;
88 		i = 1;
89 		if (*(s = t) == '-')
90 		{
91 			m = strtol(++s, &t, 10);
92 			if (s == t || m < n || m > hi)
93 				return -1;
94 			if (*(s = t) == '/')
95 			{
96 				i = strtol(++s, &t, 10);
97 				if (s == t || i < 1)
98 					return -1;
99 				s = t;
100 			}
101 		}
102 		else
103 			m = n;
104 		for (; n <= m; n += i)
105 			set[n] = 1;
106 		if (*s != ',')
107 			break;
108 		s++;
109 	}
110 	*e = s;
111 	return 1;
112 }
113 
114 /*
115  * parse date expression in s and return Time_t value
116  *
117  * if non-null, e points to the first invalid sequence in s
118  * now provides default values
119  */
120 
121 Time_t
122 tmxdate(register const char* s, char** e, Time_t now)
123 {
124 	register Tm_t*	tm;
125 	register long	n;
126 	register int	w;
127 	unsigned long	set;
128 	unsigned long	state;
129 	unsigned long	flags;
130 	Time_t		fix;
131 	char*		t;
132 	char*		u;
133 	const char*	x;
134 	char*		last;
135 	char*		type;
136 	int		day;
137 	int		dir;
138 	int		dst;
139 	int		zone;
140 	int		c;
141 	int		f;
142 	int		i;
143 	int		j;
144 	int		k;
145 	int		l;
146 	long		m;
147 	long		p;
148 	long		q;
149 	Tm_zone_t*	zp;
150 	char		skip[UCHAR_MAX + 1];
151 
152 	/*
153 	 * check DATEMSK first
154 	 */
155 
156 	debug((error(-1, "AHA tmxdate 2008-05-22")));
157 	fix = tmxscan(s, &last, NiL, &t, now, 0);
158 	if (t && !*last)
159 	{
160 		if (e)
161 			*e = last;
162 		return fix;
163 	}
164 
165  reset:
166 
167 	/*
168 	 * use now for defaults
169 	 */
170 
171 	tm = tmxmake(now);
172 	tm_info.date = tm_info.zone;
173 	day = -1;
174 	dst = TM_DST;
175 	set = state = 0;
176 	type = 0;
177 	zone = TM_LOCALZONE;
178 	skip[0] = 0;
179 	for (n = 1; n <= UCHAR_MAX; n++)
180 		skip[n] = isspace(n) || strchr("_,;@=|!^()[]{}", n);
181 
182 	/*
183 	 * get <weekday year month day hour minutes seconds ?[ds]t [ap]m>
184 	 */
185 
186 	for (;;)
187 	{
188 		state &= (state & HOLD) ? ~(HOLD) : ~(EXACT|LAST|NEXT|THIS);
189 		if ((set|state) & (YEAR|MONTH|DAY))
190 			skip['/'] = 1;
191 		message((-1, "AHA#%d state=%s%s%s%s| set=%s%s%s%s|", __LINE__, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : "", (set & EXACT) ? "|EXACT" : "", (set & LAST) ? "|LAST" : "", (set & THIS) ? "|THIS" : "", (set & NEXT) ? "|NEXT" : ""));
192 		for (;;)
193 		{
194 			if (*s == '.' || *s == '-' || *s == '+')
195 			{
196 				if (((set|state) & (YEAR|MONTH|HOUR|MINUTE|ZONE)) == (YEAR|MONTH|HOUR|MINUTE) && (i = tmgoff(s, &t, TM_LOCALZONE)) != TM_LOCALZONE)
197 				{
198 					zone = i;
199 					state |= ZONE;
200 					if (!*(s = t))
201 						break;
202 				}
203 				else if (*s == '+')
204 					break;
205 			}
206 			else if (!skip[*s])
207 				break;
208 			s++;
209 		}
210 		if (!*(last = (char*)s))
211 			break;
212 		if (*s == '#')
213 		{
214 			if (isdigit(*++s))
215 			{
216 				now = strtoull(s, &t, 0);
217 			sns:
218 				if (*(s = t) == '.')
219 				{
220 					fix = 0;
221 					m = 1000000000;
222 					while (isdigit(*++s))
223 						fix += (*s - '0') * (m /= 10);
224 					now = tmxsns(now, fix);
225 				}
226 				else if (now <= 0x7fffffff)
227 					now = tmxsns(now, 0);
228 				goto reset;
229 			}
230 			else if (*s++ == '#')
231 			{
232 				now = tmxtime(tm, zone);
233 				goto reset;
234 			}
235 			break;
236 		}
237 		f = -1;
238 		if (*s == '+')
239 		{
240 			while (isspace(*++s) || *s == '_');
241 			n = strtol(s, &t, 0);
242 			if (w = t - s)
243 			{
244 				for (s = t; skip[*s]; s++);
245 				state |= (f = n) ? NEXT : THIS;
246 				set &= ~(EXACT|LAST|NEXT|THIS);
247 				set |= state & (EXACT|LAST|NEXT|THIS);
248 			}
249 			else
250 				s = last;
251 		}
252 		if (!(state & CRON))
253 		{
254 			/*
255 			 * check for cron date
256 			 *
257 			 *	min hour day-of-month month day-of-week
258 			 *
259 			 * if it's cron then determine the next time
260 			 * that satisfies the specification
261 			 *
262 			 * NOTE: the only spacing is ' '||'_'||';'
263 			 */
264 
265 			i = 0;
266 			n = *(t = (char*)s);
267 			for (;;)
268 			{
269 				if (n == '*')
270 					n = *++s;
271 				else if (!isdigit(n))
272 					break;
273 				else
274 					while ((n = *++s) == ',' || n == '-' || n == '/' || isdigit(n));
275 				if (n != ' ' && n != '_' && n != ';')
276 				{
277 					if (!n)
278 						i++;
279 					break;
280 				}
281 				i++;
282 				while ((n = *++s) == ' ' || n == '_');
283 			}
284 			if (i == 5)
285 			{
286 				Time_t	tt;
287 				char	hit[60];
288 				char	mon[13];
289 				char	day[7];
290 
291 				state |= CRON;
292 				flags = 0;
293 				tm->tm_sec = 0;
294 				tm->tm_min++;
295 				tmfix(tm);
296 
297 				/*
298 				 * minute
299 				 */
300 
301 				if ((k = range(t, &t, hit, 0, 59)) < 0)
302 					break;
303 				if (k && !hit[i = tm->tm_min])
304 				{
305 					hit[i] = 1;
306 					do if (++i > 59)
307 					{
308 						i = 0;
309 						if (++tm->tm_hour > 59)
310 						{
311 							tm->tm_min = i;
312 							tmfix(tm);
313 						}
314 					} while (!hit[i]);
315 					tm->tm_min = i;
316 				}
317 
318 				/*
319 				 * hour
320 				 */
321 
322 				if ((k = range(t, &t, hit, 0, 23)) < 0)
323 					break;
324 				if (k && !hit[i = tm->tm_hour])
325 				{
326 					hit[i] = 1;
327 					do if (++i > 23)
328 					{
329 						i = 0;
330 						if (++tm->tm_mday > 28)
331 						{
332 							tm->tm_hour = i;
333 							tmfix(tm);
334 						}
335 					} while (!hit[i]);
336 					tm->tm_hour = i;
337 				}
338 
339 				/*
340 				 * day of month
341 				 */
342 
343 				if ((k = range(t, &t, hit, 1, 31)) < 0)
344 					break;
345 				if (k)
346 					flags |= DAY|MDAY;
347 
348 				/*
349 				 * month
350 				 */
351 
352 				if ((k = range(t, &t, mon, 1, 12)) < 0)
353 					break;
354 				if (k)
355 					flags |= MONTH;
356 				else
357 					for (i = 1; i <= 12; i++)
358 						mon[i] = 1;
359 
360 				/*
361 				 * day of week
362 				 */
363 
364 				if ((k = range(t, &t, day, 0, 6)) < 0)
365 					break;
366 				if (k)
367 					flags |= WDAY;
368 				s = t;
369 				if (flags & (MONTH|MDAY|WDAY))
370 				{
371 					fix = tmxtime(tm, zone);
372 					tm = tmxmake(fix);
373 					i = tm->tm_mon + 1;
374 					j = tm->tm_mday;
375 					k = tm->tm_wday;
376 					for (;;)
377 					{
378 						if (!mon[i])
379 						{
380 							if (++i > 12)
381 							{
382 								i = 1;
383 								tm->tm_year++;
384 							}
385 							tm->tm_mon = i - 1;
386 							tm->tm_mday = 1;
387 							tt = tmxtime(tm, zone);
388 							if (tt < fix)
389 								goto done;
390 							tm = tmxmake(tt);
391 							i = tm->tm_mon + 1;
392 							j = tm->tm_mday;
393 							k = tm->tm_wday;
394 							continue;
395 						}
396 						if (flags & (MDAY|WDAY))
397 						{
398 							if ((flags & (MDAY|WDAY)) == (MDAY|WDAY))
399 							{
400 								if (hit[j] && day[k])
401 									break;
402 							}
403 							else if ((flags & MDAY) && hit[j])
404 								break;
405 							else if ((flags & WDAY) && day[k])
406 								break;
407 							if (++j > 28)
408 							{
409 								tm->tm_mon = i - 1;
410 								tm->tm_mday = j;
411 								tm = tmxmake(tmxtime(tm, zone));
412 								i = tm->tm_mon + 1;
413 								j = tm->tm_mday;
414 								k = tm->tm_wday;
415 							}
416 							else if ((flags & WDAY) && ++k > 6)
417 								k = 0;
418 						}
419 						else if (flags & MONTH)
420 							break;
421 					}
422 					tm->tm_mon = i - 1;
423 					tm->tm_mday = j;
424 					tm->tm_wday = k;
425 				}
426 				continue;
427 			}
428 			s = t;
429 		}
430 		n = -1;
431 		if (isdigit(*s))
432 		{
433 			n = strtol(s, &t, 10);
434 			if ((w = t - s) && *t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && isdigit(*(t + 3)))
435 			{
436 				now = n;
437 				goto sns;
438 			}
439 			if ((*t == 'T' || *t == 't') && ((set|state) & (YEAR|MONTH|DAY)) == (YEAR|MONTH) && isdigit(*(t + 1)))
440 				t++;
441 			u = t + (*t == '-');
442 			if ((w == 2 || w == 4) && (*u == 'W' || *u == 'w') && isdigit(*(u + 1)))
443 			{
444 				if (w == 4)
445 				{
446 					if ((n -= 1900) < TM_WINDOW)
447 						break;
448 				}
449 				else if (n < TM_WINDOW)
450 					n += 100;
451 				m = n;
452 				n = strtol(++u, &t, 10);
453 				if ((i = (t - u)) < 2 || i > 3)
454 					break;
455 				if (i == 3)
456 				{
457 					k = n % 10;
458 					n /= 10;
459 				}
460 				else if (*t != '-')
461 					k = 1;
462 				else if (*++t && dig1(t, k) < 1 || k > 7)
463 					break;
464 				if (n < 0 || n > 53)
465 					break;
466 				if (k == 7)
467 					k = 0;
468 				tm->tm_year = m;
469 				tmweek(tm, 2, n, k);
470 				set |= YEAR|MONTH|DAY;
471 				s = t;
472 				continue;
473 			}
474 			else if (w == 6 || w == 8 && (n / 1000000) > 12)
475 			{
476 				t = (char*)s;
477 				flags = 0;
478 				if (w == 8 || w == 6 && *u != 'T' && *u != 't')
479 				{
480 					dig4(t, m);
481 					if ((m -= 1900) < TM_WINDOW)
482 						break;
483 				}
484 				else
485 				{
486 					dig2(t, m);
487 					if (m < TM_WINDOW)
488 						m += 100;
489 				}
490 				flags |= YEAR;
491 				if (dig2(t, l) <= 0 || l > 12)
492 					break;
493 				flags |= MONTH;
494 				if (*t != 'T' && *t != 't' || !isdigit(*++t))
495 				{
496 					if (w == 6)
497 						goto save_yymm;
498 					if (dig2(t, k) < 1 || k > 31)
499 						break;
500 					flags |= DAY;
501 					goto save_yymmdd;
502 				}
503 				n = strtol(s = t, &t, 0);
504 				if ((t - s) < 2)
505 					break;
506 				if (dig2(s, j) > 24)
507 					break;
508 				if ((t - s) < 2)
509 				{
510 					if ((t - s) == 1 || *t++ != '-')
511 						break;
512 					n = strtol(s = t, &t, 0);
513 					if ((t - s) < 2)
514 						break;
515 				}
516 				if (dig2(s, i) > 59)
517 					break;
518 				flags |= HOUR|MINUTE;
519 				if ((t - s) == 2)
520 				{
521 					if (dig2(s, n) > (59 + TM_MAXLEAP))
522 						break;
523 					flags |= SECOND;
524 				}
525 				else if (t - s)
526 					break;
527 				else
528 					n = 0;
529 				p = 0;
530 				if (*t == '.')
531 				{
532 					q = 1000000000;
533 					while (isdigit(*++t))
534 						p += (*t - '0') * (q /= 10);
535 					set |= NSEC;
536 				}
537 				if (n > (59 + TM_MAXLEAP))
538 					break;
539 				goto save;
540 			}
541 			else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0)
542 			{
543 				message((-1, "AHA#%d n=%d", __LINE__, n));
544  ordinal:
545 				if (n)
546 					n--;
547 				message((-1, "AHA#%d n=%d", __LINE__, n));
548 				state |= ((f = n) ? NEXT : THIS)|ORDINAL;
549 				set &= ~(EXACT|LAST|NEXT|THIS);
550 				set |= state & (EXACT|LAST|NEXT|THIS);
551 				for (s = t; skip[*s]; s++);
552 				if (isdigit(*s))
553 				{
554 					if (n = strtol(s, &t, 10))
555 						n--;
556 					s = t;
557 					if (*s == '_')
558 						s++;
559 				}
560 				else
561 					n = -1;
562 				message((-1, "AHA#%d f=%d n=%d", __LINE__, f, n));
563 			}
564 			else
565 			{
566 				if (!(state & (LAST|NEXT|THIS)) && ((i = t - s) == 4 && (*t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && *(t + 3) != '.' || (!*t || isspace(*t) || *t == '_' || isalnum(*t)) && n >= 0 && (n % 100) < 60 && ((m = (n / 100)) < 20 || m < 24 && !((set|state) & (YEAR|MONTH|HOUR|MINUTE)))) || i > 4 && i <= 12))
567 				{
568 					/*
569 					 * various { date(1) touch(1) } formats
570 					 *
571 					 *	[[cc]yy[mm]]ddhhmm[.ss[.nn...]]
572 					 *	[cc]yyjjj
573 					 *	hhmm[.ss[.nn...]]
574 					 */
575 
576 					flags = 0;
577 					if (state & CCYYMMDDHHMMSS)
578 						break;
579 					state |= CCYYMMDDHHMMSS;
580 					p = 0;
581 					if ((i == 7 || i == 5) && (!*t || *t == 'Z' || *t == 'z'))
582 					{
583 						if (i == 7)
584 						{
585 							dig4(s, m);
586 							if ((m -= 1900) < TM_WINDOW)
587 								break;
588 						}
589 						else if (dig2(s, m) < TM_WINDOW)
590 							m += 100;
591 						dig3(s, k);
592 						l = 1;
593 						j = 0;
594 						i = 0;
595 						n = 0;
596 						flags |= MONTH;
597 					}
598 					else if (i & 1)
599 						break;
600 					else
601 					{
602 						u = t;
603 						if (i == 12)
604 						{
605 							x = s;
606 							dig2(x, m);
607 							if (m <= 12)
608 							{
609 								u -= 4;
610 								i -= 4;
611 								x = s + 8;
612 								dig4(x, m);
613 							}
614 							else
615 								dig4(s, m);
616 							m -= 1900;
617 						}
618 						else if (i == 10)
619 						{
620 							x = s;
621 							if (!dig2(x, m) || m > 12 || !dig2(x, m) || m > 31 || dig2(x, m) > 24 || dig2(x, m) > 60 || dig2(x, m) <= 60 && !(tm_info.flags & TM_DATESTYLE))
622 								dig2(s, m);
623 							else
624 							{
625 								u -= 2;
626 								i -= 2;
627 								x = s + 8;
628 								dig2(x, m);
629 							}
630 							if (m < TM_WINDOW)
631 								m += 100;
632 						}
633 						else
634 							m = tm->tm_year;
635 						if ((u - s) < 8)
636 							l = tm->tm_mon + 1;
637 						else if (dig2(s, l) <= 0 || l > 12)
638 							break;
639 						else
640 							flags |= MONTH;
641 						if ((u - s) < 6)
642 							k = tm->tm_mday;
643 						else if (dig2(s, k) < 1 || k > 31)
644 							break;
645 						else
646 							flags |= DAY;
647 						if ((u - s) < 4)
648 							break;
649 						if (dig2(s, j) > 24)
650 							break;
651 						if (dig2(s, i) > 59)
652 							break;
653 						flags |= HOUR|MINUTE;
654 						if ((u - s) == 2)
655 						{
656 							dig2(s, n);
657 							flags |= SECOND;
658 						}
659 						else if (u - s)
660 							break;
661 						else if (*t != '.')
662 							n = 0;
663 						else
664 						{
665 							n = strtol(t + 1, &t, 10);
666 							flags |= SECOND;
667 							if (*t == '.')
668 							{
669 								q = 1000000000;
670 								while (isdigit(*++t))
671 									p += (*t - '0') * (q /= 10);
672 								set |= NSEC;
673 							}
674 						}
675 						if (n > (59 + TM_MAXLEAP))
676 							break;
677 					}
678 				save:
679 					tm->tm_hour = j;
680 					tm->tm_min = i;
681 					tm->tm_sec = n;
682 					tm->tm_nsec = p;
683 				save_yymmdd:
684 					tm->tm_mday = k;
685 				save_yymm:
686 					tm->tm_mon = l - 1;
687 					tm->tm_year = m;
688 					s = t;
689 					set |= flags;
690 					continue;
691 				}
692 				for (s = t; skip[*s]; s++);
693 				if (*s == ':' || *s == '.' && ((set|state) & (YEAR|MONTH|DAY|HOUR)) == (YEAR|MONTH|DAY))
694 				{
695 					c = *s;
696 					if ((state & HOUR) || n > 24)
697 						break;
698 					while (isspace(*++s) || *s == '_');
699 					if (!isdigit(*s))
700 						break;
701 					i = n;
702 					n = strtol(s, &t, 10);
703 					for (s = t; isspace(*s) || *s == '_'; s++);
704 					if (n > 59)
705 						break;
706 					j = n;
707 					m = 0;
708 					if (*s == c)
709 					{
710 						while (isspace(*++s) || *s == '_');
711 						if (!isdigit(*s))
712 							break;
713 						n = strtol(s, &t, 10);
714 						s = t;
715 						if (n > (59 + TM_MAXLEAP))
716 							break;
717 						set |= SECOND;
718 						while (isspace(*s))
719 							s++;
720 						if (*s == '.')
721 						{
722 							q = 1000000000;
723 							while (isdigit(*++s))
724 								m += (*s - '0') * (q /= 10);
725 							set |= NSEC;
726 						}
727 					}
728 					else
729 						n = 0;
730 					set |= HOUR|MINUTE;
731 					skip[':'] = 1;
732 					k = tm->tm_hour;
733 					tm->tm_hour = i;
734 					l = tm->tm_min;
735 					tm->tm_min = j;
736 					tm->tm_sec = n;
737 					tm->tm_nsec = m;
738 					while (isspace(*s))
739 						s++;
740 					switch (tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_MERIDIAN, 2))
741 					{
742 					case TM_MERIDIAN:
743 						s = t;
744 						if (i == 12)
745 							tm->tm_hour = i = 0;
746 						break;
747 					case TM_MERIDIAN+1:
748 						if (i < 12)
749 							tm->tm_hour = i += 12;
750 						break;
751 					}
752 					if (f >= 0 || (state & (LAST|NEXT)))
753 					{
754 						message((-1, "AHA#%d f=%d i=%d j=%d k=%d l=%d", __LINE__, f, i, j, k, l));
755 						state &= ~HOLD;
756 						if (f < 0)
757 						{
758 							if (state & LAST)
759 								f = -1;
760 							else if (state & NEXT)
761 								f = 1;
762 							else
763 								f = 0;
764 						}
765 						if (f > 0)
766 						{
767 							if (i > k || i == k && j > l)
768 								f--;
769 						}
770 						else if (i < k || i == k && j < l)
771 							f++;
772 						if (f > 0)
773 						{
774 							tm->tm_hour += f * 24;
775 							while (tm->tm_hour >= 24)
776 							{
777 								tm->tm_hour -= 24;
778 								tm->tm_mday++;
779 							}
780 						}
781 						else if (f < 0)
782 						{
783 							tm->tm_hour += f * 24;
784 							while (tm->tm_hour < 24)
785 							{
786 								tm->tm_hour += 24;
787 								tm->tm_mday--;
788 							}
789 						}
790 					}
791 					continue;
792 				}
793 			}
794 		}
795 		for (;;)
796 		{
797 			if (*s == '-' || *s == '+')
798 			{
799 				if (((set|state) & (MONTH|DAY|HOUR|MINUTE)) == (MONTH|DAY|HOUR|MINUTE) || *s == '+' && (!isdigit(s[1]) || !isdigit(s[2]) || s[3] != ':' && (s[3] != '.' || ((set|state) & (YEAR|MONTH)) != (YEAR|MONTH))))
800 					break;
801 				s++;
802 			}
803 			else if (skip[*s])
804 				s++;
805 			else
806 				break;
807 		}
808 		if (isalpha(*s) && n < 1000)
809 		{
810 			if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0)
811 			{
812 				s = t;
813 				switch (tm_data.lex[j])
814 				{
815 				case TM_EXACT:
816 					state |= HOLD|EXACT;
817 					set &= ~(EXACT|LAST|NEXT|THIS);
818 					set |= state & (EXACT|LAST|NEXT|THIS);
819 					continue;
820 				case TM_LAST:
821 					state |= HOLD|LAST;
822 					set &= ~(EXACT|LAST|NEXT|THIS);
823 					set |= state & (EXACT|LAST|NEXT|THIS);
824 					continue;
825 				case TM_THIS:
826 					state |= HOLD|THIS;
827 					set &= ~(EXACT|LAST|NEXT|THIS);
828 					set |= state & (EXACT|LAST|NEXT|THIS);
829 					n = 0;
830 					continue;
831 				case TM_NEXT:
832 					/*
833 					 * disambiguate english "last ... in"
834 					 */
835 
836 					if (!((state|set) & LAST))
837 					{
838 						state |= HOLD|NEXT;
839 						set &= ~(EXACT|LAST|NEXT|THIS);
840 						set |= state & (EXACT|LAST|NEXT|THIS);
841 						continue;
842 					}
843 					/*FALLTHROUGH*/
844 				case TM_FINAL:
845 					state |= HOLD|THIS|FINAL;
846 					set &= ~(EXACT|LAST|NEXT|THIS);
847 					set |= state & (EXACT|LAST|NEXT|THIS|FINAL);
848 					continue;
849 				case TM_ORDINAL:
850 					j += TM_ORDINALS - TM_ORDINAL;
851 					message((-1, "AHA#%d j=%d", __LINE__, j));
852 					/*FALLTHROUGH*/
853 				case TM_ORDINALS:
854 					n = j - TM_ORDINALS + 1;
855 					message((-1, "AHA#%d n=%d", __LINE__, n));
856 					goto ordinal;
857 				case TM_MERIDIAN:
858 					if (f >= 0)
859 						f++;
860 					else if (state & LAST)
861 						f = -1;
862 					else if (state & THIS)
863 						f = 1;
864 					else if (state & NEXT)
865 						f = 2;
866 					else
867 						f = 0;
868 					if (n > 0)
869 					{
870 						if (n > 24)
871 							goto done;
872 						tm->tm_hour = n;
873 					}
874 					for (k = tm->tm_hour; k < 0; k += 24);
875 					k %= 24;
876 					if (j == TM_MERIDIAN)
877 					{
878 						if (k == 12)
879 							tm->tm_hour -= 12;
880 					}
881 					else if (k < 12)
882 						tm->tm_hour += 12;
883 					if (n > 0)
884 						goto clear_min;
885 					continue;
886 				case TM_DAY_ABBREV:
887 					j += TM_DAY - TM_DAY_ABBREV;
888 					/*FALLTHROUGH*/
889 				case TM_DAY:
890 				case TM_PARTS:
891 				case TM_HOURS:
892 					state |= set & (EXACT|LAST|NEXT|THIS);
893 					if (!(state & (LAST|NEXT|THIS)))
894 						for (;;)
895 						{
896 							while (skip[*s])
897 								s++;
898 							if ((k = tmlex(s, &t, tm_info.format + TM_LAST, TM_NOISE - TM_LAST, NiL, 0)) >= 0)
899 							{
900 								s = t;
901 								if (k <= 2)
902 									state |= LAST;
903 								else if (k <= 5)
904 									state |= THIS;
905 								else if (k <= 8)
906 									state |= NEXT;
907 								else
908 									state |= EXACT;
909 							}
910 							else
911 							{
912 								state |= (n > 0) ? NEXT : THIS;
913 								break;
914 							}
915 							set &= ~(EXACT|LAST|NEXT|THIS);
916 							set |= state & (EXACT|LAST|NEXT|THIS);
917 						}
918 					/*FALLTHROUGH*/
919 				case TM_DAYS:
920 					message((-1, "AHA#%d n=%d j=%d f=%d state=%s%s%s%s|", __LINE__, n, j, f, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : ""));
921 					if (n == -1)
922 					{
923 						/*
924 						 * disambiguate english "second"
925 						 */
926 
927 						if (j == TM_PARTS && f == -1)
928 						{
929 							state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
930 							n = 2;
931 							goto ordinal;
932 						}
933 						n = 1;
934 					}
935 
936 					/*
937 					 * disambiguate "last" vs. { "previous" "final" }
938 					 */
939 
940 					while (isspace(*s))
941 						s++;
942 					message((-1, "AHA#%d disambiguate LAST s='%s'", __LINE__, s));
943 					if ((k = tmlex(s, &t, tm_info.format + TM_NEXT, TM_EXACT - TM_NEXT, NiL, 0)) >= 0)
944 					{
945 						s = t;
946 						if (state & LAST)
947 						{
948 							state &= ~LAST;
949 							set &= ~LAST;
950 							state |= FINAL;
951 							set |= FINAL;
952 							message((-1, "AHA#%d LAST => FINAL", __LINE__));
953 						}
954 						else
955 							state &= ~(THIS|NEXT);
956 					}
957 					message((-1, "AHA#%d disambiguate LAST k=%d", __LINE__, k));
958 					if (state & LAST)
959 						n = -n;
960 					else if (!(state & NEXT))
961 						n--;
962 					m = (f > 0) ? f * n : n;
963 					message((-1, "AHA#%d f=%d n=%d i=%d j=%d k=%d l=%d m=%d state=%s%s%s%s|", __LINE__, f, n, i, j, k, l, m, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : ""));
964 					switch (j)
965 					{
966 					case TM_DAYS+0:
967 						tm->tm_mday--;
968 						set |= DAY;
969 						goto clear_hour;
970 					case TM_DAYS+1:
971 						set |= DAY;
972 						goto clear_hour;
973 					case TM_DAYS+2:
974 						tm->tm_mday++;
975 						set |= DAY;
976 						goto clear_hour;
977 					case TM_PARTS+0:
978 						set |= SECOND;
979 						if ((m < 0 ? -m : m) > (365L*24L*60L*60L))
980 						{
981 							now = tmxtime(tm, zone) + tmxsns(m, 0);
982 							goto reset;
983 						}
984 						tm->tm_sec += m;
985 						goto clear_nsec;
986 					case TM_PARTS+1:
987 						tm->tm_min += m;
988 						set |= MINUTE;
989 						goto clear_sec;
990 					case TM_PARTS+2:
991 						tm->tm_hour += m;
992 						set |= MINUTE;
993 						goto clear_min;
994 					case TM_PARTS+3:
995 						tm->tm_mday += m;
996 						if (!(set & FINAL))
997 							set |= HOUR;
998 						goto clear_hour;
999 					case TM_PARTS+4:
1000 						tm = tmxmake(tmxtime(tm, zone));
1001 						tm->tm_mday += 7 * m - tm->tm_wday + 1;
1002 						set |= DAY;
1003 						goto clear_hour;
1004 					case TM_PARTS+5:
1005 						tm->tm_mon += m;
1006 						set |= MONTH;
1007 						goto clear_mday;
1008 					case TM_PARTS+6:
1009 						tm->tm_year += m;
1010 						goto clear_mon;
1011 					case TM_HOURS+0:
1012 						tm->tm_mday += m;
1013 						set |= DAY;
1014 						goto clear_hour;
1015 					case TM_HOURS+1:
1016 						tm->tm_mday += m;
1017 						tm->tm_hour = 6;
1018 						set |= HOUR;
1019 						goto clear_min;
1020 					case TM_HOURS+2:
1021 						tm->tm_mday += m;
1022 						tm->tm_hour = 12;
1023 						set |= HOUR;
1024 						goto clear_min;
1025 					case TM_HOURS+3:
1026 						tm->tm_mday += m;
1027 						tm->tm_hour = 18;
1028 						set |= HOUR;
1029 						goto clear_min;
1030 					}
1031 					if (m >= 0 && (state & ORDINAL))
1032 						tm->tm_mday = 1;
1033 					tm = tmxmake(tmxtime(tm, zone));
1034 					day = j -= TM_DAY;
1035 					dir = m;
1036 					message((-1, "AHA#%d j=%d m=%d", __LINE__, j, m));
1037 					j -= tm->tm_wday;
1038 					message((-1, "AHA#%d mday=%d wday=%d day=%d dir=%d f=%d i=%d j=%d l=%d m=%d", __LINE__, tm->tm_mday, tm->tm_wday, day, dir, f, i, j, l, m));
1039 					if (state & (LAST|NEXT|THIS))
1040 					{
1041 						if (state & ORDINAL)
1042 						{
1043 							while (isspace(*s))
1044 								s++;
1045 							if (isdigit(*s) || tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0) >= 0)
1046 							{
1047 								state &= ~(LAST|NEXT|THIS);
1048 								goto clear_hour;
1049 							}
1050 						}
1051 						if (j < 0)
1052 							j += 7;
1053 					}
1054 					else if (j > 0)
1055 						j -= 7;
1056 					message((-1, "AHA#%d day=%d mday=%d f=%d m=%d j=%d state=%s%s%s%s|", __LINE__, day, tm->tm_mday, f, m, j, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : ""));
1057 					set |= DAY;
1058 					if (set & FINAL)
1059 						goto clear_hour;
1060 					else if (state & (LAST|NEXT|THIS))
1061 					{
1062 						if (f >= 0)
1063 							day = -1;
1064 						else if (m > 0 && (state & (NEXT|YEAR|MONTH)) == NEXT && j >= 0)
1065 							m--;
1066 						tm->tm_mday += j + m * 7;
1067 						set &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1068 						state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1069 						if (!(state & EXACT))
1070 							goto clear_hour;
1071 					}
1072 					continue;
1073 				case TM_MONTH_ABBREV:
1074 					j += TM_MONTH - TM_MONTH_ABBREV;
1075 					/*FALLTHROUGH*/
1076 				case TM_MONTH:
1077 					if (state & MONTH)
1078 						goto done;
1079 					state |= MONTH;
1080 					i = tm->tm_mon;
1081 					tm->tm_mon = j - TM_MONTH;
1082 					if (n < 0)
1083 					{
1084 						while (skip[*s])
1085 							s++;
1086 						if (isdigit(*s))
1087 						{
1088 							n = strtol(s, &t, 10);
1089 							if (n <= 31 && *t != ':')
1090 								s = t;
1091 							else
1092 								n = -1;
1093 						}
1094 					}
1095 					if (n >= 0)
1096 					{
1097 						if (n > 31)
1098 							goto done;
1099 						state |= DAY|MDAY;
1100 						tm->tm_mday = n;
1101 						if (f > 0)
1102 							tm->tm_year += f;
1103 					}
1104 					if (state & (LAST|NEXT|THIS))
1105 					{
1106 						n = i;
1107 						goto rel_month;
1108 					}
1109 					continue;
1110 				case TM_UT:
1111 					if (state & ZONE)
1112 						goto done;
1113 					state |= ZONE;
1114 					zone = tmgoff(s, &t, 0);
1115 					s = t;
1116 					continue;
1117 				case TM_DT:
1118 					if (!dst)
1119 						goto done;
1120 					if (!(state & ZONE))
1121 					{
1122 						dst = tm_info.zone->dst;
1123 						zone = tm_info.zone->west;
1124 					}
1125 					zone += tmgoff(s, &t, dst);
1126 					s = t;
1127 					dst = 0;
1128 					state |= ZONE;
1129 					continue;
1130 				case TM_NOISE:
1131 					continue;
1132 				}
1133 			}
1134 			if (!(state & ZONE) && (zp = tmzone(s, &t, type, &dst)))
1135 			{
1136 				s = t;
1137 				zone = zp->west + dst;
1138 				tm_info.date = zp;
1139 				state |= ZONE;
1140 				if (n < 0)
1141 					continue;
1142 			}
1143 			else if (!type && (zp = tmtype(s, &t)))
1144 			{
1145 				s = t;
1146 				type = zp->type;
1147 				if (n < 0)
1148 					continue;
1149 			}
1150 			state |= BREAK;
1151 		}
1152 		else if (*s == '/')
1153 		{
1154 			if (!(state & (YEAR|MONTH)) && n >= 1900 && n < 3000 && (i = strtol(s + 1, &t, 10)) > 0 && i <= 12)
1155 			{
1156 				state |= YEAR;
1157 				tm->tm_year = n - 1900;
1158 				s = t;
1159 				i--;
1160 			}
1161 			else
1162 			{
1163 				if ((state & MONTH) || n <= 0 || n > 31)
1164 					break;
1165 				if (isalpha(*++s))
1166 				{
1167 					if ((i = tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0)) < 0)
1168 						break;
1169 					if (i >= TM_MONTH)
1170 						i -= TM_MONTH;
1171 					s = t;
1172 				}
1173 				else
1174 				{
1175 					i = n - 1;
1176 					n = strtol(s, &t, 10);
1177 					s = t;
1178 					if (n <= 0 || n > 31)
1179 						break;
1180 					if (*s == '/' && !isdigit(*(s + 1)))
1181 						break;
1182 				}
1183 				state |= DAY;
1184 				tm->tm_mday = n;
1185 			}
1186 			state |= MONTH;
1187 			n = tm->tm_mon;
1188 			tm->tm_mon = i;
1189 			if (*s == '/')
1190 			{
1191 				n = strtol(++s, &t, 10);
1192 				w = t - s;
1193 				s = t;
1194 				if (*s == '/' || *s == ':' || *s == '-' || *s == '_')
1195 					s++;
1196 			}
1197 			else
1198 			{
1199 				if (state & (LAST|NEXT|THIS))
1200 				{
1201 				rel_month:
1202 					if (state & LAST)
1203 						tm->tm_year -= (tm->tm_mon < n) ? 0 : 1;
1204 					else
1205 						tm->tm_year += ((state & NEXT) ? 1 : 0) + ((tm->tm_mon < n) ? 1 : 0);
1206 					if (state & MDAY)
1207 						goto clear_hour;
1208 					set &= ~(LAST|NEXT|THIS); /*AHA*/
1209 					state &= ~(LAST|NEXT|THIS); /*AHA*/
1210 					goto clear_mday;
1211 				}
1212 				continue;
1213 			}
1214 		}
1215 		if (n < 0 || w > 4)
1216 			break;
1217 		if (w == 4)
1218 		{
1219 			if ((state & YEAR) || n < 1900 || n >= 3000)
1220 				break;
1221 			state |= YEAR;
1222 			tm->tm_year = n - 1900;
1223 		}
1224 		else if (w == 3)
1225 		{
1226 			if (state & (MONTH|MDAY|WDAY))
1227 				break;
1228 			state |= MONTH|DAY|MDAY;
1229 			tm->tm_mon = 0;
1230 			tm->tm_mday = n;
1231 		}
1232 		else if (w == 2 && !(state & YEAR))
1233 		{
1234 			state |= YEAR;
1235 			if (n < TM_WINDOW)
1236 				n += 100;
1237 			tm->tm_year = n;
1238 		}
1239 		else if (!(state & MONTH) && n >= 1 && n <= 12)
1240 		{
1241 			state |= MONTH;
1242 			tm->tm_mon = n - 1;
1243 		}
1244 		else if (!(state & (MDAY|WDAY)) && n >= 1 && n <= 31)
1245 		{
1246 			state |= DAY|MDAY|WDAY;
1247 			tm->tm_mday = n;
1248 		}
1249 		else
1250 			break;
1251 		if (state & BREAK)
1252 		{
1253 			last = t;
1254 			break;
1255 		}
1256 		continue;
1257 	clear_mon:
1258 		if ((set|state) & (EXACT|MONTH))
1259 			continue;
1260 		tm->tm_mon = 0;
1261 	clear_mday:
1262 		set |= MONTH;
1263 		if ((set|state) & (EXACT|DAY|HOUR))
1264 			continue;
1265 		tm->tm_mday = 1;
1266 	clear_hour:
1267 		message((-1, "AHA#%d DAY", __LINE__));
1268 		set |= DAY;
1269 		if ((set|state) & (EXACT|HOUR))
1270 			continue;
1271 		tm->tm_hour = 0;
1272 	clear_min:
1273 		set |= HOUR;
1274 		if ((set|state) & (EXACT|MINUTE))
1275 			continue;
1276 		tm->tm_min = 0;
1277 	clear_sec:
1278 		set |= MINUTE;
1279 		if ((set|state) & (EXACT|SECOND))
1280 			continue;
1281 		tm->tm_sec = 0;
1282 	clear_nsec:
1283 		set |= SECOND;
1284 		if ((set|state) & (EXACT|NSEC))
1285 			continue;
1286 		tm->tm_nsec = 0;
1287 	}
1288  done:
1289 	if (day >= 0 && !(state & (MDAY|WDAY)))
1290 	{
1291 		message((-1, "AHA#%d day=%d dir=%d%s", __LINE__, day, dir, (state & FINAL) ? " FINAL" : ""));
1292 		m = dir;
1293 		if (state & MONTH)
1294 			tm->tm_mday = 1;
1295 		else if (m < 0)
1296 			m++;
1297 		tm = tmxmake(tmxtime(tm, zone));
1298 		j = day - tm->tm_wday;
1299 		if (j < 0)
1300 			j += 7;
1301 		tm->tm_mday += j + m * 7;
1302 		if (state & FINAL)
1303 			for (n = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year)); (tm->tm_mday + 7) <= n; tm->tm_mday += 7);
1304 	}
1305 	else if (day < 0 && (state & FINAL) && (set & DAY))
1306 		tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1307 	if (e)
1308 		*e = last;
1309 	return tmxtime(tm, zone);
1310 }
1311