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