1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	Calculate an approximate "time_stamp" value for a date
6  *	string.  The actual value is not at all critical,
7  *	as long as the "ordering" is ok.
8  *
9  *	The result is NOT a time_t value, i.e. ctime() will
10  *	not produce the original Date string.
11  *
12  *	The date must have format:  [...,] [D]D Mmm YY hh:mm:ss TZONE
13  *
14  *	Thanks to Wayne Davison for the timezone decoding code.
15  */
16 
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include "config.h"
21 #include "global.h"
22 
23 /* pack_date.c */
24 
25 #undef W
26 #undef E
27 #undef DST
28 #undef UTC
29 #define W	* (-60) -
30 #define E	* 60 +
31 #define DST	+ 60
32 #define UTC	60 *
33 
34 static struct zonetab {
35     char           *tz_name;
36     int             tz_offset;
37 }               ztab[] = {
38 
39     {
40 	"GMT", 0
41     },				/* Greenwich Mean */
42     {
43 	"UT", 0
44     },				/* Universal */
45     {
46 	"UTC", 0
47     },				/* Universal Coordinated */
48     {
49 	"CUT", 0
50     },				/* Coordinated Universal */
51     {
52 	"WET", 0
53     },				/* Western Europe */
54     {
55 	"BST", 0 DST
56     },				/* British Summer */
57     {
58 	"NST", 3 W 30
59     },				/* Newfoundland Standard */
60     {
61 	"NDT", 3 W 30 DST
62     },				/* Newfoundland Daylight */
63     {
64 	"AST", 4 W 0
65     },				/* Atlantic Standard */
66     {
67 	"ADT", 4 W 0 DST
68     },				/* Atlantic Daylight */
69     {
70 	"EST", 5 W 0
71     },				/* Eastern Standard */
72     {
73 	"EDT", 5 W 0 DST
74     },				/* Eastern Daylight */
75     {
76 	"CST", 6 W 0
77     },				/* Central Standard */
78     {
79 	"CDT", 6 W 0 DST
80     },				/* Central Daylight */
81     {
82 	"MST", 7 W 0
83     },				/* Mountain Standard */
84     {
85 	"MDT", 7 W 0 DST
86     },				/* Mountain Daylight */
87     {
88 	"PST", 8 W 0
89     },				/* Pacific Standard */
90     {
91 	"PDT", 8 W 0 DST
92     },				/* Pacific Daylight */
93     {
94 	"YST", 9 W 0
95     },				/* Yukon Standard */
96     {
97 	"YDT", 9 W 0 DST
98     },				/* Yukon Daylight */
99     {
100 	"AKST", 9 W 0
101     },				/* Alaska Standard */
102     {
103 	"AKDT", 9 W 0 DST
104     },				/* Alaska Daylight */
105     {
106 	"HST", 10 W 0
107     },				/* Hawaii Standard */
108     {
109 	"HDT", 10 W 0 DST
110     },				/* Hawaii Daylight */
111     {
112 	"HAST", 10 W 0
113     },				/* Hawaii-Aleutian Standard */
114     {
115 	"HADT", 10 W 0 DST
116     },				/* Hawaii-Aleutian Daylight */
117     {
118 	"CET", 1 E 0
119     },				/* Central European */
120     {
121 	"CES", 1 E 0 DST
122     },				/* Central European Summer */
123     {
124 	"MET", 1 E 0
125     },				/* Middle European */
126     {
127 	"MES", 1 E 0 DST
128     },				/* Middle European Summer */
129     {
130 	"MEWT", 1 E 0
131     },				/* Middle European Winter */
132     {
133 	"MEST", 1 E 0 DST
134     },				/* Middle European Summer */
135     {
136 	"EET", 2 E 0
137     },				/* Eastern Europe */
138     {
139 	"MSK", 3 E 0
140     },				/* Moscow Winter */
141     {
142 	"MSD", 3 E 0 DST
143     },				/* Moscow Summer */
144     {
145 	"WAST", 8 E 0
146     },				/* West Australian Standard */
147     {
148 	"WADT", 8 E 0 DST
149     },				/* West Australian Daylight */
150     {
151 	"HKT", 8 E 0
152     },				/* Hong Kong */
153     {
154 	"CCT", 8 E 0
155     },				/* China Coast */
156     {
157 	"JST", 9 E 0
158     },				/* Japan Standard */
159     {
160 	"KST", 9 E 0
161     },				/* Korean Standard */
162     {
163 	"KST", 9 E 0 DST
164     },				/* Korean Daylight */
165     {
166 	"CAST", 9 E 30
167     },				/* Central Australian Standard */
168     {
169 	"CADT", 9 E 30 DST
170     },				/* Central Australian Daylight */
171     {
172 	"EAST", 10 E 0
173     },				/* Eastern Australian Standard */
174     {
175 	"EADT", 10 E 0 DST
176     },				/* Eastern Australian Daylight */
177     {
178 	"NZST", 12 E 0
179     },				/* New Zealand Standard */
180     {
181 	"NZDT", 12 E 0 DST
182     },				/* New Zealand Daylight */
183     {
184 	"A", UTC 1
185     },				/* UTC+1h */
186     {
187 	"B", UTC 2
188     },				/* UTC+2h */
189     {
190 	"C", UTC 3
191     },				/* UTC+3h */
192     {
193 	"D", UTC 4
194     },				/* UTC+4h */
195     {
196 	"E", UTC 5
197     },				/* UTC+5h */
198     {
199 	"F", UTC 6
200     },				/* UTC+6h */
201     {
202 	"G", UTC 7
203     },				/* UTC+7h */
204     {
205 	"H", UTC 8
206     },				/* UTC+8h */
207     {
208 	"I", UTC 9
209     },				/* UTC+9h */
210     {
211 	"K", UTC 10
212     },				/* UTC+10h */
213     {
214 	"L", UTC 11
215     },				/* UTC+11h */
216     {
217 	"M", UTC 12
218     },				/* UTC+12h */
219     {
220 	"N", UTC - 1
221     },				/* UTC-1h */
222     {
223 	"O", UTC - 2
224     },				/* UTC-2h */
225     {
226 	"P", UTC - 3
227     },				/* UTC-3h */
228     {
229 	"Q", UTC - 4
230     },				/* UTC-4h */
231     {
232 	"R", UTC - 5
233     },				/* UTC-5h */
234     {
235 	"S", UTC - 6
236     },				/* UTC-6h */
237     {
238 	"T", UTC - 7
239     },				/* UTC-7h */
240     {
241 	"U", UTC - 8
242     },				/* UTC-8h */
243     {
244 	"V", UTC - 9
245     },				/* UTC-9h */
246     {
247 	"W", UTC - 10
248     },				/* UTC-10h */
249     {
250 	"X", UTC - 11
251     },				/* UTC-11h */
252     {
253 	"Y", UTC - 12
254     },				/* UTC-12h */
255     {
256 	"Z", 0
257     },				/* Greenwich Mean */
258     {
259 	NULL, 0
260     },
261 };
262 
263 #undef MAXZ
264 #define MAXZ	6
265 
266 static int      month_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
267 
268 #define leap_year(y) (((y)&3) == 0  &&  ((y)%100 != 0 || (y)%400 == 0))
269 
270 
271 /*
272  * month_days
273  *
274  *	Returns:	How many days in the month.
275  */
276 
277 static int
month_days(int year,int month)278 month_days(int year, int month)
279 {
280     return month_table[month] + (month == 1 && leap_year(year));
281 }
282 
283 
284 /*
285  * numeric_zone
286  *
287  *	Parameters:	"date" is the numeric offset	{+-}[H]H[MM]
288  *
289  *	Returns:	number of minutes offset from GMT
290  */
291 
292 static int
numeric_zone(register char * date)293 numeric_zone(register char *date)
294 {
295     register int    n;
296     static char     num[MAXZ];
297     int             adjust = 0, sign;
298 
299     switch (*date) {
300 	case '-':
301 	    date++;
302 	    sign = -1;
303 	    break;
304 	case '+':
305 	    date++;
306 	default:		/* FALLTHROUGH */
307 	    sign = 1;
308 	    break;
309     }
310 
311     for (n = 0; n < MAXZ && *date && isdigit((int) *date);)
312 	num[n++] = *date++;
313     num[n] = '\0';
314 
315     switch (n) {
316 	case 4:		/* +HHMM */
317 	    adjust = atoi(num + 2);
318 	    num[2] = '\0';
319 	case 2:		/* +HH *//* FALLTHROUGH */
320 	    adjust += atoi(num) * 60;
321 	    break;
322 	case 3:		/* +HMM */
323 	    adjust = atoi(num + 1);
324 	    num[1] = '\0';
325 	case 1:		/* +H *//* FALLTHROUGH */
326 	    adjust += atoi(num) * 60;
327 	    break;
328 	default:		/* bad form */
329 	    break;
330     }
331     adjust *= sign;
332     return adjust;
333 }
334 
335 
336 /*
337  * tzone
338  *
339  *	Paremeters:	"date" is the strings containing TIMEZONE info
340  *
341  *	Returns:	number of minutes offset from GMT
342  */
343 
344 static int
tzone(register char * date)345 tzone(register char *date)
346 {
347     register int    i = 0;
348     static char     zone[MAXZ];
349     register struct zonetab *z;
350 
351     while (*date && isspace((int) *date))
352 	date++;
353 
354     if (*date == '+' || *date == '-' || isdigit((int) *date))
355 	return numeric_zone(date);
356 
357     for (; *date && isascii(*date); date++) {
358 	if (isspace((int) *date))
359 	    break;
360 	if (!isalnum((int) *date))
361 	    continue;		/* p.s.t. -> pst */
362 	if (i == MAXZ)
363 	    continue;
364 	zone[i++] = islower((int) *date) ? toupper((int) *date) : *date;
365     }
366 
367     while (*date && isspace((int) *date))
368 	date++;
369     if (i == 0)
370 	return 0;
371     if (*date == '+' || *date == '-' || isdigit((int) *date))
372 	return numeric_zone(date);
373 
374     zone[i] = '\0';
375 
376     for (z = ztab; z->tz_name != NULL; z++) {
377 	i = strcmp(zone, z->tz_name);
378 	if (i != 0)
379 	    continue;
380 	return z->tz_offset;
381     }
382     return 0;
383 }
384 
385 
386 /*
387  * next_int
388  *
389  *	Parameters:	"dp" is the string to process
390  *
391  *	Returns:	the integer value of the first "number"
392  *			or 0 if none
393  *
394  *	Side Effects:	moves *dp to the char after "number"
395  */
396 
397 static int
next_int(char ** dp)398 next_int(char **dp)
399 {
400     register char  *str = *dp;
401     register int    i;
402 
403     while (*str && !isdigit((int) *str))
404 	str++;
405 
406     i = atoi(str);
407     while (*str && isdigit((int) *str))
408 	str++;
409 
410     *dp = str;
411     return i;
412 }
413 
414 /*
415  * pack_date
416  *
417  *	Parameters:	"date" is the date string to be parsed
418  *
419  *	Returns:	roughly the number of seconds since the beginning
420  *			of the epoch to "date"
421  */
422 
423 time_stamp
pack_date(char * date)424 pack_date(char *date)
425 {
426     register int    sec, min, hour, day, month, year, i;
427 
428     if (date == NULL || (day = next_int(&date)) == 0)
429 	return 0;
430 
431     while (*date && isspace((int) *date))
432 	date++;
433 
434     if (date[0] == '\0' || date[1] == '\0' || date[2] == '\0')
435 	return 0;
436     switch (date[0]) {
437 	case 'J':
438 	case 'j':
439 	    if (date[1] == 'a' || date[1] == 'A') {
440 		month = 0;
441 		break;
442 	    }
443 	    if (date[2] == 'n' || date[2] == 'N') {
444 		month = 5;
445 		break;
446 	    }
447 	    month = 6;
448 	    break;
449 	case 'F':
450 	case 'f':
451 	    month = 1;
452 	    break;
453 	case 'M':
454 	case 'm':
455 	    if (date[2] == 'r' || date[2] == 'R') {
456 		month = 2;
457 		break;
458 	    }
459 	    month = 4;
460 	    break;
461 	case 'A':
462 	case 'a':
463 	    if (date[1] == 'p' || date[1] == 'P') {
464 		month = 3;
465 		break;
466 	    }
467 	    month = 7;
468 	    break;
469 	case 'S':
470 	case 's':
471 	    month = 8;
472 	    break;
473 	case 'O':
474 	case 'o':
475 	    month = 9;
476 	    break;
477 	case 'N':
478 	case 'n':
479 	    month = 10;
480 	    break;
481 	case 'D':
482 	case 'd':
483 	    month = 11;
484 	    break;
485 	default:
486 	    return 0;
487     }
488 
489     year = next_int(&date);
490     hour = next_int(&date);
491     min = next_int(&date);
492     if (*date == ':')
493 	sec = next_int(&date);
494     else
495 	sec = 0;
496 
497     if (year <= 1000)
498 	year += 1900;		/* YY -> xxYY */
499     if (year < 1970)
500 	year += 100;		/* must be after 1999 */
501 
502     /* Set `min' to be the number of minutes after midnight UTC.  */
503     min += hour * 60 - tzone(date);
504     for (; min < 0; min += 24 * 60)
505 	if (--day <= 0) {
506 	    if (--month < 0) {
507 		--year;
508 		month = 11;
509 	    }
510 	    day = month_days(year, month);
511 	}
512     for (; 24 * 60 <= min; min -= 24 * 60)
513 	if (month_days(year, month) < ++day) {
514 	    if (11 < ++month) {
515 		++year;
516 		month = 0;
517 	    }
518 	    day = 1;
519 	}
520     day += (year - 1970) * 366;
521     for (i = 0; i < month; i++)
522 	day += month_days(year, i);
523     --day;
524 
525     return (day * 24 * 60 * 60) + (min * 60) + sec;
526 }
527