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
72static int
73range(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
123static void
124powerize(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
156Time_t
157tmxdate(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