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