xref: /titanic_52/usr/src/contrib/ast/src/lib/libast/tm/tmxdate.c (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 #undef	BREAK
42 
43 #define BREAK		(1<<0)
44 #define CCYYMMDDHHMMSS	(1<<1)
45 #define CRON		(1<<2)
46 #define DAY		(1<<3)
47 #define EXACT		(1<<4)
48 #define FINAL		(1<<5)
49 #define HOLD		(1<<6)
50 #define HOUR		(1<<7)
51 #define LAST		(1<<8)
52 #define MDAY		(1<<9)
53 #define MINUTE		(1<<10)
54 #define MONTH		(1<<11)
55 #define NEXT		(1<<12)
56 #define NSEC		(1<<13)
57 #define ORDINAL		(1<<14)
58 #define SECOND		(1<<15)
59 #define THIS		(1L<<16)
60 #define WDAY		(1L<<17)
61 #define WORK		(1L<<18)
62 #define YEAR		(1L<<19)
63 #define ZONE		(1L<<20)
64 
65 #define FFMT		"%s%s%s%s%s%s%s|"
66 #define	FLAGS(f)	(f&EXACT)?"|EXACT":"",(f&LAST)?"|LAST":"",(f&THIS)?"|THIS":"",(f&NEXT)?"|NEXT":"",(f&ORDINAL)?"|ORDINAL":"",(f&FINAL)?"|FINAL":"",(f&WORK)?"|WORK":""
67 /*
68  * parse cron range into set
69  * return: -1:error 0:* 1:some
70  */
71 
72 static int
73 range(register char* s, char** e, char* set, int lo, int hi)
74 {
75 	int	n;
76 	int	m;
77 	int	i;
78 	char*	t;
79 
80 	while (isspace(*s) || *s == '_')
81 		s++;
82 	if (*s == '*')
83 	{
84 		*e = s + 1;
85 		return 0;
86 	}
87 	memset(set, 0, hi + 1);
88 	for (;;)
89 	{
90 		n = strtol(s, &t, 10);
91 		if (s == t || n < lo || n > hi)
92 			return -1;
93 		i = 1;
94 		if (*(s = t) == '-')
95 		{
96 			m = strtol(++s, &t, 10);
97 			if (s == t || m < n || m > hi)
98 				return -1;
99 			if (*(s = t) == '/')
100 			{
101 				i = strtol(++s, &t, 10);
102 				if (s == t || i < 1)
103 					return -1;
104 				s = t;
105 			}
106 		}
107 		else
108 			m = n;
109 		for (; n <= m; n += i)
110 			set[n] = 1;
111 		if (*s != ',')
112 			break;
113 		s++;
114 	}
115 	*e = s;
116 	return 1;
117 }
118 
119 /*
120  * normalize <p,q> to power of 10 u in tm
121  */
122 
123 static void
124 powerize(Tm_t* tm, unsigned long p, unsigned long q, unsigned long u)
125 {
126 	Time_t	t = p;
127 
128 	while (q > u)
129 	{
130 		q /= 10;
131 		t /= 10;
132 	}
133 	while (q < u)
134 	{
135 		q *= 10;
136 		t *= 10;
137 	}
138 	tm->tm_nsec += (int)(t % TMX_RESOLUTION);
139 	tm->tm_sec += (int)(t / TMX_RESOLUTION);
140 }
141 
142 #define K1(c1)			(c1)
143 #define K2(c1,c2)		(((c1)<<8)|(c2))
144 #define K3(c1,c2,c3)		(((c1)<<16)|((c2)<<8)|(c3))
145 #define K4(c1,c2,c3,c4)		(((c1)<<24)|((c2)<<16)|((c3)<<8)|(c4))
146 
147 #define P_INIT(n)		w = n; p = q = 0; u = (char*)s + 1
148 
149 /*
150  * parse date expression in s and return Time_t value
151  *
152  * if non-null, e points to the first invalid sequence in s
153  * now provides default values
154  */
155 
156 Time_t
157 tmxdate(register const char* s, char** e, Time_t now)
158 {
159 	register Tm_t*	tm;
160 	register long	n;
161 	register int	w;
162 	unsigned long	set;
163 	unsigned long	state;
164 	unsigned long	flags;
165 	Time_t		fix;
166 	char*		t;
167 	char*		u;
168 	const char*	o;
169 	const char*	x;
170 	char*		last;
171 	char*		type;
172 	int		day;
173 	int		dir;
174 	int		dst;
175 	int		zone;
176 	int		c;
177 	int		f;
178 	int		i;
179 	int		j;
180 	int		k;
181 	int		l;
182 	long		m;
183 	unsigned long	p;
184 	unsigned long	q;
185 	Tm_zone_t*	zp;
186 	Tm_t		ts;
187 	char		skip[UCHAR_MAX + 1];
188 
189 	/*
190 	 * check DATEMSK first
191 	 */
192 
193 	debug((error(-1, "AHA tmxdate 2009-03-06")));
194 	fix = tmxscan(s, &last, NiL, &t, now, 0);
195 	if (t && !*last)
196 	{
197 		if (e)
198 			*e = last;
199 		return fix;
200 	}
201 	o = s;
202 
203  reset:
204 
205 	/*
206 	 * use now for defaults
207 	 */
208 
209 	tm = tmxtm(&ts, now, NiL);
210 	tm_info.date = tm->tm_zone;
211 	day = -1;
212 	dir = 0;
213 	dst = TM_DST;
214 	set = state = 0;
215 	type = 0;
216 	zone = TM_LOCALZONE;
217 	skip[0] = 0;
218 	for (n = 1; n <= UCHAR_MAX; n++)
219 		skip[n] = isspace(n) || strchr("_,;@=|!^()[]{}", n);
220 
221 	/*
222 	 * get <weekday year month day hour minutes seconds ?[ds]t [ap]m>
223 	 */
224 
225  again:
226 	for (;;)
227 	{
228 		state &= (state & HOLD) ? ~(HOLD) : ~(EXACT|LAST|NEXT|THIS);
229 		if ((set|state) & (YEAR|MONTH|DAY))
230 			skip['/'] = 1;
231 		message((-1, "AHA#%d state=" FFMT " set=" FFMT, __LINE__, FLAGS(state), FLAGS(set)));
232 		for (;;)
233 		{
234 			if (*s == '.' || *s == '-' || *s == '+')
235 			{
236 				if (((set|state) & (YEAR|MONTH|HOUR|MINUTE|ZONE)) == (YEAR|MONTH|HOUR|MINUTE) && (i = tmgoff(s, &t, TM_LOCALZONE)) != TM_LOCALZONE)
237 				{
238 					zone = i;
239 					state |= ZONE;
240 					if (!*(s = t))
241 						break;
242 				}
243 				else if (*s == '+')
244 					break;
245 			}
246 			else if (!skip[*s])
247 				break;
248 			s++;
249 		}
250 		if (!*(last = (char*)s))
251 			break;
252 		if (*s == '#')
253 		{
254 			if (isdigit(*++s))
255 			{
256 				now = strtoull(s, &t, 0);
257 			sns:
258 				if (*(s = t) == '.')
259 				{
260 					fix = 0;
261 					m = 1000000000;
262 					while (isdigit(*++s))
263 						fix += (*s - '0') * (m /= 10);
264 					now = tmxsns(now, fix);
265 				}
266 				else if (now <= 0x7fffffff)
267 					now = tmxsns(now, 0);
268 				goto reset;
269 			}
270 			else if (*s++ == '#')
271 			{
272 				now = tmxtime(tm, zone);
273 				goto reset;
274 			}
275 			break;
276 		}
277 		if ((*s == 'P' || *s == 'p') && (!isalpha(*(s + 1)) || (*(s + 1) == 'T' || *(s + 1) == 't') && !isalpha(*(s + 2))))
278 		{
279 			Tm_t	otm;
280 
281 			/*
282 			 * iso duration
283 			 */
284 
285 			otm = *tm;
286 			t = (char*)s;
287 			m = 0;
288 			P_INIT('Y');
289 			do
290 			{
291 				c = *++s;
292 			duration_next:
293 				switch (c)
294 				{
295 				case 0:
296 					m++;
297 					if ((char*)s > u)
298 					{
299 						s--;
300 						c = '_';
301 						goto duration_next;
302 					}
303 					break;
304 				case 'T':
305 				case 't':
306 					m++;
307 					if ((char*)s > u)
308 					{
309 						s++;
310 						c = 'D';
311 						goto duration_next;
312 					}
313 					continue;
314 				case 'Y':
315 				case 'y':
316 					m = 0;
317 					if (q > 1)
318 						tm->tm_sec += (365L*24L*60L*60L) * p / q;
319 					else
320 						tm->tm_year += p;
321 					P_INIT('M');
322 					continue;
323 				case 'm':
324 					if (!m)
325 						m = 1;
326 					/*FALLTHROUGH*/
327 				case 'M':
328 					switch (*(s + 1))
329 					{
330 					case 'I':
331 					case 'i':
332 						s++;
333 						m = 1;
334 						w = 'S';
335 						break;
336 					case 'O':
337 					case 'o':
338 						s++;
339 						m = 0;
340 						w = 'H';
341 						break;
342 					case 'S':
343 					case 's':
344 						s++;
345 						m = 2;
346 						w = 's';
347 						break;
348 					}
349 					switch (m)
350 					{
351 					case 0:
352 						m = 1;
353 						if (q > 1)
354 							tm->tm_sec += (3042L*24L*60L*60L) * p / q / 100L;
355 						else
356 							tm->tm_mon += p;
357 						break;
358 					case 1:
359 						m = 2;
360 						if (q > 1)
361 							tm->tm_sec += (60L) * p / q;
362 						else
363 							tm->tm_min += p;
364 						break;
365 					default:
366 						if (q > 1)
367 							powerize(tm, p, q, 1000UL);
368 						else
369 							tm->tm_nsec += p * 1000000L;
370 						break;
371 					}
372 					P_INIT(w);
373 					continue;
374 				case 'W':
375 				case 'w':
376 					m = 0;
377 					if (q > 1)
378 						tm->tm_sec += (7L*24L*60L*60L) * p / q;
379 					else
380 						tm->tm_mday += 7 * p;
381 					P_INIT('D');
382 					continue;
383 				case 'D':
384 				case 'd':
385 					m = 0;
386 					if (q > 1)
387 						tm->tm_sec += (24L*60L*60L) * p / q;
388 					else
389 						tm->tm_mday += p;
390 					P_INIT('H');
391 					continue;
392 				case 'H':
393 				case 'h':
394 					m = 1;
395 					if (q > 1)
396 						tm->tm_sec += (60L*60L) * p / q;
397 					else
398 						tm->tm_hour += p;
399 					P_INIT('m');
400 					continue;
401 				case 'S':
402 				case 's':
403 					m = 2;
404 					/*FALLTHROUGH*/
405 				case ' ':
406 				case '_':
407 				case '\n':
408 				case '\r':
409 				case '\t':
410 				case '\v':
411 					if (q > 1)
412 						powerize(tm, p, q, 1000000000UL);
413 					else
414 						tm->tm_sec += p;
415 					P_INIT('U');
416 					continue;
417 				case 'U':
418 				case 'u':
419 					switch (*(s + 1))
420 					{
421 					case 'S':
422 					case 's':
423 						s++;
424 						break;
425 					}
426 					m = 0;
427 					if (q > 1)
428 						powerize(tm, p, q, 1000000UL);
429 					else
430 						tm->tm_nsec += p * 1000L;
431 					P_INIT('N');
432 					continue;
433 				case 'N':
434 				case 'n':
435 					switch (*(s + 1))
436 					{
437 					case 'S':
438 					case 's':
439 						s++;
440 						break;
441 					}
442 					m = 0;
443 					if (q > 1)
444 						powerize(tm, p, q, 1000000000UL);
445 					else
446 						tm->tm_nsec += p;
447 					P_INIT('Y');
448 					continue;
449 				case '.':
450 					if (q)
451 						goto exact;
452 					q = 1;
453 					continue;
454 				case '-':
455 					c = 'M';
456 					u = (char*)s++;
457 					while (*++u && *u != ':')
458 						if (*u == '-')
459 						{
460 							c = 'Y';
461 							break;
462 						}
463 					goto duration_next;
464 				case ':':
465 					c = 'm';
466 					u = (char*)s++;
467 					while (*++u)
468 						if (*u == ':')
469 						{
470 							c = 'H';
471 							break;
472 						}
473 					goto duration_next;
474 				case '0':
475 				case '1':
476 				case '2':
477 				case '3':
478 				case '4':
479 				case '5':
480 				case '6':
481 				case '7':
482 				case '8':
483 				case '9':
484 					q *= 10;
485 					p = p * 10 + (c - '0');
486 					continue;
487 				default:
488 				exact:
489 					*tm = otm;
490 					s = (const char*)t + 1;
491 					if (*t == 'p')
492 					{
493 						state |= HOLD|EXACT;
494 						set &= ~(EXACT|LAST|NEXT|THIS);
495 						set |= state & (EXACT|LAST|NEXT|THIS);
496 					}
497 					goto again;
498 				}
499 				break;
500 			} while (c);
501 			continue;
502 		}
503 		f = -1;
504 		if (*s == '+')
505 		{
506 			while (isspace(*++s) || *s == '_');
507 			n = strtol(s, &t, 0);
508 			if (w = t - s)
509 			{
510 				for (s = t; skip[*s]; s++);
511 				state |= (f = n) ? NEXT : THIS;
512 				set &= ~(EXACT|LAST|NEXT|THIS);
513 				set |= state & (EXACT|LAST|NEXT|THIS);
514 			}
515 			else
516 				s = last;
517 		}
518 		if (!(state & CRON))
519 		{
520 			/*
521 			 * check for cron date
522 			 *
523 			 *	min hour day-of-month month day-of-week
524 			 *
525 			 * if it's cron then determine the next time
526 			 * that satisfies the specification
527 			 *
528 			 * NOTE: the only spacing is ' '||'_'||';'
529 			 */
530 
531 			i = 0;
532 			n = *(t = (char*)s);
533 			for (;;)
534 			{
535 				if (n == '*')
536 					n = *++s;
537 				else if (!isdigit(n))
538 					break;
539 				else
540 					while ((n = *++s) == ',' || n == '-' || n == '/' || isdigit(n));
541 				if (n != ' ' && n != '_' && n != ';')
542 				{
543 					if (!n)
544 						i++;
545 					break;
546 				}
547 				i++;
548 				while ((n = *++s) == ' ' || n == '_');
549 			}
550 			if (i == 5)
551 			{
552 				Time_t	tt;
553 				char	hit[60];
554 				char	mon[13];
555 				char	day[7];
556 
557 				state |= CRON;
558 				flags = 0;
559 				tm->tm_sec = 0;
560 				tm->tm_min++;
561 				tmfix(tm);
562 
563 				/*
564 				 * minute
565 				 */
566 
567 				if ((k = range(t, &t, hit, 0, 59)) < 0)
568 					break;
569 				if (k && !hit[i = tm->tm_min])
570 				{
571 					hit[i] = 1;
572 					do if (++i > 59)
573 					{
574 						i = 0;
575 						if (++tm->tm_hour > 59)
576 						{
577 							tm->tm_min = i;
578 							tmfix(tm);
579 						}
580 					} while (!hit[i]);
581 					tm->tm_min = i;
582 				}
583 
584 				/*
585 				 * hour
586 				 */
587 
588 				if ((k = range(t, &t, hit, 0, 23)) < 0)
589 					break;
590 				if (k && !hit[i = tm->tm_hour])
591 				{
592 					hit[i] = 1;
593 					do if (++i > 23)
594 					{
595 						i = 0;
596 						if (++tm->tm_mday > 28)
597 						{
598 							tm->tm_hour = i;
599 							tmfix(tm);
600 						}
601 					} while (!hit[i]);
602 					tm->tm_hour = i;
603 				}
604 
605 				/*
606 				 * day of month
607 				 */
608 
609 				if ((k = range(t, &t, hit, 1, 31)) < 0)
610 					break;
611 				if (k)
612 					flags |= DAY|MDAY;
613 
614 				/*
615 				 * month
616 				 */
617 
618 				if ((k = range(t, &t, mon, 1, 12)) < 0)
619 					break;
620 				if (k)
621 					flags |= MONTH;
622 				else
623 					for (i = 1; i <= 12; i++)
624 						mon[i] = 1;
625 
626 				/*
627 				 * day of week
628 				 */
629 
630 				if ((k = range(t, &t, day, 0, 6)) < 0)
631 					break;
632 				if (k)
633 					flags |= WDAY;
634 				s = t;
635 				if (flags & (MONTH|MDAY|WDAY))
636 				{
637 					fix = tmxtime(tm, zone);
638 					tm = tmxtm(tm, fix, tm->tm_zone);
639 					i = tm->tm_mon + 1;
640 					j = tm->tm_mday;
641 					k = tm->tm_wday;
642 					for (;;)
643 					{
644 						if (!mon[i])
645 						{
646 							if (++i > 12)
647 							{
648 								i = 1;
649 								tm->tm_year++;
650 							}
651 							tm->tm_mon = i - 1;
652 							tm->tm_mday = 1;
653 							tt = tmxtime(tm, zone);
654 							if (tt < fix)
655 								goto done;
656 							tm = tmxtm(tm, tt, tm->tm_zone);
657 							i = tm->tm_mon + 1;
658 							j = tm->tm_mday;
659 							k = tm->tm_wday;
660 							continue;
661 						}
662 						if (flags & (MDAY|WDAY))
663 						{
664 							if ((flags & (MDAY|WDAY)) == (MDAY|WDAY))
665 							{
666 								if (hit[j] && day[k])
667 									break;
668 							}
669 							else if ((flags & MDAY) && hit[j])
670 								break;
671 							else if ((flags & WDAY) && day[k])
672 								break;
673 							if (++j > 28)
674 							{
675 								tm->tm_mon = i - 1;
676 								tm->tm_mday = j;
677 								tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
678 								i = tm->tm_mon + 1;
679 								j = tm->tm_mday;
680 								k = tm->tm_wday;
681 							}
682 							else if ((flags & WDAY) && ++k > 6)
683 								k = 0;
684 						}
685 						else if (flags & MONTH)
686 							break;
687 					}
688 					tm->tm_mon = i - 1;
689 					tm->tm_mday = j;
690 					tm->tm_wday = k;
691 				}
692 				continue;
693 			}
694 			s = t;
695 		}
696 		n = -1;
697 		if (isdigit(*s))
698 		{
699 			n = strtol(s, &t, 10);
700 			if ((w = t - s) && *t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && isdigit(*(t + 3)))
701 			{
702 				now = n;
703 				goto sns;
704 			}
705 			if ((*t == 'T' || *t == 't') && ((set|state) & (YEAR|MONTH|DAY)) == (YEAR|MONTH) && isdigit(*(t + 1)))
706 				t++;
707 			u = t + (*t == '-');
708 			message((-1, "AHA#%d n=%d w=%d u='%c' f=%d t=\"%s\"", __LINE__, n, w, *u, f, t));
709 			if ((w == 2 || w == 4) && (*u == 'W' || *u == 'w') && isdigit(*(u + 1)))
710 			{
711 				if (w == 4)
712 				{
713 					if ((n -= 1900) < TM_WINDOW)
714 						break;
715 				}
716 				else if (n < TM_WINDOW)
717 					n += 100;
718 				m = n;
719 				n = strtol(++u, &t, 10);
720 				if ((i = (t - u)) < 2 || i > 3)
721 					break;
722 				if (i == 3)
723 				{
724 					k = n % 10;
725 					n /= 10;
726 				}
727 				else if (*t != '-')
728 					k = 1;
729 				else if (*++t && dig1(t, k) < 1 || k > 7)
730 					break;
731 				if (n < 0 || n > 53)
732 					break;
733 				if (k == 7)
734 					k = 0;
735 				tm->tm_year = m;
736 				tmweek(tm, 2, n, k);
737 				set |= YEAR|MONTH|DAY;
738 				s = t;
739 				continue;
740 			}
741 			else if (w == 6 || w == 8 && (n / 1000000) > 12)
742 			{
743 				t = (char*)s;
744 				flags = 0;
745 				if (w == 8 || w == 6 && *u != 'T' && *u != 't')
746 				{
747 					dig4(t, m);
748 					if ((m -= 1900) < TM_WINDOW)
749 						break;
750 				}
751 				else
752 				{
753 					dig2(t, m);
754 					if (m < TM_WINDOW)
755 						m += 100;
756 				}
757 				flags |= YEAR;
758 				if (dig2(t, l) <= 0 || l > 12)
759 					break;
760 				flags |= MONTH;
761 				if (*t != 'T' && *t != 't' || !isdigit(*++t))
762 				{
763 					if (w == 6)
764 						goto save_yymm;
765 					if (dig2(t, k) < 1 || k > 31)
766 						break;
767 					flags |= DAY;
768 					goto save_yymmdd;
769 				}
770 				n = strtol(s = t, &t, 0);
771 				if ((t - s) < 2)
772 					break;
773 				if (dig2(s, j) > 24)
774 					break;
775 				if ((t - s) < 2)
776 				{
777 					if ((t - s) == 1 || *t++ != '-')
778 						break;
779 					n = strtol(s = t, &t, 0);
780 					if ((t - s) < 2)
781 						break;
782 				}
783 				if (dig2(s, i) > 59)
784 					break;
785 				flags |= HOUR|MINUTE;
786 				if ((t - s) == 2)
787 				{
788 					if (dig2(s, n) > (59 + TM_MAXLEAP))
789 						break;
790 					flags |= SECOND;
791 				}
792 				else if (t - s)
793 					break;
794 				else
795 					n = 0;
796 				p = 0;
797 				if (*t == '.')
798 				{
799 					q = 1000000000;
800 					while (isdigit(*++t))
801 						p += (*t - '0') * (q /= 10);
802 					set |= NSEC;
803 				}
804 				if (n > (59 + TM_MAXLEAP))
805 					break;
806 				goto save;
807 			}
808 			else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0)
809 			{
810 				message((-1, "AHA#%d n=%d", __LINE__, n));
811  ordinal:
812 				if (n)
813 					n--;
814 				message((-1, "AHA#%d n=%d", __LINE__, n));
815 				state |= ((f = n) ? NEXT : THIS)|ORDINAL;
816 				set &= ~(EXACT|LAST|NEXT|THIS);
817 				set |= state & (EXACT|LAST|NEXT|THIS);
818 				for (s = t; skip[*s]; s++);
819 				if (isdigit(*s))
820 				{
821 					if (n = strtol(s, &t, 10))
822 						n--;
823 					s = t;
824 					if (*s == '_')
825 						s++;
826 				}
827 				else
828 					n = -1;
829 				dir = f;
830 				message((-1, "AHA#%d f=%d n=%d state=" FFMT, __LINE__, f, n, FLAGS(state)));
831 			}
832 			else
833 			{
834 				for (u = t; isspace(*u); u++);
835 				message((-1, "AHA#%d n=%d u=\"%s\"", __LINE__, n, u));
836 				if ((j = tmlex(u, NiL, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0 && tm_data.lex[j] == TM_PARTS)
837 					s = u;
838 				else
839 				{
840 					message((-1, "AHA#%d t=\"%s\"", __LINE__, t));
841 					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))
842 					{
843 						/*
844 						 * various { date(1) touch(1) } formats
845 						 *
846 						 *	[[cc]yy[mm]]ddhhmm[.ss[.nn...]]
847 						 *	[cc]yyjjj
848 						 *	hhmm[.ss[.nn...]]
849 						 */
850 
851 						message((-1, "AHA#%d t=\"%s\"", __LINE__, t));
852 						flags = 0;
853 						if (state & CCYYMMDDHHMMSS)
854 							break;
855 						state |= CCYYMMDDHHMMSS;
856 						p = 0;
857 						if ((i == 7 || i == 5) && (!*t || *t == 'Z' || *t == 'z'))
858 						{
859 							if (i == 7)
860 							{
861 								dig4(s, m);
862 								if ((m -= 1900) < TM_WINDOW)
863 									break;
864 							}
865 							else if (dig2(s, m) < TM_WINDOW)
866 								m += 100;
867 							dig3(s, k);
868 							l = 1;
869 							j = 0;
870 							i = 0;
871 							n = 0;
872 							flags |= MONTH;
873 						}
874 						else if (i & 1)
875 							break;
876 						else
877 						{
878 							u = t;
879 							if (i == 12)
880 							{
881 								x = s;
882 								dig2(x, m);
883 								if (m <= 12)
884 								{
885 									u -= 4;
886 									i -= 4;
887 									x = s + 8;
888 									dig4(x, m);
889 								}
890 								else
891 									dig4(s, m);
892 								if (m < 1969 || m >= 3000)
893 									break;
894 								m -= 1900;
895 							}
896 							else if (i == 10)
897 							{
898 								x = s;
899 								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))
900 									dig2(s, m);
901 								else
902 								{
903 									u -= 2;
904 									i -= 2;
905 									x = s + 8;
906 									dig2(x, m);
907 								}
908 								if (m < TM_WINDOW)
909 									m += 100;
910 							}
911 							else
912 								m = tm->tm_year;
913 							if ((u - s) < 8)
914 								l = tm->tm_mon + 1;
915 							else if (dig2(s, l) <= 0 || l > 12)
916 								break;
917 							else
918 								flags |= MONTH;
919 							if ((u - s) < 6)
920 								k = tm->tm_mday;
921 							else if (dig2(s, k) < 1 || k > 31)
922 								break;
923 							else
924 								flags |= DAY;
925 							if ((u - s) < 4)
926 								break;
927 							if (dig2(s, j) > 24)
928 								break;
929 							if (dig2(s, i) > 59)
930 								break;
931 							flags |= HOUR|MINUTE;
932 							if ((u - s) == 2)
933 							{
934 								dig2(s, n);
935 								flags |= SECOND;
936 							}
937 							else if (u - s)
938 								break;
939 							else if (*t != '.')
940 								n = 0;
941 							else
942 							{
943 								n = strtol(t + 1, &t, 10);
944 								flags |= SECOND;
945 								if (*t == '.')
946 								{
947 									q = 1000000000;
948 									while (isdigit(*++t))
949 										p += (*t - '0') * (q /= 10);
950 									set |= NSEC;
951 								}
952 							}
953 							if (n > (59 + TM_MAXLEAP))
954 								break;
955 						}
956 					save:
957 						tm->tm_hour = j;
958 						tm->tm_min = i;
959 						tm->tm_sec = n;
960 						tm->tm_nsec = p;
961 					save_yymmdd:
962 						tm->tm_mday = k;
963 					save_yymm:
964 						tm->tm_mon = l - 1;
965 						tm->tm_year = m;
966 						s = t;
967 						set |= flags;
968 						continue;
969 					}
970 					for (s = t; skip[*s]; s++);
971 					message((-1, "AHA#%d s=\"%s\"", __LINE__, s));
972 					if (*s == ':' || *s == '.' && ((set|state) & (YEAR|MONTH|DAY|HOUR)) == (YEAR|MONTH|DAY))
973 					{
974 						c = *s;
975 						if ((state & HOUR) || n > 24)
976 							break;
977 						while (isspace(*++s) || *s == '_');
978 						if (!isdigit(*s))
979 							break;
980 						i = n;
981 						n = strtol(s, &t, 10);
982 						for (s = t; isspace(*s) || *s == '_'; s++);
983 						if (n > 59)
984 							break;
985 						j = n;
986 						m = 0;
987 						if (*s == c)
988 						{
989 							while (isspace(*++s) || *s == '_');
990 							if (!isdigit(*s))
991 								break;
992 							n = strtol(s, &t, 10);
993 							s = t;
994 							if (n > (59 + TM_MAXLEAP))
995 								break;
996 							set |= SECOND;
997 							while (isspace(*s))
998 								s++;
999 							if (*s == '.')
1000 							{
1001 								q = 1000000000;
1002 								while (isdigit(*++s))
1003 									m += (*s - '0') * (q /= 10);
1004 								set |= NSEC;
1005 							}
1006 						}
1007 						else
1008 							n = 0;
1009 						set |= HOUR|MINUTE;
1010 						skip[':'] = 1;
1011 						k = tm->tm_hour;
1012 						tm->tm_hour = i;
1013 						l = tm->tm_min;
1014 						tm->tm_min = j;
1015 						tm->tm_sec = n;
1016 						tm->tm_nsec = m;
1017 						while (isspace(*s))
1018 							s++;
1019 						switch (tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_MERIDIAN, 2))
1020 						{
1021 						case TM_MERIDIAN:
1022 							s = t;
1023 							if (i == 12)
1024 								tm->tm_hour = i = 0;
1025 							break;
1026 						case TM_MERIDIAN+1:
1027 							if (i < 12)
1028 								tm->tm_hour = i += 12;
1029 							break;
1030 						}
1031 						if (f >= 0 || (state & (LAST|NEXT)))
1032 						{
1033 							message((-1, "AHA#%d f=%d i=%d j=%d k=%d l=%d", __LINE__, f, i, j, k, l));
1034 							state &= ~HOLD;
1035 							if (f < 0)
1036 							{
1037 								if (state & LAST)
1038 									f = -1;
1039 								else if (state & NEXT)
1040 									f = 1;
1041 								else
1042 									f = 0;
1043 							}
1044 							if (f > 0)
1045 							{
1046 								if (i > k || i == k && j > l)
1047 									f--;
1048 							}
1049 							else if (i < k || i == k && j < l)
1050 								f++;
1051 							if (f > 0)
1052 							{
1053 								tm->tm_hour += f * 24;
1054 								while (tm->tm_hour >= 24)
1055 								{
1056 									tm->tm_hour -= 24;
1057 									tm->tm_mday++;
1058 								}
1059 							}
1060 							else if (f < 0)
1061 							{
1062 								tm->tm_hour += f * 24;
1063 								while (tm->tm_hour < 24)
1064 								{
1065 									tm->tm_hour += 24;
1066 									tm->tm_mday--;
1067 								}
1068 							}
1069 						}
1070 						continue;
1071 					}
1072 				}
1073 			}
1074 		}
1075 		for (;;)
1076 		{
1077 			message((-1, "AHA#%d s=\"%s\"", __LINE__, s));
1078 			if (*s == '-' || *s == '+')
1079 			{
1080 				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))))
1081 					break;
1082 				s++;
1083 			}
1084 			else if (skip[*s])
1085 				s++;
1086 			else
1087 				break;
1088 		}
1089 		if (isalpha(*s))
1090 		{
1091 			if (n > 0)
1092 			{
1093 				x = s;
1094 				q = *s++;
1095 				message((-1, "AHA#%d n=%d q='%c'", __LINE__, n, q));
1096 				if (isalpha(*s))
1097 				{
1098 					q <<= 8;
1099 					q |= *s++;
1100 					if (isalpha(*s))
1101 					{
1102 						if (tmlex(s, &t, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES, NiL, 0) >= 0)
1103 							s = t;
1104 						if (isalpha(*s))
1105 						{
1106 							q <<= 8;
1107 							q |= *s++;
1108 							if (isalpha(*s))
1109 							{
1110 								q <<= 8;
1111 								q |= *s++;
1112 								if (isalpha(*s))
1113 									q = 0;
1114 							}
1115 						}
1116 					}
1117 				}
1118 				switch (q)
1119 				{
1120 				case K1('y'):
1121 				case K1('Y'):
1122 				case K2('y','r'):
1123 				case K2('Y','R'):
1124 					tm->tm_year += n;
1125 					set |= YEAR;
1126 					continue;
1127 				case K1('M'):
1128 				case K2('m','o'):
1129 				case K2('M','O'):
1130 					tm->tm_mon += n;
1131 					set |= MONTH;
1132 					continue;
1133 				case K1('w'):
1134 				case K1('W'):
1135 				case K2('w','k'):
1136 				case K2('W','K'):
1137 					tm->tm_mday += n * 7;
1138 					set |= DAY;
1139 					continue;
1140 				case K1('d'):
1141 				case K1('D'):
1142 				case K2('d','a'):
1143 				case K2('d','y'):
1144 				case K2('D','A'):
1145 				case K2('D','Y'):
1146 					tm->tm_mday += n;
1147 					set |= DAY;
1148 					continue;
1149 				case K1('h'):
1150 				case K1('H'):
1151 				case K2('h','r'):
1152 				case K2('H','R'):
1153 					tm->tm_hour += n;
1154 					set |= HOUR;
1155 					continue;
1156 				case K1('m'):
1157 				case K2('m','n'):
1158 				case K2('M','N'):
1159 					tm->tm_min += n;
1160 					set |= MINUTE;
1161 					continue;
1162 				case K1('s'):
1163 				case K2('s','c'):
1164 				case K1('S'):
1165 				case K2('S','C'):
1166 					tm->tm_sec += n;
1167 					set |= SECOND;
1168 					continue;
1169 				case K2('m','s'):
1170 				case K3('m','s','c'):
1171 				case K4('m','s','e','c'):
1172 				case K2('M','S'):
1173 				case K3('M','S','C'):
1174 				case K4('M','S','E','C'):
1175 					tm->tm_nsec += n * 1000000L;
1176 					continue;
1177 				case K1('u'):
1178 				case K2('u','s'):
1179 				case K3('u','s','c'):
1180 				case K4('u','s','e','c'):
1181 				case K1('U'):
1182 				case K2('U','S'):
1183 				case K3('U','S','C'):
1184 				case K4('U','S','E','C'):
1185 					tm->tm_nsec += n * 1000L;
1186 					continue;
1187 				case K2('n','s'):
1188 				case K3('n','s','c'):
1189 				case K4('n','s','e','c'):
1190 				case K2('N','S'):
1191 				case K3('N','S','C'):
1192 				case K4('N','S','E','C'):
1193 					tm->tm_nsec += n;
1194 					continue;
1195 				}
1196 				s = x;
1197 			}
1198 			if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0)
1199 			{
1200 				if (tm_data.lex[j] == TM_PARTS || n < 1000)
1201 				{
1202 					s = t;
1203 					switch (tm_data.lex[j])
1204 					{
1205 					case TM_EXACT:
1206 						state |= HOLD|EXACT;
1207 						set &= ~(EXACT|LAST|NEXT|THIS);
1208 						set |= state & (EXACT|LAST|NEXT|THIS);
1209 						continue;
1210 					case TM_LAST:
1211 						state |= HOLD|LAST;
1212 						set &= ~(EXACT|LAST|NEXT|THIS);
1213 						set |= state & (EXACT|LAST|NEXT|THIS);
1214 						continue;
1215 					case TM_THIS:
1216 						state |= HOLD|THIS;
1217 						set &= ~(EXACT|LAST|NEXT|THIS);
1218 						set |= state & (EXACT|LAST|NEXT|THIS);
1219 						n = 0;
1220 						continue;
1221 					case TM_NEXT:
1222 						/*
1223 						 * disambiguate english "last ... in"
1224 						 */
1225 
1226 						if (!((state|set) & LAST))
1227 						{
1228 							state |= HOLD|NEXT;
1229 							set &= ~(EXACT|LAST|NEXT|THIS);
1230 							set |= state & (EXACT|LAST|NEXT|THIS);
1231 							continue;
1232 						}
1233 						/*FALLTHROUGH*/
1234 					case TM_FINAL:
1235 						state |= HOLD|THIS|FINAL;
1236 						set &= ~(EXACT|LAST|NEXT|THIS);
1237 						set |= state & (EXACT|LAST|NEXT|THIS|FINAL);
1238 						continue;
1239 					case TM_WORK:
1240 						message((-1, "AHA#%d WORK", __LINE__));
1241 						state |= WORK;
1242 						set |= DAY;
1243 						if (state & LAST)
1244 						{
1245 							state &= ~LAST;
1246 							set &= ~LAST;
1247 							state |= FINAL;
1248 							set |= FINAL;
1249 						}
1250 						goto clear_hour;
1251 					case TM_ORDINAL:
1252 						j += TM_ORDINALS - TM_ORDINAL;
1253 						message((-1, "AHA#%d j=%d", __LINE__, j));
1254 						/*FALLTHROUGH*/
1255 					case TM_ORDINALS:
1256 						n = j - TM_ORDINALS + 1;
1257 						message((-1, "AHA#%d n=%d", __LINE__, n));
1258 						goto ordinal;
1259 					case TM_MERIDIAN:
1260 						if (f >= 0)
1261 							f++;
1262 						else if (state & LAST)
1263 							f = -1;
1264 						else if (state & THIS)
1265 							f = 1;
1266 						else if (state & NEXT)
1267 							f = 2;
1268 						else
1269 							f = 0;
1270 						if (n > 0)
1271 						{
1272 							if (n > 24)
1273 								goto done;
1274 							tm->tm_hour = n;
1275 						}
1276 						for (k = tm->tm_hour; k < 0; k += 24);
1277 						k %= 24;
1278 						if (j == TM_MERIDIAN)
1279 						{
1280 							if (k == 12)
1281 								tm->tm_hour -= 12;
1282 						}
1283 						else if (k < 12)
1284 							tm->tm_hour += 12;
1285 						if (n > 0)
1286 							goto clear_min;
1287 						continue;
1288 					case TM_DAY_ABBREV:
1289 						j += TM_DAY - TM_DAY_ABBREV;
1290 						/*FALLTHROUGH*/
1291 					case TM_DAY:
1292 					case TM_PARTS:
1293 					case TM_HOURS:
1294 						state |= set & (EXACT|LAST|NEXT|THIS);
1295 						if (!(state & (LAST|NEXT|THIS)))
1296 							for (;;)
1297 							{
1298 								while (skip[*s])
1299 									s++;
1300 								if ((k = tmlex(s, &t, tm_info.format + TM_LAST, TM_NOISE - TM_LAST, NiL, 0)) >= 0)
1301 								{
1302 									s = t;
1303 									if (k <= 2)
1304 										state |= LAST;
1305 									else if (k <= 5)
1306 										state |= THIS;
1307 									else if (k <= 8)
1308 										state |= NEXT;
1309 									else
1310 										state |= EXACT;
1311 								}
1312 								else
1313 								{
1314 									state |= (n > 0) ? NEXT : THIS;
1315 									break;
1316 								}
1317 								set &= ~(EXACT|LAST|NEXT|THIS);
1318 								set |= state & (EXACT|LAST|NEXT|THIS);
1319 							}
1320 						/*FALLTHROUGH*/
1321 					case TM_DAYS:
1322 						message((-1, "AHA#%d n=%d j=%d f=%d state=" FFMT, __LINE__, n, j, f, FLAGS(state)));
1323 						if (n == -1)
1324 						{
1325 							/*
1326 							 * disambiguate english "second"
1327 							 */
1328 
1329 							if (j == TM_PARTS && f == -1)
1330 							{
1331 								state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1332 								n = 2;
1333 								goto ordinal;
1334 							}
1335 							n = 1;
1336 						}
1337 
1338 						/*
1339 						 * disambiguate "last" vs. { "previous" "final" }
1340 						 */
1341 
1342 						while (isspace(*s))
1343 							s++;
1344 						message((-1, "AHA#%d disambiguate LAST s='%s'", __LINE__, s));
1345 						if ((k = tmlex(s, &t, tm_info.format + TM_NEXT, TM_EXACT - TM_NEXT, NiL, 0)) >= 0 || (k = tmlex(s, &t, tm_info.format + TM_PARTS + 3, 1, NiL, 0)) >= 0)
1346 						{
1347 							s = t;
1348 							if (state & LAST)
1349 							{
1350 								state &= ~LAST;
1351 								set &= ~LAST;
1352 								state |= FINAL;
1353 								set |= FINAL;
1354 								message((-1, "AHA#%d LAST => FINAL", __LINE__));
1355 							}
1356 							else
1357 								state &= ~(THIS|NEXT);
1358 						}
1359 						message((-1, "AHA#%d disambiguate LAST k=%d", __LINE__, k));
1360 						if (state & LAST)
1361 							n = -n;
1362 						else if (!(state & NEXT))
1363 							n--;
1364 						m = (f > 0) ? f * n : n;
1365 						message((-1, "AHA#%d f=%d n=%d i=%d j=%d k=%d l=%d m=%d state=" FFMT, __LINE__, f, n, i, j, k, l, m, FLAGS(state)));
1366 						switch (j)
1367 						{
1368 						case TM_DAYS+0:
1369 							tm->tm_mday--;
1370 							set |= DAY;
1371 							goto clear_hour;
1372 						case TM_DAYS+1:
1373 							set |= DAY;
1374 							goto clear_hour;
1375 						case TM_DAYS+2:
1376 							tm->tm_mday++;
1377 							set |= DAY;
1378 							goto clear_hour;
1379 						case TM_PARTS+0:
1380 							set |= SECOND;
1381 							if ((m < 0 ? -m : m) > (365L*24L*60L*60L))
1382 							{
1383 								now = tmxtime(tm, zone) + tmxsns(m, 0);
1384 								goto reset;
1385 							}
1386 							tm->tm_sec += m;
1387 							goto clear_nsec;
1388 						case TM_PARTS+1:
1389 							tm->tm_min += m;
1390 							set |= MINUTE;
1391 							goto clear_sec;
1392 						case TM_PARTS+2:
1393 							tm->tm_hour += m;
1394 							set |= MINUTE;
1395 							goto clear_min;
1396 						case TM_PARTS+3:
1397 							message((-1, "AHA#%d DAY m=%d n=%d%s", __LINE__, m, n, (state & LAST) ? " LAST" : ""));
1398 							if ((state & (LAST|NEXT|THIS)) == LAST)
1399 								tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1400 							else if (state & ORDINAL)
1401 								tm->tm_mday = m + 1;
1402 							else
1403 								tm->tm_mday += m;
1404 							if (!(set & (FINAL|WORK)))
1405 								set |= HOUR;
1406 							goto clear_hour;
1407 						case TM_PARTS+4:
1408 							tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1409 							tm->tm_mday += 7 * m - tm->tm_wday + 1;
1410 							set |= DAY;
1411 							goto clear_hour;
1412 						case TM_PARTS+5:
1413 							tm->tm_mon += m;
1414 							set |= MONTH;
1415 							goto clear_mday;
1416 						case TM_PARTS+6:
1417 							tm->tm_year += m;
1418 							goto clear_mon;
1419 						case TM_HOURS+0:
1420 							tm->tm_mday += m;
1421 							set |= DAY;
1422 							goto clear_hour;
1423 						case TM_HOURS+1:
1424 							tm->tm_mday += m;
1425 							tm->tm_hour = 6;
1426 							set |= HOUR;
1427 							goto clear_min;
1428 						case TM_HOURS+2:
1429 							tm->tm_mday += m;
1430 							tm->tm_hour = 12;
1431 							set |= HOUR;
1432 							goto clear_min;
1433 						case TM_HOURS+3:
1434 							tm->tm_mday += m;
1435 							tm->tm_hour = 18;
1436 							set |= HOUR;
1437 							goto clear_min;
1438 						}
1439 						if (m >= 0 && (state & ORDINAL))
1440 							tm->tm_mday = 1;
1441 						tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1442 						day = j -= TM_DAY;
1443 						if (!dir)
1444 							dir = m;
1445 						message((-1, "AHA#%d j=%d m=%d", __LINE__, j, m));
1446 						j -= tm->tm_wday;
1447 						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));
1448 						if (state & (LAST|NEXT|THIS))
1449 						{
1450 							if (state & ORDINAL)
1451 							{
1452 								while (isspace(*s))
1453 									s++;
1454 								if (isdigit(*s) || tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0) >= 0)
1455 								{
1456 									state &= ~(LAST|NEXT|THIS);
1457 									goto clear_hour;
1458 								}
1459 							}
1460 							if (j < 0)
1461 								j += 7;
1462 						}
1463 						else if (j > 0)
1464 							j -= 7;
1465 						message((-1, "AHA#%d day=%d mday=%d f=%d m=%d j=%d state=" FFMT, __LINE__, day, tm->tm_mday, f, m, j, FLAGS(state)));
1466 						set |= DAY;
1467 						if (set & (FINAL|WORK))
1468 							goto clear_hour;
1469 						else if (state & (LAST|NEXT|THIS))
1470 						{
1471 							if (f >= 0)
1472 								day = -1;
1473 							else if (m > 0 && (state & (NEXT|YEAR|MONTH)) == NEXT && j >= 0)
1474 								m--;
1475 							tm->tm_mday += j + m * 7;
1476 							set &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1477 							state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1478 							if (!(state & EXACT))
1479 								goto clear_hour;
1480 						}
1481 						continue;
1482 					case TM_MONTH_ABBREV:
1483 						j += TM_MONTH - TM_MONTH_ABBREV;
1484 						/*FALLTHROUGH*/
1485 					case TM_MONTH:
1486 						if (state & MONTH)
1487 							goto done;
1488 						state |= MONTH;
1489 						i = tm->tm_mon;
1490 						tm->tm_mon = j - TM_MONTH;
1491 						if (n < 0)
1492 						{
1493 							while (skip[*s])
1494 								s++;
1495 							if (isdigit(*s))
1496 							{
1497 								n = strtol(s, &t, 10);
1498 								if (n <= 31 && *t != ':')
1499 									s = t;
1500 								else
1501 									n = -1;
1502 							}
1503 						}
1504 						if (n >= 0)
1505 						{
1506 							if (n > 31)
1507 								goto done;
1508 							state |= DAY|MDAY;
1509 							tm->tm_mday = n;
1510 							if (f > 0)
1511 								tm->tm_year += f;
1512 						}
1513 						if (state & (LAST|NEXT|THIS))
1514 						{
1515 							n = i;
1516 							goto rel_month;
1517 						}
1518 						continue;
1519 					case TM_UT:
1520 						if (state & ZONE)
1521 							goto done;
1522 						state |= ZONE;
1523 						zone = tmgoff(s, &t, 0);
1524 						s = t;
1525 						continue;
1526 					case TM_DT:
1527 						if (!dst)
1528 							goto done;
1529 						if (!(state & ZONE))
1530 						{
1531 							dst = tm->tm_zone->dst;
1532 							zone = tm->tm_zone->west;
1533 						}
1534 						zone += tmgoff(s, &t, dst);
1535 						s = t;
1536 						dst = 0;
1537 						state |= ZONE;
1538 						continue;
1539 					case TM_NOISE:
1540 						continue;
1541 					}
1542 				}
1543 			}
1544 			if (n < 1000)
1545 			{
1546 				if (!(state & ZONE) && (zp = tmzone(s, &t, type, &dst)))
1547 				{
1548 					s = t;
1549 					zone = zp->west + dst;
1550 					tm_info.date = zp;
1551 					state |= ZONE;
1552 					if (n < 0)
1553 						continue;
1554 				}
1555 				else if (!type && (zp = tmtype(s, &t)))
1556 				{
1557 					s = t;
1558 					type = zp->type;
1559 					if (n < 0)
1560 						continue;
1561 				}
1562 				state |= BREAK;
1563 			}
1564 		}
1565 		else if (*s == '/')
1566 		{
1567 			if (!(state & (YEAR|MONTH)) && n >= 1969 && n < 3000 && (i = strtol(s + 1, &t, 10)) > 0 && i <= 12)
1568 			{
1569 				state |= YEAR;
1570 				tm->tm_year = n - 1900;
1571 				s = t;
1572 				i--;
1573 			}
1574 			else
1575 			{
1576 				if ((state & MONTH) || n <= 0 || n > 31)
1577 					break;
1578 				if (isalpha(*++s))
1579 				{
1580 					if ((i = tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0)) < 0)
1581 						break;
1582 					if (i >= TM_MONTH)
1583 						i -= TM_MONTH;
1584 					s = t;
1585 				}
1586 				else
1587 				{
1588 					i = n - 1;
1589 					n = strtol(s, &t, 10);
1590 					s = t;
1591 					if (n <= 0 || n > 31)
1592 						break;
1593 					if (*s == '/' && !isdigit(*(s + 1)))
1594 						break;
1595 				}
1596 				state |= DAY;
1597 				tm->tm_mday = n;
1598 			}
1599 			state |= MONTH;
1600 			n = tm->tm_mon;
1601 			tm->tm_mon = i;
1602 			if (*s == '/')
1603 			{
1604 				n = strtol(++s, &t, 10);
1605 				w = t - s;
1606 				s = t;
1607 				if (*s == '/' || *s == ':' || *s == '-' || *s == '_')
1608 					s++;
1609 			}
1610 			else
1611 			{
1612 				if (state & (LAST|NEXT|THIS))
1613 				{
1614 				rel_month:
1615 					if (state & LAST)
1616 						tm->tm_year -= (tm->tm_mon < n) ? 0 : 1;
1617 					else
1618 						tm->tm_year += ((state & NEXT) ? 1 : 0) + ((tm->tm_mon < n) ? 1 : 0);
1619 					if (state & MDAY)
1620 						goto clear_hour;
1621 					set &= ~(LAST|NEXT|THIS); /*AHA*/
1622 					state &= ~(LAST|NEXT|THIS); /*AHA*/
1623 					goto clear_mday;
1624 				}
1625 				continue;
1626 			}
1627 		}
1628 		if (n < 0 || w > 4)
1629 			break;
1630 		if (w == 4)
1631 		{
1632 			if ((state & YEAR) || n < 1969 || n >= 3000)
1633 				break;
1634 			state |= YEAR;
1635 			tm->tm_year = n - 1900;
1636 		}
1637 		else if (w == 3)
1638 		{
1639 			if (state & (MONTH|MDAY|WDAY))
1640 				break;
1641 			state |= MONTH|DAY|MDAY;
1642 			tm->tm_mon = 0;
1643 			tm->tm_mday = n;
1644 		}
1645 		else if (w == 2 && !(state & YEAR))
1646 		{
1647 			state |= YEAR;
1648 			if (n < TM_WINDOW)
1649 				n += 100;
1650 			tm->tm_year = n;
1651 		}
1652 		else if (!(state & MONTH) && n >= 1 && n <= 12)
1653 		{
1654 			state |= MONTH;
1655 			tm->tm_mon = n - 1;
1656 		}
1657 		else if (!(state & (MDAY|WDAY)) && n >= 1 && n <= 31)
1658 		{
1659 			state |= DAY|MDAY|WDAY;
1660 			tm->tm_mday = n;
1661 		}
1662 		else
1663 			break;
1664 		if (state & BREAK)
1665 		{
1666 			last = t;
1667 			break;
1668 		}
1669 		continue;
1670 	clear_mon:
1671 		if ((set|state) & (EXACT|MONTH))
1672 			continue;
1673 		tm->tm_mon = 0;
1674 	clear_mday:
1675 		set |= MONTH;
1676 		if ((set|state) & (EXACT|DAY|HOUR))
1677 			continue;
1678 		tm->tm_mday = 1;
1679 	clear_hour:
1680 		message((-1, "AHA#%d DAY", __LINE__));
1681 		set |= DAY;
1682 		if ((set|state) & (EXACT|HOUR))
1683 			continue;
1684 		tm->tm_hour = 0;
1685 	clear_min:
1686 		set |= HOUR;
1687 		if ((set|state) & (EXACT|MINUTE))
1688 			continue;
1689 		tm->tm_min = 0;
1690 	clear_sec:
1691 		set |= MINUTE;
1692 		if ((set|state) & (EXACT|SECOND))
1693 			continue;
1694 		tm->tm_sec = 0;
1695 	clear_nsec:
1696 		set |= SECOND;
1697 		if ((set|state) & (EXACT|NSEC))
1698 			continue;
1699 		tm->tm_nsec = 0;
1700 	}
1701  done:
1702 	if (day >= 0 && !(state & (MDAY|WDAY)))
1703 	{
1704 		message((-1, "AHA#%d day=%d dir=%d state=" FFMT, __LINE__, day, dir, FLAGS(state)));
1705 		tmfix(tm);
1706 		m = dir;
1707 		if (state & MONTH)
1708 			tm->tm_mday = 1;
1709 		else if (m < 0)
1710 			m++;
1711 		tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1712 		j = day - tm->tm_wday;
1713 		if (j < 0)
1714 			j += 7;
1715 		tm->tm_mday += j + m * 7;
1716 		if (state & FINAL)
1717 			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);
1718 	}
1719 	else if (day < 0 && (state & FINAL) && (set & DAY))
1720 	{
1721 		tmfix(tm);
1722 		tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1723 	}
1724 	if (state & WORK)
1725 	{
1726 		tm->tm_mday = (set & FINAL) ? (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))) : 1;
1727 		tmfix(tm);
1728 		message((-1, "AHA#%d WORK mday=%d wday=%d", __LINE__, tm->tm_mday, tm->tm_wday));
1729 		if (tm->tm_wday == 0 && (j = 1) || tm->tm_wday == 6 && (j = 2))
1730 		{
1731 			if ((tm->tm_mday + j) > (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))))
1732 				j -= 3;
1733 			tm->tm_mday += j;
1734 		}
1735 	}
1736 	now = tmxtime(tm, zone);
1737 	if (tm->tm_year <= 70 && tmxsec(now) > 31536000)
1738 	{
1739 		now = 0;
1740 		last = (char*)o;
1741 	}
1742 	if (e)
1743 		*e = last;
1744 	return now;
1745 }
1746