8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / lib / libast / common / tm / tmxdate.c
blob3be483572f00c867409745a88d19fd6f4d24f6a5
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
24 * Glenn Fowler
25 * AT&T Research
27 * Time_t conversion support
29 * relative times inspired by Steve Bellovin's netnews getdate(3)
32 #include <tmx.h>
33 #include <ctype.h>
34 #include <debug.h>
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')
41 #undef BREAK
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)
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":""
68 * parse cron range into set
69 * return: -1:error 0:* 1:some
72 static int
73 range(register char* s, char** e, char* set, int lo, int hi)
75 int n;
76 int m;
77 int i;
78 char* t;
80 while (isspace(*s) || *s == '_')
81 s++;
82 if (*s == '*')
84 *e = s + 1;
85 return 0;
87 memset(set, 0, hi + 1);
88 for (;;)
90 n = strtol(s, &t, 10);
91 if (s == t || n < lo || n > hi)
92 return -1;
93 i = 1;
94 if (*(s = t) == '-')
96 m = strtol(++s, &t, 10);
97 if (s == t || m < n || m > hi)
98 return -1;
99 if (*(s = t) == '/')
101 i = strtol(++s, &t, 10);
102 if (s == t || i < 1)
103 return -1;
104 s = t;
107 else
108 m = n;
109 for (; n <= m; n += i)
110 set[n] = 1;
111 if (*s != ',')
112 break;
113 s++;
115 *e = s;
116 return 1;
120 * normalize <p,q> to power of 10 u in tm
123 static void
124 powerize(Tm_t* tm, unsigned long p, unsigned long q, unsigned long u)
126 Time_t t = p;
128 while (q > u)
130 q /= 10;
131 t /= 10;
133 while (q < u)
135 q *= 10;
136 t *= 10;
138 tm->tm_nsec += (int)(t % TMX_RESOLUTION);
139 tm->tm_sec += (int)(t / TMX_RESOLUTION);
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))
147 #define P_INIT(n) w = n; p = q = 0; u = (char*)s + 1
150 * parse date expression in s and return Time_t value
152 * if non-null, e points to the first invalid sequence in s
153 * now provides default values
156 Time_t
157 tmxdate(register const char* s, char** e, Time_t now)
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];
190 * check DATEMSK first
193 debug((error(-1, "AHA tmxdate 2009-03-06")));
194 fix = tmxscan(s, &last, NiL, &t, now, 0);
195 if (t && !*last)
197 if (e)
198 *e = last;
199 return fix;
201 o = s;
203 reset:
206 * use now for defaults
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);
222 * get <weekday year month day hour minutes seconds ?[ds]t [ap]m>
225 again:
226 for (;;)
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 (;;)
234 if (*s == '.' || *s == '-' || *s == '+')
236 if (((set|state) & (YEAR|MONTH|HOUR|MINUTE|ZONE)) == (YEAR|MONTH|HOUR|MINUTE) && (i = tmgoff(s, &t, TM_LOCALZONE)) != TM_LOCALZONE)
238 zone = i;
239 state |= ZONE;
240 if (!*(s = t))
241 break;
243 else if (*s == '+')
244 break;
246 else if (!skip[*s])
247 break;
248 s++;
250 if (!*(last = (char*)s))
251 break;
252 if (*s == '#')
254 if (isdigit(*++s))
256 now = strtoull(s, &t, 0);
257 sns:
258 if (*(s = t) == '.')
260 fix = 0;
261 m = 1000000000;
262 while (isdigit(*++s))
263 fix += (*s - '0') * (m /= 10);
264 now = tmxsns(now, fix);
266 else if (now <= 0x7fffffff)
267 now = tmxsns(now, 0);
268 goto reset;
270 else if (*s++ == '#')
272 now = tmxtime(tm, zone);
273 goto reset;
275 break;
277 if ((*s == 'P' || *s == 'p') && (!isalpha(*(s + 1)) || (*(s + 1) == 'T' || *(s + 1) == 't') && !isalpha(*(s + 2))))
279 Tm_t otm;
282 * iso duration
285 otm = *tm;
286 t = (char*)s;
287 m = 0;
288 P_INIT('Y');
291 c = *++s;
292 duration_next:
293 switch (c)
295 case 0:
296 m++;
297 if ((char*)s > u)
299 s--;
300 c = '_';
301 goto duration_next;
303 break;
304 case 'T':
305 case 't':
306 m++;
307 if ((char*)s > u)
309 s++;
310 c = 'D';
311 goto duration_next;
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))
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;
349 switch (m)
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;
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))
421 case 'S':
422 case 's':
423 s++;
424 break;
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))
437 case 'S':
438 case 's':
439 s++;
440 break;
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 == '-')
460 c = 'Y';
461 break;
463 goto duration_next;
464 case ':':
465 c = 'm';
466 u = (char*)s++;
467 while (*++u)
468 if (*u == ':')
470 c = 'H';
471 break;
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')
493 state |= HOLD|EXACT;
494 set &= ~(EXACT|LAST|NEXT|THIS);
495 set |= state & (EXACT|LAST|NEXT|THIS);
497 goto again;
499 break;
500 } while (c);
501 continue;
503 f = -1;
504 if (*s == '+')
506 while (isspace(*++s) || *s == '_');
507 n = strtol(s, &t, 0);
508 if (w = t - s)
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);
515 else
516 s = last;
518 if (!(state & CRON))
521 * check for cron date
523 * min hour day-of-month month day-of-week
525 * if it's cron then determine the next time
526 * that satisfies the specification
528 * NOTE: the only spacing is ' '||'_'||';'
531 i = 0;
532 n = *(t = (char*)s);
533 for (;;)
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 != ';')
543 if (!n)
544 i++;
545 break;
547 i++;
548 while ((n = *++s) == ' ' || n == '_');
550 if (i == 5)
552 Time_t tt;
553 char hit[60];
554 char mon[13];
555 char day[7];
557 state |= CRON;
558 flags = 0;
559 tm->tm_sec = 0;
560 tm->tm_min++;
561 tmfix(tm);
564 * minute
567 if ((k = range(t, &t, hit, 0, 59)) < 0)
568 break;
569 if (k && !hit[i = tm->tm_min])
571 hit[i] = 1;
572 do if (++i > 59)
574 i = 0;
575 if (++tm->tm_hour > 59)
577 tm->tm_min = i;
578 tmfix(tm);
580 } while (!hit[i]);
581 tm->tm_min = i;
585 * hour
588 if ((k = range(t, &t, hit, 0, 23)) < 0)
589 break;
590 if (k && !hit[i = tm->tm_hour])
592 hit[i] = 1;
593 do if (++i > 23)
595 i = 0;
596 if (++tm->tm_mday > 28)
598 tm->tm_hour = i;
599 tmfix(tm);
601 } while (!hit[i]);
602 tm->tm_hour = i;
606 * day of month
609 if ((k = range(t, &t, hit, 1, 31)) < 0)
610 break;
611 if (k)
612 flags |= DAY|MDAY;
615 * month
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;
627 * day of week
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))
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 (;;)
644 if (!mon[i])
646 if (++i > 12)
648 i = 1;
649 tm->tm_year++;
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;
662 if (flags & (MDAY|WDAY))
664 if ((flags & (MDAY|WDAY)) == (MDAY|WDAY))
666 if (hit[j] && day[k])
667 break;
669 else if ((flags & MDAY) && hit[j])
670 break;
671 else if ((flags & WDAY) && day[k])
672 break;
673 if (++j > 28)
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;
682 else if ((flags & WDAY) && ++k > 6)
683 k = 0;
685 else if (flags & MONTH)
686 break;
688 tm->tm_mon = i - 1;
689 tm->tm_mday = j;
690 tm->tm_wday = k;
692 continue;
694 s = t;
696 n = -1;
697 if (isdigit(*s))
699 n = strtol(s, &t, 10);
700 if ((w = t - s) && *t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && isdigit(*(t + 3)))
702 now = n;
703 goto sns;
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)))
710 if (w == 4)
712 if ((n -= 1900) < TM_WINDOW)
713 break;
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)
723 k = n % 10;
724 n /= 10;
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;
740 else if (w == 6 || w == 8 && (n / 1000000) > 12)
742 t = (char*)s;
743 flags = 0;
744 if (w == 8 || w == 6 && *u != 'T' && *u != 't')
746 dig4(t, m);
747 if ((m -= 1900) < TM_WINDOW)
748 break;
750 else
752 dig2(t, m);
753 if (m < TM_WINDOW)
754 m += 100;
756 flags |= YEAR;
757 if (dig2(t, l) <= 0 || l > 12)
758 break;
759 flags |= MONTH;
760 if (*t != 'T' && *t != 't' || !isdigit(*++t))
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;
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)
776 if ((t - s) == 1 || *t++ != '-')
777 break;
778 n = strtol(s = t, &t, 0);
779 if ((t - s) < 2)
780 break;
782 if (dig2(s, i) > 59)
783 break;
784 flags |= HOUR|MINUTE;
785 if ((t - s) == 2)
787 if (dig2(s, n) > (59 + TM_MAXLEAP))
788 break;
789 flags |= SECOND;
791 else if (t - s)
792 break;
793 else
794 n = 0;
795 p = 0;
796 if (*t == '.')
798 q = 1000000000;
799 while (isdigit(*++t))
800 p += (*t - '0') * (q /= 10);
801 set |= NSEC;
803 if (n > (59 + TM_MAXLEAP))
804 break;
805 goto save;
807 else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0)
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))
820 if (n = strtol(s, &t, 10))
821 n--;
822 s = t;
823 if (*s == '_')
824 s++;
826 else
827 n = -1;
828 dir = f;
829 message((-1, "AHA#%d f=%d n=%d state=" FFMT, __LINE__, f, n, FLAGS(state)));
831 else
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))
836 * various { date(1) touch(1) } formats
838 * [[cc]yy[mm]]ddhhmm[.ss[.nn...]]
839 * [cc]yyjjj
840 * hhmm[.ss[.nn...]]
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'))
850 if (i == 7)
852 dig4(s, m);
853 if ((m -= 1900) < TM_WINDOW)
854 break;
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;
865 else if (i & 1)
866 break;
867 else
869 u = t;
870 if (i == 12)
872 x = s;
873 dig2(x, m);
874 if (m <= 12)
876 u -= 4;
877 i -= 4;
878 x = s + 8;
879 dig4(x, m);
881 else
882 dig4(s, m);
883 if (m < 1969 || m >= 3000)
884 break;
885 m -= 1900;
887 else if (i == 10)
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
894 u -= 2;
895 i -= 2;
896 x = s + 8;
897 dig2(x, m);
899 if (m < TM_WINDOW)
900 m += 100;
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)
925 dig2(s, n);
926 flags |= SECOND;
928 else if (u - s)
929 break;
930 else if (*t != '.')
931 n = 0;
932 else
934 n = strtol(t + 1, &t, 10);
935 flags |= SECOND;
936 if (*t == '.')
938 q = 1000000000;
939 while (isdigit(*++t))
940 p += (*t - '0') * (q /= 10);
941 set |= NSEC;
944 if (n > (59 + TM_MAXLEAP))
945 break;
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;
961 for (s = t; skip[*s]; s++);
962 if (*s == ':' || *s == '.' && ((set|state) & (YEAR|MONTH|DAY|HOUR)) == (YEAR|MONTH|DAY))
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)
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 == '.')
991 q = 1000000000;
992 while (isdigit(*++s))
993 m += (*s - '0') * (q /= 10);
994 set |= NSEC;
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))
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;
1021 if (f >= 0 || (state & (LAST|NEXT)))
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)
1027 if (state & LAST)
1028 f = -1;
1029 else if (state & NEXT)
1030 f = 1;
1031 else
1032 f = 0;
1034 if (f > 0)
1036 if (i > k || i == k && j > l)
1037 f--;
1039 else if (i < k || i == k && j < l)
1040 f++;
1041 if (f > 0)
1043 tm->tm_hour += f * 24;
1044 while (tm->tm_hour >= 24)
1046 tm->tm_hour -= 24;
1047 tm->tm_mday++;
1050 else if (f < 0)
1052 tm->tm_hour += f * 24;
1053 while (tm->tm_hour < 24)
1055 tm->tm_hour += 24;
1056 tm->tm_mday--;
1060 continue;
1064 for (;;)
1066 if (*s == '-' || *s == '+')
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++;
1072 else if (skip[*s])
1073 s++;
1074 else
1075 break;
1077 if (isalpha(*s))
1079 if (n > 0)
1081 x = s;
1082 q = *s++;
1083 if (isalpha(*s))
1085 q <<= 8;
1086 q |= *s++;
1087 if (isalpha(*s))
1089 if (tmlex(s, &t, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES, NiL, 0) >= 0)
1090 s = t;
1091 if (isalpha(*s))
1093 q <<= 8;
1094 q |= *s++;
1095 if (isalpha(*s))
1097 q <<= 8;
1098 q |= *s++;
1099 if (isalpha(*s))
1100 q = 0;
1105 switch (q)
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;
1183 s = x;
1185 if (n < 1000)
1187 if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0)
1189 s = t;
1190 switch (tm_data.lex[j])
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:
1210 * disambiguate english "last ... in"
1213 if (!((state|set) & LAST))
1215 state |= HOLD|NEXT;
1216 set &= ~(EXACT|LAST|NEXT|THIS);
1217 set |= state & (EXACT|LAST|NEXT|THIS);
1218 continue;
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)
1232 state &= ~LAST;
1233 set &= ~LAST;
1234 state |= FINAL;
1235 set |= FINAL;
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)
1259 if (n > 24)
1260 goto done;
1261 tm->tm_hour = n;
1263 for (k = tm->tm_hour; k < 0; k += 24);
1264 k %= 24;
1265 if (j == TM_MERIDIAN)
1267 if (k == 12)
1268 tm->tm_hour -= 12;
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 (;;)
1285 while (skip[*s])
1286 s++;
1287 if ((k = tmlex(s, &t, tm_info.format + TM_LAST, TM_NOISE - TM_LAST, NiL, 0)) >= 0)
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;
1299 else
1301 state |= (n > 0) ? NEXT : THIS;
1302 break;
1304 set &= ~(EXACT|LAST|NEXT|THIS);
1305 set |= state & (EXACT|LAST|NEXT|THIS);
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)
1313 * disambiguate english "second"
1316 if (j == TM_PARTS && f == -1)
1318 state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1319 n = 2;
1320 goto ordinal;
1322 n = 1;
1326 * disambiguate "last" vs. { "previous" "final" }
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)
1334 s = t;
1335 if (state & LAST)
1337 state &= ~LAST;
1338 set &= ~LAST;
1339 state |= FINAL;
1340 set |= FINAL;
1341 message((-1, "AHA#%d LAST => FINAL", __LINE__));
1343 else
1344 state &= ~(THIS|NEXT);
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)
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))
1370 now = tmxtime(tm, zone) + tmxsns(m, 0);
1371 goto reset;
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;
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))
1437 if (state & ORDINAL)
1439 while (isspace(*s))
1440 s++;
1441 if (isdigit(*s) || tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0) >= 0)
1443 state &= ~(LAST|NEXT|THIS);
1444 goto clear_hour;
1447 if (j < 0)
1448 j += 7;
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))
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;
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)
1480 while (skip[*s])
1481 s++;
1482 if (isdigit(*s))
1484 n = strtol(s, &t, 10);
1485 if (n <= 31 && *t != ':')
1486 s = t;
1487 else
1488 n = -1;
1491 if (n >= 0)
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;
1500 if (state & (LAST|NEXT|THIS))
1502 n = i;
1503 goto rel_month;
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))
1518 dst = tm->tm_zone->dst;
1519 zone = tm->tm_zone->west;
1521 zone += tmgoff(s, &t, dst);
1522 s = t;
1523 dst = 0;
1524 state |= ZONE;
1525 continue;
1526 case TM_NOISE:
1527 continue;
1530 if (!(state & ZONE) && (zp = tmzone(s, &t, type, &dst)))
1532 s = t;
1533 zone = zp->west + dst;
1534 tm_info.date = zp;
1535 state |= ZONE;
1536 if (n < 0)
1537 continue;
1539 else if (!type && (zp = tmtype(s, &t)))
1541 s = t;
1542 type = zp->type;
1543 if (n < 0)
1544 continue;
1546 state |= BREAK;
1549 else if (*s == '/')
1551 if (!(state & (YEAR|MONTH)) && n >= 1969 && n < 3000 && (i = strtol(s + 1, &t, 10)) > 0 && i <= 12)
1553 state |= YEAR;
1554 tm->tm_year = n - 1900;
1555 s = t;
1556 i--;
1558 else
1560 if ((state & MONTH) || n <= 0 || n > 31)
1561 break;
1562 if (isalpha(*++s))
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;
1570 else
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;
1580 state |= DAY;
1581 tm->tm_mday = n;
1583 state |= MONTH;
1584 n = tm->tm_mon;
1585 tm->tm_mon = i;
1586 if (*s == '/')
1588 n = strtol(++s, &t, 10);
1589 w = t - s;
1590 s = t;
1591 if (*s == '/' || *s == ':' || *s == '-' || *s == '_')
1592 s++;
1594 else
1596 if (state & (LAST|NEXT|THIS))
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;
1609 continue;
1612 if (n < 0 || w > 4)
1613 break;
1614 if (w == 4)
1616 if ((state & YEAR) || n < 1969 || n >= 3000)
1617 break;
1618 state |= YEAR;
1619 tm->tm_year = n - 1900;
1621 else if (w == 3)
1623 if (state & (MONTH|MDAY|WDAY))
1624 break;
1625 state |= MONTH|DAY|MDAY;
1626 tm->tm_mon = 0;
1627 tm->tm_mday = n;
1629 else if (w == 2 && !(state & YEAR))
1631 state |= YEAR;
1632 if (n < TM_WINDOW)
1633 n += 100;
1634 tm->tm_year = n;
1636 else if (!(state & MONTH) && n >= 1 && n <= 12)
1638 state |= MONTH;
1639 tm->tm_mon = n - 1;
1641 else if (!(state & (MDAY|WDAY)) && n >= 1 && n <= 31)
1643 state |= DAY|MDAY|WDAY;
1644 tm->tm_mday = n;
1646 else
1647 break;
1648 if (state & BREAK)
1650 last = t;
1651 break;
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;
1685 done:
1686 if (day >= 0 && !(state & (MDAY|WDAY)))
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);
1703 else if (day < 0 && (state & FINAL) && (set & DAY))
1705 tmfix(tm);
1706 tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1708 if (state & WORK)
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))
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;
1720 now = tmxtime(tm, zone);
1721 if (tm->tm_year <= 70 && tmxsec(now) > 31536000)
1723 now = 0;
1724 last = (char*)o;
1726 if (e)
1727 *e = last;
1728 return now;