1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2011 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
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			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