xref: /titanic_50/usr/src/lib/libast/common/tm/tmxdate.c (revision bf994817a71d4ac680198e25fe79d13c247306e0)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2010 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 #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 			if ((w == 2 || w == 4) && (*u == 'W' || *u == 'w') && isdigit(*(u + 1)))
709 			{
710 				if (w == 4)
711 				{
712 					if ((n -= 1900) < TM_WINDOW)
713 						break;
714 				}
715 				else if (n < TM_WINDOW)
716 					n += 100;
717 				m = n;
718 				n = strtol(++u, &t, 10);
719 				if ((i = (t - u)) < 2 || i > 3)
720 					break;
721 				if (i == 3)
722 				{
723 					k = n % 10;
724 					n /= 10;
725 				}
726 				else if (*t != '-')
727 					k = 1;
728 				else if (*++t && dig1(t, k) < 1 || k > 7)
729 					break;
730 				if (n < 0 || n > 53)
731 					break;
732 				if (k == 7)
733 					k = 0;
734 				tm->tm_year = m;
735 				tmweek(tm, 2, n, k);
736 				set |= YEAR|MONTH|DAY;
737 				s = t;
738 				continue;
739 			}
740 			else if (w == 6 || w == 8 && (n / 1000000) > 12)
741 			{
742 				t = (char*)s;
743 				flags = 0;
744 				if (w == 8 || w == 6 && *u != 'T' && *u != 't')
745 				{
746 					dig4(t, m);
747 					if ((m -= 1900) < TM_WINDOW)
748 						break;
749 				}
750 				else
751 				{
752 					dig2(t, m);
753 					if (m < TM_WINDOW)
754 						m += 100;
755 				}
756 				flags |= YEAR;
757 				if (dig2(t, l) <= 0 || l > 12)
758 					break;
759 				flags |= MONTH;
760 				if (*t != 'T' && *t != 't' || !isdigit(*++t))
761 				{
762 					if (w == 6)
763 						goto save_yymm;
764 					if (dig2(t, k) < 1 || k > 31)
765 						break;
766 					flags |= DAY;
767 					goto save_yymmdd;
768 				}
769 				n = strtol(s = t, &t, 0);
770 				if ((t - s) < 2)
771 					break;
772 				if (dig2(s, j) > 24)
773 					break;
774 				if ((t - s) < 2)
775 				{
776 					if ((t - s) == 1 || *t++ != '-')
777 						break;
778 					n = strtol(s = t, &t, 0);
779 					if ((t - s) < 2)
780 						break;
781 				}
782 				if (dig2(s, i) > 59)
783 					break;
784 				flags |= HOUR|MINUTE;
785 				if ((t - s) == 2)
786 				{
787 					if (dig2(s, n) > (59 + TM_MAXLEAP))
788 						break;
789 					flags |= SECOND;
790 				}
791 				else if (t - s)
792 					break;
793 				else
794 					n = 0;
795 				p = 0;
796 				if (*t == '.')
797 				{
798 					q = 1000000000;
799 					while (isdigit(*++t))
800 						p += (*t - '0') * (q /= 10);
801 					set |= NSEC;
802 				}
803 				if (n > (59 + TM_MAXLEAP))
804 					break;
805 				goto save;
806 			}
807 			else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0)
808 			{
809 				message((-1, "AHA#%d n=%d", __LINE__, n));
810  ordinal:
811 				if (n)
812 					n--;
813 				message((-1, "AHA#%d n=%d", __LINE__, n));
814 				state |= ((f = n) ? NEXT : THIS)|ORDINAL;
815 				set &= ~(EXACT|LAST|NEXT|THIS);
816 				set |= state & (EXACT|LAST|NEXT|THIS);
817 				for (s = t; skip[*s]; s++);
818 				if (isdigit(*s))
819 				{
820 					if (n = strtol(s, &t, 10))
821 						n--;
822 					s = t;
823 					if (*s == '_')
824 						s++;
825 				}
826 				else
827 					n = -1;
828 				dir = f;
829 				message((-1, "AHA#%d f=%d n=%d state=" FFMT, __LINE__, f, n, FLAGS(state)));
830 			}
831 			else
832 			{
833 				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))
834 				{
835 					/*
836 					 * various { date(1) touch(1) } formats
837 					 *
838 					 *	[[cc]yy[mm]]ddhhmm[.ss[.nn...]]
839 					 *	[cc]yyjjj
840 					 *	hhmm[.ss[.nn...]]
841 					 */
842 
843 					flags = 0;
844 					if (state & CCYYMMDDHHMMSS)
845 						break;
846 					state |= CCYYMMDDHHMMSS;
847 					p = 0;
848 					if ((i == 7 || i == 5) && (!*t || *t == 'Z' || *t == 'z'))
849 					{
850 						if (i == 7)
851 						{
852 							dig4(s, m);
853 							if ((m -= 1900) < TM_WINDOW)
854 								break;
855 						}
856 						else if (dig2(s, m) < TM_WINDOW)
857 							m += 100;
858 						dig3(s, k);
859 						l = 1;
860 						j = 0;
861 						i = 0;
862 						n = 0;
863 						flags |= MONTH;
864 					}
865 					else if (i & 1)
866 						break;
867 					else
868 					{
869 						u = t;
870 						if (i == 12)
871 						{
872 							x = s;
873 							dig2(x, m);
874 							if (m <= 12)
875 							{
876 								u -= 4;
877 								i -= 4;
878 								x = s + 8;
879 								dig4(x, m);
880 							}
881 							else
882 								dig4(s, m);
883 							if (m < 1969 || m >= 3000)
884 								break;
885 							m -= 1900;
886 						}
887 						else if (i == 10)
888 						{
889 							x = s;
890 							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))
891 								dig2(s, m);
892 							else
893 							{
894 								u -= 2;
895 								i -= 2;
896 								x = s + 8;
897 								dig2(x, m);
898 							}
899 							if (m < TM_WINDOW)
900 								m += 100;
901 						}
902 						else
903 							m = tm->tm_year;
904 						if ((u - s) < 8)
905 							l = tm->tm_mon + 1;
906 						else if (dig2(s, l) <= 0 || l > 12)
907 							break;
908 						else
909 							flags |= MONTH;
910 						if ((u - s) < 6)
911 							k = tm->tm_mday;
912 						else if (dig2(s, k) < 1 || k > 31)
913 							break;
914 						else
915 							flags |= DAY;
916 						if ((u - s) < 4)
917 							break;
918 						if (dig2(s, j) > 24)
919 							break;
920 						if (dig2(s, i) > 59)
921 							break;
922 						flags |= HOUR|MINUTE;
923 						if ((u - s) == 2)
924 						{
925 							dig2(s, n);
926 							flags |= SECOND;
927 						}
928 						else if (u - s)
929 							break;
930 						else if (*t != '.')
931 							n = 0;
932 						else
933 						{
934 							n = strtol(t + 1, &t, 10);
935 							flags |= SECOND;
936 							if (*t == '.')
937 							{
938 								q = 1000000000;
939 								while (isdigit(*++t))
940 									p += (*t - '0') * (q /= 10);
941 								set |= NSEC;
942 							}
943 						}
944 						if (n > (59 + TM_MAXLEAP))
945 							break;
946 					}
947 				save:
948 					tm->tm_hour = j;
949 					tm->tm_min = i;
950 					tm->tm_sec = n;
951 					tm->tm_nsec = p;
952 				save_yymmdd:
953 					tm->tm_mday = k;
954 				save_yymm:
955 					tm->tm_mon = l - 1;
956 					tm->tm_year = m;
957 					s = t;
958 					set |= flags;
959 					continue;
960 				}
961 				for (s = t; skip[*s]; s++);
962 				if (*s == ':' || *s == '.' && ((set|state) & (YEAR|MONTH|DAY|HOUR)) == (YEAR|MONTH|DAY))
963 				{
964 					c = *s;
965 					if ((state & HOUR) || n > 24)
966 						break;
967 					while (isspace(*++s) || *s == '_');
968 					if (!isdigit(*s))
969 						break;
970 					i = n;
971 					n = strtol(s, &t, 10);
972 					for (s = t; isspace(*s) || *s == '_'; s++);
973 					if (n > 59)
974 						break;
975 					j = n;
976 					m = 0;
977 					if (*s == c)
978 					{
979 						while (isspace(*++s) || *s == '_');
980 						if (!isdigit(*s))
981 							break;
982 						n = strtol(s, &t, 10);
983 						s = t;
984 						if (n > (59 + TM_MAXLEAP))
985 							break;
986 						set |= SECOND;
987 						while (isspace(*s))
988 							s++;
989 						if (*s == '.')
990 						{
991 							q = 1000000000;
992 							while (isdigit(*++s))
993 								m += (*s - '0') * (q /= 10);
994 							set |= NSEC;
995 						}
996 					}
997 					else
998 						n = 0;
999 					set |= HOUR|MINUTE;
1000 					skip[':'] = 1;
1001 					k = tm->tm_hour;
1002 					tm->tm_hour = i;
1003 					l = tm->tm_min;
1004 					tm->tm_min = j;
1005 					tm->tm_sec = n;
1006 					tm->tm_nsec = m;
1007 					while (isspace(*s))
1008 						s++;
1009 					switch (tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_MERIDIAN, 2))
1010 					{
1011 					case TM_MERIDIAN:
1012 						s = t;
1013 						if (i == 12)
1014 							tm->tm_hour = i = 0;
1015 						break;
1016 					case TM_MERIDIAN+1:
1017 						if (i < 12)
1018 							tm->tm_hour = i += 12;
1019 						break;
1020 					}
1021 					if (f >= 0 || (state & (LAST|NEXT)))
1022 					{
1023 						message((-1, "AHA#%d f=%d i=%d j=%d k=%d l=%d", __LINE__, f, i, j, k, l));
1024 						state &= ~HOLD;
1025 						if (f < 0)
1026 						{
1027 							if (state & LAST)
1028 								f = -1;
1029 							else if (state & NEXT)
1030 								f = 1;
1031 							else
1032 								f = 0;
1033 						}
1034 						if (f > 0)
1035 						{
1036 							if (i > k || i == k && j > l)
1037 								f--;
1038 						}
1039 						else if (i < k || i == k && j < l)
1040 							f++;
1041 						if (f > 0)
1042 						{
1043 							tm->tm_hour += f * 24;
1044 							while (tm->tm_hour >= 24)
1045 							{
1046 								tm->tm_hour -= 24;
1047 								tm->tm_mday++;
1048 							}
1049 						}
1050 						else if (f < 0)
1051 						{
1052 							tm->tm_hour += f * 24;
1053 							while (tm->tm_hour < 24)
1054 							{
1055 								tm->tm_hour += 24;
1056 								tm->tm_mday--;
1057 							}
1058 						}
1059 					}
1060 					continue;
1061 				}
1062 			}
1063 		}
1064 		for (;;)
1065 		{
1066 			if (*s == '-' || *s == '+')
1067 			{
1068 				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))))
1069 					break;
1070 				s++;
1071 			}
1072 			else if (skip[*s])
1073 				s++;
1074 			else
1075 				break;
1076 		}
1077 		if (isalpha(*s))
1078 		{
1079 			if (n > 0)
1080 			{
1081 				x = s;
1082 				q = *s++;
1083 				if (isalpha(*s))
1084 				{
1085 					q <<= 8;
1086 					q |= *s++;
1087 					if (isalpha(*s))
1088 					{
1089 						if (tmlex(s, &t, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES, NiL, 0) >= 0)
1090 							s = t;
1091 						if (isalpha(*s))
1092 						{
1093 							q <<= 8;
1094 							q |= *s++;
1095 							if (isalpha(*s))
1096 							{
1097 								q <<= 8;
1098 								q |= *s++;
1099 								if (isalpha(*s))
1100 									q = 0;
1101 							}
1102 						}
1103 					}
1104 				}
1105 				switch (q)
1106 				{
1107 				case K1('y'):
1108 				case K1('Y'):
1109 				case K2('y','r'):
1110 				case K2('Y','R'):
1111 					tm->tm_year += n;
1112 					set |= YEAR;
1113 					continue;
1114 				case K1('M'):
1115 				case K2('m','o'):
1116 				case K2('M','O'):
1117 					tm->tm_mon += n;
1118 					set |= MONTH;
1119 					continue;
1120 				case K1('w'):
1121 				case K1('W'):
1122 				case K2('w','k'):
1123 				case K2('W','K'):
1124 					tm->tm_mday += n * 7;
1125 					set |= DAY;
1126 					continue;
1127 				case K1('d'):
1128 				case K1('D'):
1129 				case K2('d','a'):
1130 				case K2('d','y'):
1131 				case K2('D','A'):
1132 				case K2('D','Y'):
1133 					tm->tm_mday += n;
1134 					set |= DAY;
1135 					continue;
1136 				case K1('h'):
1137 				case K1('H'):
1138 				case K2('h','r'):
1139 				case K2('H','R'):
1140 					tm->tm_hour += n;
1141 					set |= HOUR;
1142 					continue;
1143 				case K1('m'):
1144 				case K2('m','n'):
1145 				case K2('M','N'):
1146 					tm->tm_min += n;
1147 					set |= MINUTE;
1148 					continue;
1149 				case K1('s'):
1150 				case K2('s','c'):
1151 				case K1('S'):
1152 				case K2('S','C'):
1153 					tm->tm_sec += n;
1154 					set |= SECOND;
1155 					continue;
1156 				case K2('m','s'):
1157 				case K3('m','s','c'):
1158 				case K4('m','s','e','c'):
1159 				case K2('M','S'):
1160 				case K3('M','S','C'):
1161 				case K4('M','S','E','C'):
1162 					tm->tm_nsec += n * 1000000L;
1163 					continue;
1164 				case K1('u'):
1165 				case K2('u','s'):
1166 				case K3('u','s','c'):
1167 				case K4('u','s','e','c'):
1168 				case K1('U'):
1169 				case K2('U','S'):
1170 				case K3('U','S','C'):
1171 				case K4('U','S','E','C'):
1172 					tm->tm_nsec += n * 1000L;
1173 					continue;
1174 				case K2('n','s'):
1175 				case K3('n','s','c'):
1176 				case K4('n','s','e','c'):
1177 				case K2('N','S'):
1178 				case K3('N','S','C'):
1179 				case K4('N','S','E','C'):
1180 					tm->tm_nsec += n;
1181 					continue;
1182 				}
1183 				s = x;
1184 			}
1185 			if (n < 1000)
1186 			{
1187 				if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0)
1188 				{
1189 					s = t;
1190 					switch (tm_data.lex[j])
1191 					{
1192 					case TM_EXACT:
1193 						state |= HOLD|EXACT;
1194 						set &= ~(EXACT|LAST|NEXT|THIS);
1195 						set |= state & (EXACT|LAST|NEXT|THIS);
1196 						continue;
1197 					case TM_LAST:
1198 						state |= HOLD|LAST;
1199 						set &= ~(EXACT|LAST|NEXT|THIS);
1200 						set |= state & (EXACT|LAST|NEXT|THIS);
1201 						continue;
1202 					case TM_THIS:
1203 						state |= HOLD|THIS;
1204 						set &= ~(EXACT|LAST|NEXT|THIS);
1205 						set |= state & (EXACT|LAST|NEXT|THIS);
1206 						n = 0;
1207 						continue;
1208 					case TM_NEXT:
1209 						/*
1210 						 * disambiguate english "last ... in"
1211 						 */
1212 
1213 						if (!((state|set) & LAST))
1214 						{
1215 							state |= HOLD|NEXT;
1216 							set &= ~(EXACT|LAST|NEXT|THIS);
1217 							set |= state & (EXACT|LAST|NEXT|THIS);
1218 							continue;
1219 						}
1220 						/*FALLTHROUGH*/
1221 					case TM_FINAL:
1222 						state |= HOLD|THIS|FINAL;
1223 						set &= ~(EXACT|LAST|NEXT|THIS);
1224 						set |= state & (EXACT|LAST|NEXT|THIS|FINAL);
1225 						continue;
1226 					case TM_WORK:
1227 						message((-1, "AHA#%d WORK", __LINE__));
1228 						state |= WORK;
1229 						set |= DAY;
1230 						if (state & LAST)
1231 						{
1232 							state &= ~LAST;
1233 							set &= ~LAST;
1234 							state |= FINAL;
1235 							set |= FINAL;
1236 						}
1237 						goto clear_hour;
1238 					case TM_ORDINAL:
1239 						j += TM_ORDINALS - TM_ORDINAL;
1240 						message((-1, "AHA#%d j=%d", __LINE__, j));
1241 						/*FALLTHROUGH*/
1242 					case TM_ORDINALS:
1243 						n = j - TM_ORDINALS + 1;
1244 						message((-1, "AHA#%d n=%d", __LINE__, n));
1245 						goto ordinal;
1246 					case TM_MERIDIAN:
1247 						if (f >= 0)
1248 							f++;
1249 						else if (state & LAST)
1250 							f = -1;
1251 						else if (state & THIS)
1252 							f = 1;
1253 						else if (state & NEXT)
1254 							f = 2;
1255 						else
1256 							f = 0;
1257 						if (n > 0)
1258 						{
1259 							if (n > 24)
1260 								goto done;
1261 							tm->tm_hour = n;
1262 						}
1263 						for (k = tm->tm_hour; k < 0; k += 24);
1264 						k %= 24;
1265 						if (j == TM_MERIDIAN)
1266 						{
1267 							if (k == 12)
1268 								tm->tm_hour -= 12;
1269 						}
1270 						else if (k < 12)
1271 							tm->tm_hour += 12;
1272 						if (n > 0)
1273 							goto clear_min;
1274 						continue;
1275 					case TM_DAY_ABBREV:
1276 						j += TM_DAY - TM_DAY_ABBREV;
1277 						/*FALLTHROUGH*/
1278 					case TM_DAY:
1279 					case TM_PARTS:
1280 					case TM_HOURS:
1281 						state |= set & (EXACT|LAST|NEXT|THIS);
1282 						if (!(state & (LAST|NEXT|THIS)))
1283 							for (;;)
1284 							{
1285 								while (skip[*s])
1286 									s++;
1287 								if ((k = tmlex(s, &t, tm_info.format + TM_LAST, TM_NOISE - TM_LAST, NiL, 0)) >= 0)
1288 								{
1289 									s = t;
1290 									if (k <= 2)
1291 										state |= LAST;
1292 									else if (k <= 5)
1293 										state |= THIS;
1294 									else if (k <= 8)
1295 										state |= NEXT;
1296 									else
1297 										state |= EXACT;
1298 								}
1299 								else
1300 								{
1301 									state |= (n > 0) ? NEXT : THIS;
1302 									break;
1303 								}
1304 								set &= ~(EXACT|LAST|NEXT|THIS);
1305 								set |= state & (EXACT|LAST|NEXT|THIS);
1306 							}
1307 						/*FALLTHROUGH*/
1308 					case TM_DAYS:
1309 						message((-1, "AHA#%d n=%d j=%d f=%d state=" FFMT, __LINE__, n, j, f, FLAGS(state)));
1310 						if (n == -1)
1311 						{
1312 							/*
1313 							 * disambiguate english "second"
1314 							 */
1315 
1316 							if (j == TM_PARTS && f == -1)
1317 							{
1318 								state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1319 								n = 2;
1320 								goto ordinal;
1321 							}
1322 							n = 1;
1323 						}
1324 
1325 						/*
1326 						 * disambiguate "last" vs. { "previous" "final" }
1327 						 */
1328 
1329 						while (isspace(*s))
1330 							s++;
1331 						message((-1, "AHA#%d disambiguate LAST s='%s'", __LINE__, s));
1332 						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)
1333 						{
1334 							s = t;
1335 							if (state & LAST)
1336 							{
1337 								state &= ~LAST;
1338 								set &= ~LAST;
1339 								state |= FINAL;
1340 								set |= FINAL;
1341 								message((-1, "AHA#%d LAST => FINAL", __LINE__));
1342 							}
1343 							else
1344 								state &= ~(THIS|NEXT);
1345 						}
1346 						message((-1, "AHA#%d disambiguate LAST k=%d", __LINE__, k));
1347 						if (state & LAST)
1348 							n = -n;
1349 						else if (!(state & NEXT))
1350 							n--;
1351 						m = (f > 0) ? f * n : n;
1352 						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)));
1353 						switch (j)
1354 						{
1355 						case TM_DAYS+0:
1356 							tm->tm_mday--;
1357 							set |= DAY;
1358 							goto clear_hour;
1359 						case TM_DAYS+1:
1360 							set |= DAY;
1361 							goto clear_hour;
1362 						case TM_DAYS+2:
1363 							tm->tm_mday++;
1364 							set |= DAY;
1365 							goto clear_hour;
1366 						case TM_PARTS+0:
1367 							set |= SECOND;
1368 							if ((m < 0 ? -m : m) > (365L*24L*60L*60L))
1369 							{
1370 								now = tmxtime(tm, zone) + tmxsns(m, 0);
1371 								goto reset;
1372 							}
1373 							tm->tm_sec += m;
1374 							goto clear_nsec;
1375 						case TM_PARTS+1:
1376 							tm->tm_min += m;
1377 							set |= MINUTE;
1378 							goto clear_sec;
1379 						case TM_PARTS+2:
1380 							tm->tm_hour += m;
1381 							set |= MINUTE;
1382 							goto clear_min;
1383 						case TM_PARTS+3:
1384 							message((-1, "AHA#%d DAY m=%d n=%d%s", __LINE__, m, n, (state & LAST) ? " LAST" : ""));
1385 							if ((state & (LAST|NEXT|THIS)) == LAST)
1386 								tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1387 							else if (state & ORDINAL)
1388 								tm->tm_mday = m + 1;
1389 							else
1390 								tm->tm_mday += m;
1391 							if (!(set & (FINAL|WORK)))
1392 								set |= HOUR;
1393 							goto clear_hour;
1394 						case TM_PARTS+4:
1395 							tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1396 							tm->tm_mday += 7 * m - tm->tm_wday + 1;
1397 							set |= DAY;
1398 							goto clear_hour;
1399 						case TM_PARTS+5:
1400 							tm->tm_mon += m;
1401 							set |= MONTH;
1402 							goto clear_mday;
1403 						case TM_PARTS+6:
1404 							tm->tm_year += m;
1405 							goto clear_mon;
1406 						case TM_HOURS+0:
1407 							tm->tm_mday += m;
1408 							set |= DAY;
1409 							goto clear_hour;
1410 						case TM_HOURS+1:
1411 							tm->tm_mday += m;
1412 							tm->tm_hour = 6;
1413 							set |= HOUR;
1414 							goto clear_min;
1415 						case TM_HOURS+2:
1416 							tm->tm_mday += m;
1417 							tm->tm_hour = 12;
1418 							set |= HOUR;
1419 							goto clear_min;
1420 						case TM_HOURS+3:
1421 							tm->tm_mday += m;
1422 							tm->tm_hour = 18;
1423 							set |= HOUR;
1424 							goto clear_min;
1425 						}
1426 						if (m >= 0 && (state & ORDINAL))
1427 							tm->tm_mday = 1;
1428 						tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1429 						day = j -= TM_DAY;
1430 						if (!dir)
1431 							dir = m;
1432 						message((-1, "AHA#%d j=%d m=%d", __LINE__, j, m));
1433 						j -= tm->tm_wday;
1434 						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));
1435 						if (state & (LAST|NEXT|THIS))
1436 						{
1437 							if (state & ORDINAL)
1438 							{
1439 								while (isspace(*s))
1440 									s++;
1441 								if (isdigit(*s) || tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0) >= 0)
1442 								{
1443 									state &= ~(LAST|NEXT|THIS);
1444 									goto clear_hour;
1445 								}
1446 							}
1447 							if (j < 0)
1448 								j += 7;
1449 						}
1450 						else if (j > 0)
1451 							j -= 7;
1452 						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)));
1453 						set |= DAY;
1454 						if (set & (FINAL|WORK))
1455 							goto clear_hour;
1456 						else if (state & (LAST|NEXT|THIS))
1457 						{
1458 							if (f >= 0)
1459 								day = -1;
1460 							else if (m > 0 && (state & (NEXT|YEAR|MONTH)) == NEXT && j >= 0)
1461 								m--;
1462 							tm->tm_mday += j + m * 7;
1463 							set &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1464 							state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1465 							if (!(state & EXACT))
1466 								goto clear_hour;
1467 						}
1468 						continue;
1469 					case TM_MONTH_ABBREV:
1470 						j += TM_MONTH - TM_MONTH_ABBREV;
1471 						/*FALLTHROUGH*/
1472 					case TM_MONTH:
1473 						if (state & MONTH)
1474 							goto done;
1475 						state |= MONTH;
1476 						i = tm->tm_mon;
1477 						tm->tm_mon = j - TM_MONTH;
1478 						if (n < 0)
1479 						{
1480 							while (skip[*s])
1481 								s++;
1482 							if (isdigit(*s))
1483 							{
1484 								n = strtol(s, &t, 10);
1485 								if (n <= 31 && *t != ':')
1486 									s = t;
1487 								else
1488 									n = -1;
1489 							}
1490 						}
1491 						if (n >= 0)
1492 						{
1493 							if (n > 31)
1494 								goto done;
1495 							state |= DAY|MDAY;
1496 							tm->tm_mday = n;
1497 							if (f > 0)
1498 								tm->tm_year += f;
1499 						}
1500 						if (state & (LAST|NEXT|THIS))
1501 						{
1502 							n = i;
1503 							goto rel_month;
1504 						}
1505 						continue;
1506 					case TM_UT:
1507 						if (state & ZONE)
1508 							goto done;
1509 						state |= ZONE;
1510 						zone = tmgoff(s, &t, 0);
1511 						s = t;
1512 						continue;
1513 					case TM_DT:
1514 						if (!dst)
1515 							goto done;
1516 						if (!(state & ZONE))
1517 						{
1518 							dst = tm->tm_zone->dst;
1519 							zone = tm->tm_zone->west;
1520 						}
1521 						zone += tmgoff(s, &t, dst);
1522 						s = t;
1523 						dst = 0;
1524 						state |= ZONE;
1525 						continue;
1526 					case TM_NOISE:
1527 						continue;
1528 					}
1529 				}
1530 				if (!(state & ZONE) && (zp = tmzone(s, &t, type, &dst)))
1531 				{
1532 					s = t;
1533 					zone = zp->west + dst;
1534 					tm_info.date = zp;
1535 					state |= ZONE;
1536 					if (n < 0)
1537 						continue;
1538 				}
1539 				else if (!type && (zp = tmtype(s, &t)))
1540 				{
1541 					s = t;
1542 					type = zp->type;
1543 					if (n < 0)
1544 						continue;
1545 				}
1546 				state |= BREAK;
1547 			}
1548 		}
1549 		else if (*s == '/')
1550 		{
1551 			if (!(state & (YEAR|MONTH)) && n >= 1969 && n < 3000 && (i = strtol(s + 1, &t, 10)) > 0 && i <= 12)
1552 			{
1553 				state |= YEAR;
1554 				tm->tm_year = n - 1900;
1555 				s = t;
1556 				i--;
1557 			}
1558 			else
1559 			{
1560 				if ((state & MONTH) || n <= 0 || n > 31)
1561 					break;
1562 				if (isalpha(*++s))
1563 				{
1564 					if ((i = tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0)) < 0)
1565 						break;
1566 					if (i >= TM_MONTH)
1567 						i -= TM_MONTH;
1568 					s = t;
1569 				}
1570 				else
1571 				{
1572 					i = n - 1;
1573 					n = strtol(s, &t, 10);
1574 					s = t;
1575 					if (n <= 0 || n > 31)
1576 						break;
1577 					if (*s == '/' && !isdigit(*(s + 1)))
1578 						break;
1579 				}
1580 				state |= DAY;
1581 				tm->tm_mday = n;
1582 			}
1583 			state |= MONTH;
1584 			n = tm->tm_mon;
1585 			tm->tm_mon = i;
1586 			if (*s == '/')
1587 			{
1588 				n = strtol(++s, &t, 10);
1589 				w = t - s;
1590 				s = t;
1591 				if (*s == '/' || *s == ':' || *s == '-' || *s == '_')
1592 					s++;
1593 			}
1594 			else
1595 			{
1596 				if (state & (LAST|NEXT|THIS))
1597 				{
1598 				rel_month:
1599 					if (state & LAST)
1600 						tm->tm_year -= (tm->tm_mon < n) ? 0 : 1;
1601 					else
1602 						tm->tm_year += ((state & NEXT) ? 1 : 0) + ((tm->tm_mon < n) ? 1 : 0);
1603 					if (state & MDAY)
1604 						goto clear_hour;
1605 					set &= ~(LAST|NEXT|THIS); /*AHA*/
1606 					state &= ~(LAST|NEXT|THIS); /*AHA*/
1607 					goto clear_mday;
1608 				}
1609 				continue;
1610 			}
1611 		}
1612 		if (n < 0 || w > 4)
1613 			break;
1614 		if (w == 4)
1615 		{
1616 			if ((state & YEAR) || n < 1969 || n >= 3000)
1617 				break;
1618 			state |= YEAR;
1619 			tm->tm_year = n - 1900;
1620 		}
1621 		else if (w == 3)
1622 		{
1623 			if (state & (MONTH|MDAY|WDAY))
1624 				break;
1625 			state |= MONTH|DAY|MDAY;
1626 			tm->tm_mon = 0;
1627 			tm->tm_mday = n;
1628 		}
1629 		else if (w == 2 && !(state & YEAR))
1630 		{
1631 			state |= YEAR;
1632 			if (n < TM_WINDOW)
1633 				n += 100;
1634 			tm->tm_year = n;
1635 		}
1636 		else if (!(state & MONTH) && n >= 1 && n <= 12)
1637 		{
1638 			state |= MONTH;
1639 			tm->tm_mon = n - 1;
1640 		}
1641 		else if (!(state & (MDAY|WDAY)) && n >= 1 && n <= 31)
1642 		{
1643 			state |= DAY|MDAY|WDAY;
1644 			tm->tm_mday = n;
1645 		}
1646 		else
1647 			break;
1648 		if (state & BREAK)
1649 		{
1650 			last = t;
1651 			break;
1652 		}
1653 		continue;
1654 	clear_mon:
1655 		if ((set|state) & (EXACT|MONTH))
1656 			continue;
1657 		tm->tm_mon = 0;
1658 	clear_mday:
1659 		set |= MONTH;
1660 		if ((set|state) & (EXACT|DAY|HOUR))
1661 			continue;
1662 		tm->tm_mday = 1;
1663 	clear_hour:
1664 		message((-1, "AHA#%d DAY", __LINE__));
1665 		set |= DAY;
1666 		if ((set|state) & (EXACT|HOUR))
1667 			continue;
1668 		tm->tm_hour = 0;
1669 	clear_min:
1670 		set |= HOUR;
1671 		if ((set|state) & (EXACT|MINUTE))
1672 			continue;
1673 		tm->tm_min = 0;
1674 	clear_sec:
1675 		set |= MINUTE;
1676 		if ((set|state) & (EXACT|SECOND))
1677 			continue;
1678 		tm->tm_sec = 0;
1679 	clear_nsec:
1680 		set |= SECOND;
1681 		if ((set|state) & (EXACT|NSEC))
1682 			continue;
1683 		tm->tm_nsec = 0;
1684 	}
1685  done:
1686 	if (day >= 0 && !(state & (MDAY|WDAY)))
1687 	{
1688 		message((-1, "AHA#%d day=%d dir=%d state=" FFMT, __LINE__, day, dir, FLAGS(state)));
1689 		tmfix(tm);
1690 		m = dir;
1691 		if (state & MONTH)
1692 			tm->tm_mday = 1;
1693 		else if (m < 0)
1694 			m++;
1695 		tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1696 		j = day - tm->tm_wday;
1697 		if (j < 0)
1698 			j += 7;
1699 		tm->tm_mday += j + m * 7;
1700 		if (state & FINAL)
1701 			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);
1702 	}
1703 	else if (day < 0 && (state & FINAL) && (set & DAY))
1704 	{
1705 		tmfix(tm);
1706 		tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1707 	}
1708 	if (state & WORK)
1709 	{
1710 		tm->tm_mday = (set & FINAL) ? (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))) : 1;
1711 		tmfix(tm);
1712 		message((-1, "AHA#%d WORK mday=%d wday=%d", __LINE__, tm->tm_mday, tm->tm_wday));
1713 		if (tm->tm_wday == 0 && (j = 1) || tm->tm_wday == 6 && (j = 2))
1714 		{
1715 			if ((tm->tm_mday + j) > (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))))
1716 				j -= 3;
1717 			tm->tm_mday += j;
1718 		}
1719 	}
1720 	now = tmxtime(tm, zone);
1721 	if (tm->tm_year <= 70 && tmxsec(now) > 31536000)
1722 	{
1723 		now = 0;
1724 		last = (char*)o;
1725 	}
1726 	if (e)
1727 		*e = last;
1728 	return now;
1729 }
1730