2 * PARTIME parse date/time string into a TM structure
5 * #include "time.h" -- expanded tm structure
6 * char *str; struct tm *tp;
10 * else time values in specified TM structure (unspecified values
13 * This code is quasi-public; it may be used freely in like software.
14 * It is not to be sold, nor used in licensed software without
15 * permission of the author.
16 * For everyone's benefit, please report bugs and improvements!
17 * Copyright 1980 by Ken Harrenstien, SRI International.
18 * (ARPANET: KLH @ SRI)
22 * If parsing changed so that no backup needed, could perhaps modify
23 * to use a FILE input stream. Need terminator, though.
24 * Perhaps should return 0 on success, else a non-zero error val?
25 * Flush AMPM from TM structure and handle locally within PARTIME,
31 "$Header: /pub/NetBSD/misc/repositories/cvsroot/src/usr.bin/rcs/src/Attic/partime.c,v 1.1 1993/03/21 09:58:06 cgd Exp $";
34 /* $Log: partime.c,v $
35 * Revision 1.4 89/05/01 14:48:46 narten
36 * fixed #ifdef DEBUG construct
38 * Revision 1.3 88/11/08 12:02:15 narten
39 * changes from eggert@sm.unisys.com (Paul Eggert)
41 * Revision 1.3 88/08/28 14:53:40 eggert
42 * Remove unportable "#endif XXX"s.
44 * Revision 1.2 87/03/27 14:21:53 jenkins
47 * Revision 1.1 84/01/23 14:50:07 kcs
50 * Revision 1.1 82/05/06 11:38:26 wft
60 static char timeid
[] = TIMEID
;
65 long wval
; /* must be big enough to hold pointer or integer */
70 #define TWSPEC 01 /* Word wants special processing */
71 #define TWTIME 02 /* Word is a time value (absence implies date) */
72 #define TWDST 04 /* Word is a DST-type timezone */
73 #define TW1200 010 /* Word is NOON or MIDNIGHT (sigh) */
77 struct tmwent tmwords
[] = {
78 {"january", 0, 0, TM_MON
},
79 {"february", 1, 0, TM_MON
},
80 {"march", 2, 0, TM_MON
},
81 {"april", 3, 0, TM_MON
},
82 {"may", 4, 0, TM_MON
},
83 {"june", 5, 0, TM_MON
},
84 {"july", 6, 0, TM_MON
},
85 {"august", 7, 0, TM_MON
},
86 {"september", 8, 0, TM_MON
},
87 {"october", 9, 0, TM_MON
},
88 {"november", 10, 0, TM_MON
},
89 {"december", 11, 0, TM_MON
},
91 {"sunday", 0, 0, TM_WDAY
},
92 {"monday", 1, 0, TM_WDAY
},
93 {"tuesday", 2, 0, TM_WDAY
},
94 {"wednesday", 3, 0, TM_WDAY
},
95 {"thursday", 4, 0, TM_WDAY
},
96 {"friday", 5, 0, TM_WDAY
},
97 {"saturday", 6, 0, TM_WDAY
},
99 {"gmt", 0*60, TWTIME
, TM_ZON
}, /* Greenwich */
100 {"gst", 0*60, TWTIME
, TM_ZON
},
101 {"gdt", 0*60, TWTIME
+TWDST
, TM_ZON
}, /* ?? */
103 {"ast", 4*60, TWTIME
, TM_ZON
}, /* Atlantic */
104 {"est", 5*60, TWTIME
, TM_ZON
}, /* Eastern */
105 {"cst", 6*60, TWTIME
, TM_ZON
}, /* Central */
106 {"mst", 7*60, TWTIME
, TM_ZON
}, /* Mountain */
107 {"pst", 8*60, TWTIME
, TM_ZON
}, /* Pacific */
108 {"yst", 9*60, TWTIME
, TM_ZON
}, /* Yukon */
109 {"hst", 10*60, TWTIME
, TM_ZON
}, /* Hawaii */
110 {"bst", 11*60, TWTIME
, TM_ZON
}, /* Bering */
112 {"adt", 4*60, TWTIME
+TWDST
, TM_ZON
}, /* Atlantic */
113 {"edt", 5*60, TWTIME
+TWDST
, TM_ZON
}, /* Eastern */
114 {"cdt", 6*60, TWTIME
+TWDST
, TM_ZON
}, /* Central */
115 {"mdt", 7*60, TWTIME
+TWDST
, TM_ZON
}, /* Mountain */
116 {"pdt", 8*60, TWTIME
+TWDST
, TM_ZON
}, /* Pacific */
117 {"ydt", 9*60, TWTIME
+TWDST
, TM_ZON
}, /* Yukon */
118 {"hdt", 10*60, TWTIME
+TWDST
, TM_ZON
}, /* Hawaii */
119 {"bdt", 11*60, TWTIME
+TWDST
, TM_ZON
}, /* Bering */
121 {"daylight", 1, TWTIME
+TWDST
, TM_ZON
}, /* Local Daylight */
122 {"standard", 1, TWTIME
, TM_ZON
}, /* Local Standard */
123 {"std", 1, TWTIME
, TM_ZON
}, /* " " */
125 {"am", 1, TWTIME
, TM_AMPM
},
126 {"pm", 2, TWTIME
, TM_AMPM
},
127 {"noon", 12,TWTIME
+TW1200
, 0}, /* Special frobs */
128 {"midnight", 0, TWTIME
+TW1200
, 0},
129 {"at", (long)ptnoise
, TWSPEC
, 0}, /* Noise word */
131 {0, 0, 0, 0}, /* Zero entry to terminate searches */
134 #define TMWILD (-2) /* Value meaning item specified as wild-card */
135 /* (May use someday...) */
138 char *tcp
; /* pointer to string */
139 int tcnt
; /* # chars */
140 char tbrk
; /* "break" char */
141 char tbrkl
; /* last break char */
142 char tflg
; /* 0 = alpha, 1 = numeric */
143 union { /* Resulting value; */
144 int tnum
;/* either a #, or */
145 struct tmwent
*ttmw
;/* ptr to a tmwent. */
153 register struct tmwent
*twp
;
155 struct token btoken
, atoken
;
161 zaptime(tp
); /* Initialize the TM structure */
162 midnoon
= TMNULL
; /* and our own temp stuff */
163 btoken
.tcnt
= btoken
.tbrkl
= 0;
167 if(!ptitoken(btoken
.tcp
+btoken
.tcnt
,&btoken
)) /* Get a token */
168 { if(btoken
.tval
.tnum
) return(0); /* Read error? */
169 if(midnoon
!= TMNULL
) /* EOF, wrap up */
170 return(pt12hack(tp
, midnoon
));
171 return(1); /* Win return! */
173 if(btoken
.tflg
== 0) /* Alpha? */
174 { twp
= btoken
.tval
.ttmw
; /* Yes, get ptr to entry */
175 if(twp
->wflgs
&TWSPEC
) /* Special alpha crock */
176 { aproc
= (int (*) ()) (twp
->wval
);
177 if(!(*aproc
)(tp
, twp
, &btoken
))
178 return(0); /* ERR: special word err */
181 if(twp
->wflgs
&TW1200
)
182 if(ptstash(&midnoon
,(int)twp
->wval
))
183 return(0); /* ERR: noon/midnite clash */
185 if(ptstash(&tp
[twp
->wtype
],(int)twp
->wval
))
186 return(0); /* ERR: val already set */
187 if(twp
->wtype
== TM_ZON
) /* If was zone, hack DST */
188 if(ptstash(&tp
[TM_ISDST
],(twp
->wflgs
&TWDST
)))
189 return(0); /* ERR: DST conflict */
193 /* Token is number. Lots of hairy heuristics. */
194 if(btoken
.tcnt
>= 7) /* More than 6 digits in string? */
195 return(0); /* ERR: number too big */
196 if(btoken
.tcnt
== 6) /* 6 digits = HHMMSS. Needs special crock */
197 { /* since 6 digits are too big for integer! */
198 i
= (btoken
.tcp
[0]-'0')*10 /* Gobble 1st 2 digits */
200 btoken
.tcnt
= 2; /* re-read last 4 chars */
204 i
= btoken
.tval
.tnum
; /* Value now known to be valid; get it. */
205 if( btoken
.tcnt
== 5 /* 5 digits = HMMSS */
206 || btoken
.tcnt
== 3) /* 3 digits = HMM */
207 { if(btoken
.tcnt
!= 3)
208 if(ptstash(&tp
[TM_SEC
], i
%100))
209 return(0); /* ERR: sec conflict */
211 hhmm4
: if(ptstash(&tp
[TM_MIN
], i
%100))
212 return(0); /* ERR: min conflict */
214 hh2
: if(ptstash(&tp
[TM_HOUR
], i
))
215 return(0); /* ERR: hour conflict */
219 if(btoken
.tcnt
== 4) /* 4 digits = YEAR or HHMM */
220 { if(tp
[TM_YEAR
] != TMNULL
) goto hhmm4
; /* Already got yr? */
221 if(tp
[TM_HOUR
] != TMNULL
) goto year4
; /* Already got hr? */
222 if((i
%100) > 59) goto year4
; /* MM >= 60? */
223 if(btoken
.tbrk
== ':') /* HHMM:SS ? */
224 if( ptstash(&tp
[TM_HOUR
],i
/100)
225 || ptstash(&tp
[TM_MIN
], i
%100))
226 return(0); /* ERR: hr/min clash */
227 else goto coltm2
; /* Go handle SS */
228 if(btoken
.tbrk
!= ',' && btoken
.tbrk
!= '/'
229 && ptitoken(btoken
.tcp
+btoken
.tcnt
,&atoken
) /* Peek */
230 && atoken
.tflg
== 0 /* alpha */
231 && (atoken
.tval
.ttmw
->wflgs
&TWTIME
)) /* HHMM-ZON */
233 if(btoken
.tbrkl
== '-' /* DD-Mon-YYYY */
234 || btoken
.tbrkl
== ',' /* Mon DD, YYYY */
235 || btoken
.tbrkl
== '/' /* MM/DD/YYYY */
236 || btoken
.tbrkl
== '.' /* DD.MM.YYYY */
237 || btoken
.tbrk
== '-' /* YYYY-MM-DD */
239 goto hhmm4
; /* Give up, assume HHMM. */
242 /* From this point on, assume tcnt == 1 or 2 */
243 /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
244 if(btoken
.tbrk
== ':') /* HH:MM[:SS] */
245 goto coltime
; /* must be part of time. */
246 if(i
> 31) goto yy2
; /* If >= 32, only YY poss. */
248 /* Check for numerical-format date */
249 for (cp
= "/-."; ch
= *cp
++;)
250 { ord
= (ch
== '.' ? 0 : 1); /* n/m = D/M or M/D */
251 if(btoken
.tbrk
== ch
) /* "NN-" */
252 { if(btoken
.tbrkl
!= ch
)
253 { if(ptitoken(btoken
.tcp
+btoken
.tcnt
,&atoken
)
255 && atoken
.tval
.ttmw
->wtype
== TM_MON
)
257 if(ord
)goto mm2
; else goto dd2
; /* "NN-" */
259 if(tp
[TM_DAY
] == TMNULL
260 && tp
[TM_YEAR
] != TMNULL
) /* If "YY-NN-" */
261 goto mm2
; /* then always MM */
262 if(ord
)goto dd2
; else goto mm2
;
264 if(btoken
.tbrkl
== ch
/* "-NN" */
265 && tp
[ord
? TM_MON
: TM_DAY
] != TMNULL
)
266 if(tp
[ord
? TM_DAY
: TM_MON
] == TMNULL
) /* MM/DD */
267 if(ord
)goto dd2
; else goto mm2
;
268 else goto yy2
; /* "-YY" */
271 /* At this point only YY, DD, and HH are left.
272 * YY is very unlikely since value is <= 32 and there was
273 * no numerical format date. Make one last try at YY
274 * before dropping through to DD vs HH code.
276 if(btoken
.tcnt
== 2 /* If 2 digits */
277 && tp
[TM_HOUR
] != TMNULL
/* and already have hour */
278 && tp
[TM_DAY
] != TMNULL
/* and day, but */
279 && tp
[TM_YEAR
] == TMNULL
) /* no year, then assume */
280 goto yy2
; /* that's what we have. */
282 /* Now reduced to choice between HH and DD */
283 if(tp
[TM_HOUR
] != TMNULL
) goto dd2
; /* Have hour? Assume day. */
284 if(tp
[TM_DAY
] != TMNULL
) goto hh2
; /* Have day? Assume hour. */
285 if(i
> 24) goto dd2
; /* Impossible HH means DD */
286 if(!ptitoken(btoken
.tcp
+btoken
.tcnt
, &atoken
)) /* Read ahead! */
287 if(atoken
.tval
.tnum
) return(0); /* ERR: bad token */
288 else goto dd2
; /* EOF, assume day. */
289 if( atoken
.tflg
== 0 /* If next token is an alpha */
290 && atoken
.tval
.ttmw
->wflgs
&TWTIME
) /* time-spec, assume hour */
291 goto hh2
; /* e.g. "3 PM", "11-EDT" */
293 dd2
: if(ptstash(&tp
[TM_DAY
],i
)) /* Store day (1 based) */
297 mm2
: if(ptstash(&tp
[TM_MON
], i
-1)) /* Store month (make zero based) */
302 year4
: if(ptstash(&tp
[TM_YEAR
],i
)) /* Store year (full number) */
303 return(0); /* ERR: year conflict */
306 /* Hack HH:MM[[:]SS] */
308 if(ptstash(&tp
[TM_HOUR
],i
)) return(0);
309 if(!ptitoken(btoken
.tcp
+btoken
.tcnt
,&btoken
))
310 return(!btoken
.tval
.tnum
);
311 if(!btoken
.tflg
) return(0); /* ERR: HH:<alpha> */
312 if(btoken
.tcnt
== 4) /* MMSS */
313 if(ptstash(&tp
[TM_MIN
],btoken
.tval
.tnum
/100)
314 || ptstash(&tp
[TM_SEC
],btoken
.tval
.tnum
%100))
318 || ptstash(&tp
[TM_MIN
],btoken
.tval
.tnum
))
319 return(0); /* ERR: MM bad */
320 if(btoken
.tbrk
!= ':') goto domore
; /* Seconds follow? */
321 coltm2
: if(!ptitoken(btoken
.tcp
+btoken
.tcnt
,&btoken
))
322 return(!btoken
.tval
.tnum
);
323 if(!btoken
.tflg
|| btoken
.tcnt
!= 2 /* Verify SS */
324 || ptstash(&tp
[TM_SEC
], btoken
.tval
.tnum
))
325 return(0); /* ERR: SS bad */
329 /* Store date/time value, return 0 if successful.
330 * Fails if entry already set to a different value.
335 if( *(a
=adr
) != TMNULL
)
341 /* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
342 * just prior to returning from partime.
346 { register int *tp
, i
, h
;
348 if (((i
=tp
[TM_MIN
]) && i
!= TMNULL
) /* Ensure mins, secs */
349 || ((i
=tp
[TM_SEC
]) && i
!= TMNULL
)) /* are 0 or unspec'd */
350 return(0); /* ERR: MM:SS not 00:00 */
351 i
= aval
; /* Get 0 or 12 (midnite or noon) */
352 if ((h
= tp
[TM_HOUR
]) == TMNULL
/* If hour unspec'd, win */
353 || h
== 12) /* or if 12:00 (matches either) */
354 tp
[TM_HOUR
] = i
; /* Then set time */
355 else if(!(i
== 0 /* Nope, but if midnight and */
356 &&(h
== 0 || h
== 24))) /* time matches, can pass. */
357 return(0); /* ERR: HH conflicts */
358 tp
[TM_AMPM
] = TMNULL
; /* Always reset this value if won */
362 /* Null routine for no-op tokens */
364 ptnoise() { return(1); }
366 /* Get a token and identify it to some degree.
367 * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
368 * hit error of some sort
372 register struct token
*tkp
;
379 if(pttoken(astr
,tkp
) == 0)
382 VOID
printf("EOF\n");
393 VOID
printf("Token: \"%s\" ",cp
);
398 for(i
= tkp
->tcnt
; i
> 0; i
--)
399 tkp
->tval
.tnum
= (int)tkp
->tval
.tnum
*10 + ((*cp
++)-'0');
401 { i
= ptmatchstr(cp
, tkp
->tcnt
, tmwords
);
402 tkp
->tval
.tnum
= i
? i
: -1; /* Set -1 for error */
405 if(!i
) VOID
printf("Not found!\n");
413 VOID
printf("Val: %d.\n",tkp
->tval
.tnum
);
414 else VOID
printf("Found: \"%s\", val: %d., type %d\n",
415 tkp
->tval
.ttmw
->went
,tkp
->tval
.ttmw
->wval
,tkp
->tval
.ttmw
->wtype
);
421 /* Read token from input string into token structure */
423 register struct token
*tkp
;
429 tkp
->tcp
= cp
= astr
;
430 tkp
->tbrkl
= tkp
->tbrk
; /* Set "last break" */
431 tkp
->tcnt
= tkp
->tbrk
= tkp
->tflg
= 0;
435 { case ' ': case '\t': /* Flush all whitespace */
436 while((c
= *cp
++) && isspace(c
));
437 cp
--; /* Drop thru to handle brk */
438 case '(': case ')': /* Perhaps any non-alphanum */
439 case '-': case ',': /* shd qualify as break? */
440 case '/': case ':': case '.': /* Break chars */
441 if(tkp
->tcnt
== 0) /* If no token yet */
442 { tkp
->tcp
= cp
; /* ignore the brk */
444 continue; /* and go on. */
449 if(tkp
->tcnt
== 0) /* If first char of token, */
450 tkp
->tflg
= isdigit(c
); /* determine type */
451 if(( isdigit(c
) && tkp
->tflg
) /* If not first, make sure */
452 ||(!isdigit(c
) && !tkp
->tflg
)) /* char matches type */
453 tkp
->tcnt
++; /* Win, add to token. */
455 cp
--; /* Wrong type, back up */
460 return(tkp
->tcnt
); /* When hit EOF */
464 ptmatchstr(astr
,cnt
,astruc
)
467 struct tmwent
*astruc
;
468 { register char *cp
, *mp
;
470 struct tmwent
*lastptr
;
471 struct integ
{ int word
; }; /* For getting at array ptr */
475 for(;mp
= (char *)((struct integ
*)astruc
)->word
; astruc
+= 1)
477 for(i
= cnt
; i
> 0; i
--)
478 { switch((c
= *cp
++) ^ *mp
++) /* XOR the chars */
479 { case 0: continue; /* Exact match */
480 case 040: if(isalpha(c
))
486 if(*mp
== 0) return((unsigned int)astruc
); /* Exact match */
487 else if(lastptr
) return(0); /* Ambiguous */
488 else lastptr
= astruc
; /* 1st ambig */
490 return((unsigned int)lastptr
);
497 /* clears tm structure pointed to by tp */
499 i
= (sizeof (struct tm
))/(sizeof (int));
500 do *tp
++ = TMNULL
; /* Set entry to "unspecified" */
501 while(--i
); /* Faster than FOR */