Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmlibarchive/libarchive/archive_getdate.c
3153 views
1
/*
2
* This code is in the public domain and has no copyright.
3
*
4
* This is a plain C recursive-descent translation of an old
5
* public-domain YACC grammar that has been used for parsing dates in
6
* very many open-source projects.
7
*
8
* Since the original authors were generous enough to donate their
9
* work to the public domain, I feel compelled to match their
10
* generosity.
11
*
12
* Tim Kientzle, February 2009.
13
*/
14
15
/*
16
* Header comment from original getdate.y:
17
*/
18
19
/*
20
** Originally written by Steven M. Bellovin <[email protected]> while
21
** at the University of North Carolina at Chapel Hill. Later tweaked by
22
** a couple of people on Usenet. Completely overhauled by Rich $alz
23
** <[email protected]> and Jim Berets <[email protected]> in August, 1990;
24
**
25
** This grammar has 10 shift/reduce conflicts.
26
**
27
** This code is in the public domain and has no copyright.
28
*/
29
30
#ifndef CM_GET_DATE
31
#include "archive_platform.h"
32
#endif
33
34
#include <ctype.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <time.h>
39
40
#define __LIBARCHIVE_BUILD 1
41
#include "archive_getdate.h"
42
43
/* Basic time units. */
44
#define EPOCH 1970
45
#define MINUTE (60L)
46
#define HOUR (60L * MINUTE)
47
#define DAY (24L * HOUR)
48
49
/* Daylight-savings mode: on, off, or not yet known. */
50
enum DSTMODE { DSTon, DSToff, DSTmaybe };
51
/* Meridian: am or pm. */
52
enum { tAM, tPM };
53
/* Token types returned by nexttoken() */
54
enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
55
tUNUMBER, tZONE, tDST };
56
struct token { int token; time_t value; };
57
58
/*
59
* Parser state.
60
*/
61
struct gdstate {
62
struct token *tokenp; /* Pointer to next token. */
63
/* HaveXxxx counts how many of this kind of phrase we've seen;
64
* it's a fatal error to have more than one time, zone, day,
65
* or date phrase. */
66
int HaveYear;
67
int HaveMonth;
68
int HaveDay;
69
int HaveWeekDay; /* Day of week */
70
int HaveTime; /* Hour/minute/second */
71
int HaveZone; /* timezone and/or DST info */
72
int HaveRel; /* time offset; we can have more than one */
73
/* Absolute time values. */
74
time_t Timezone; /* Seconds offset from GMT */
75
time_t Day;
76
time_t Hour;
77
time_t Minutes;
78
time_t Month;
79
time_t Seconds;
80
time_t Year;
81
/* DST selection */
82
enum DSTMODE DSTmode;
83
/* Day of week accounting, e.g., "3rd Tuesday" */
84
time_t DayOrdinal; /* "3" in "3rd Tuesday" */
85
time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */
86
/* Relative time values: hour/day/week offsets are measured in
87
* seconds, month/year are counted in months. */
88
time_t RelMonth;
89
time_t RelSeconds;
90
};
91
92
/*
93
* A series of functions that recognize certain common time phrases.
94
* Each function returns 1 if it managed to make sense of some of the
95
* tokens, zero otherwise.
96
*/
97
98
/*
99
* hour:minute or hour:minute:second with optional AM, PM, or numeric
100
* timezone offset
101
*/
102
static int
103
timephrase(struct gdstate *gds)
104
{
105
if (gds->tokenp[0].token == tUNUMBER
106
&& gds->tokenp[1].token == ':'
107
&& gds->tokenp[2].token == tUNUMBER
108
&& gds->tokenp[3].token == ':'
109
&& gds->tokenp[4].token == tUNUMBER) {
110
/* "12:14:18" or "22:08:07" */
111
++gds->HaveTime;
112
gds->Hour = gds->tokenp[0].value;
113
gds->Minutes = gds->tokenp[2].value;
114
gds->Seconds = gds->tokenp[4].value;
115
gds->tokenp += 5;
116
}
117
else if (gds->tokenp[0].token == tUNUMBER
118
&& gds->tokenp[1].token == ':'
119
&& gds->tokenp[2].token == tUNUMBER) {
120
/* "12:14" or "22:08" */
121
++gds->HaveTime;
122
gds->Hour = gds->tokenp[0].value;
123
gds->Minutes = gds->tokenp[2].value;
124
gds->Seconds = 0;
125
gds->tokenp += 3;
126
}
127
else if (gds->tokenp[0].token == tUNUMBER
128
&& gds->tokenp[1].token == tAMPM) {
129
/* "7" is a time if it's followed by "am" or "pm" */
130
++gds->HaveTime;
131
gds->Hour = gds->tokenp[0].value;
132
gds->Minutes = gds->Seconds = 0;
133
/* We'll handle the AM/PM below. */
134
gds->tokenp += 1;
135
} else {
136
/* We can't handle this. */
137
return 0;
138
}
139
140
if (gds->tokenp[0].token == tAMPM) {
141
/* "7:12pm", "12:20:13am" */
142
if (gds->Hour == 12)
143
gds->Hour = 0;
144
if (gds->tokenp[0].value == tPM)
145
gds->Hour += 12;
146
gds->tokenp += 1;
147
}
148
if (gds->tokenp[0].token == '+'
149
&& gds->tokenp[1].token == tUNUMBER) {
150
/* "7:14+0700" */
151
gds->HaveZone++;
152
gds->DSTmode = DSToff;
153
gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
154
+ (gds->tokenp[1].value % 100) * MINUTE);
155
gds->tokenp += 2;
156
}
157
if (gds->tokenp[0].token == '-'
158
&& gds->tokenp[1].token == tUNUMBER) {
159
/* "19:14:12-0530" */
160
gds->HaveZone++;
161
gds->DSTmode = DSToff;
162
gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
163
+ (gds->tokenp[1].value % 100) * MINUTE);
164
gds->tokenp += 2;
165
}
166
return 1;
167
}
168
169
/*
170
* Timezone name, possibly including DST.
171
*/
172
static int
173
zonephrase(struct gdstate *gds)
174
{
175
if (gds->tokenp[0].token == tZONE
176
&& gds->tokenp[1].token == tDST) {
177
gds->HaveZone++;
178
gds->Timezone = gds->tokenp[0].value;
179
gds->DSTmode = DSTon;
180
gds->tokenp += 1;
181
return 1;
182
}
183
184
if (gds->tokenp[0].token == tZONE) {
185
gds->HaveZone++;
186
gds->Timezone = gds->tokenp[0].value;
187
gds->DSTmode = DSToff;
188
gds->tokenp += 1;
189
return 1;
190
}
191
192
if (gds->tokenp[0].token == tDAYZONE) {
193
gds->HaveZone++;
194
gds->Timezone = gds->tokenp[0].value;
195
gds->DSTmode = DSTon;
196
gds->tokenp += 1;
197
return 1;
198
}
199
return 0;
200
}
201
202
/*
203
* Year/month/day in various combinations.
204
*/
205
static int
206
datephrase(struct gdstate *gds)
207
{
208
if (gds->tokenp[0].token == tUNUMBER
209
&& gds->tokenp[1].token == '/'
210
&& gds->tokenp[2].token == tUNUMBER
211
&& gds->tokenp[3].token == '/'
212
&& gds->tokenp[4].token == tUNUMBER) {
213
gds->HaveYear++;
214
gds->HaveMonth++;
215
gds->HaveDay++;
216
if (gds->tokenp[0].value >= 13) {
217
/* First number is big: 2004/01/29, 99/02/17 */
218
gds->Year = gds->tokenp[0].value;
219
gds->Month = gds->tokenp[2].value;
220
gds->Day = gds->tokenp[4].value;
221
} else if ((gds->tokenp[4].value >= 13)
222
|| (gds->tokenp[2].value >= 13)) {
223
/* Last number is big: 01/07/98 */
224
/* Middle number is big: 01/29/04 */
225
gds->Month = gds->tokenp[0].value;
226
gds->Day = gds->tokenp[2].value;
227
gds->Year = gds->tokenp[4].value;
228
} else {
229
/* No significant clues: 02/03/04 */
230
gds->Month = gds->tokenp[0].value;
231
gds->Day = gds->tokenp[2].value;
232
gds->Year = gds->tokenp[4].value;
233
}
234
gds->tokenp += 5;
235
return 1;
236
}
237
238
if (gds->tokenp[0].token == tUNUMBER
239
&& gds->tokenp[1].token == '/'
240
&& gds->tokenp[2].token == tUNUMBER) {
241
/* "1/15" */
242
gds->HaveMonth++;
243
gds->HaveDay++;
244
gds->Month = gds->tokenp[0].value;
245
gds->Day = gds->tokenp[2].value;
246
gds->tokenp += 3;
247
return 1;
248
}
249
250
if (gds->tokenp[0].token == tUNUMBER
251
&& gds->tokenp[1].token == '-'
252
&& gds->tokenp[2].token == tUNUMBER
253
&& gds->tokenp[3].token == '-'
254
&& gds->tokenp[4].token == tUNUMBER) {
255
/* ISO 8601 format. yyyy-mm-dd. */
256
gds->HaveYear++;
257
gds->HaveMonth++;
258
gds->HaveDay++;
259
gds->Year = gds->tokenp[0].value;
260
gds->Month = gds->tokenp[2].value;
261
gds->Day = gds->tokenp[4].value;
262
gds->tokenp += 5;
263
return 1;
264
}
265
266
if (gds->tokenp[0].token == tUNUMBER
267
&& gds->tokenp[1].token == '-'
268
&& gds->tokenp[2].token == tMONTH
269
&& gds->tokenp[3].token == '-'
270
&& gds->tokenp[4].token == tUNUMBER) {
271
gds->HaveYear++;
272
gds->HaveMonth++;
273
gds->HaveDay++;
274
if (gds->tokenp[0].value > 31) {
275
/* e.g. 1992-Jun-17 */
276
gds->Year = gds->tokenp[0].value;
277
gds->Month = gds->tokenp[2].value;
278
gds->Day = gds->tokenp[4].value;
279
} else {
280
/* e.g. 17-JUN-1992. */
281
gds->Day = gds->tokenp[0].value;
282
gds->Month = gds->tokenp[2].value;
283
gds->Year = gds->tokenp[4].value;
284
}
285
gds->tokenp += 5;
286
return 1;
287
}
288
289
if (gds->tokenp[0].token == tMONTH
290
&& gds->tokenp[1].token == tUNUMBER
291
&& gds->tokenp[2].token == ','
292
&& gds->tokenp[3].token == tUNUMBER) {
293
/* "June 17, 2001" */
294
gds->HaveYear++;
295
gds->HaveMonth++;
296
gds->HaveDay++;
297
gds->Month = gds->tokenp[0].value;
298
gds->Day = gds->tokenp[1].value;
299
gds->Year = gds->tokenp[3].value;
300
gds->tokenp += 4;
301
return 1;
302
}
303
304
if (gds->tokenp[0].token == tMONTH
305
&& gds->tokenp[1].token == tUNUMBER) {
306
/* "May 3" */
307
gds->HaveMonth++;
308
gds->HaveDay++;
309
gds->Month = gds->tokenp[0].value;
310
gds->Day = gds->tokenp[1].value;
311
gds->tokenp += 2;
312
return 1;
313
}
314
315
if (gds->tokenp[0].token == tUNUMBER
316
&& gds->tokenp[1].token == tMONTH
317
&& gds->tokenp[2].token == tUNUMBER) {
318
/* "12 Sept 1997" */
319
gds->HaveYear++;
320
gds->HaveMonth++;
321
gds->HaveDay++;
322
gds->Day = gds->tokenp[0].value;
323
gds->Month = gds->tokenp[1].value;
324
gds->Year = gds->tokenp[2].value;
325
gds->tokenp += 3;
326
return 1;
327
}
328
329
if (gds->tokenp[0].token == tUNUMBER
330
&& gds->tokenp[1].token == tMONTH) {
331
/* "12 Sept" */
332
gds->HaveMonth++;
333
gds->HaveDay++;
334
gds->Day = gds->tokenp[0].value;
335
gds->Month = gds->tokenp[1].value;
336
gds->tokenp += 2;
337
return 1;
338
}
339
340
return 0;
341
}
342
343
/*
344
* Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.
345
*/
346
static int
347
relunitphrase(struct gdstate *gds)
348
{
349
if (gds->tokenp[0].token == '-'
350
&& gds->tokenp[1].token == tUNUMBER
351
&& gds->tokenp[2].token == tSEC_UNIT) {
352
/* "-3 hours" */
353
gds->HaveRel++;
354
gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;
355
gds->tokenp += 3;
356
return 1;
357
}
358
if (gds->tokenp[0].token == '+'
359
&& gds->tokenp[1].token == tUNUMBER
360
&& gds->tokenp[2].token == tSEC_UNIT) {
361
/* "+1 minute" */
362
gds->HaveRel++;
363
gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;
364
gds->tokenp += 3;
365
return 1;
366
}
367
if (gds->tokenp[0].token == tUNUMBER
368
&& gds->tokenp[1].token == tSEC_UNIT) {
369
/* "1 day" */
370
gds->HaveRel++;
371
gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;
372
gds->tokenp += 2;
373
return 1;
374
}
375
if (gds->tokenp[0].token == '-'
376
&& gds->tokenp[1].token == tUNUMBER
377
&& gds->tokenp[2].token == tMONTH_UNIT) {
378
/* "-3 months" */
379
gds->HaveRel++;
380
gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;
381
gds->tokenp += 3;
382
return 1;
383
}
384
if (gds->tokenp[0].token == '+'
385
&& gds->tokenp[1].token == tUNUMBER
386
&& gds->tokenp[2].token == tMONTH_UNIT) {
387
/* "+5 years" */
388
gds->HaveRel++;
389
gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;
390
gds->tokenp += 3;
391
return 1;
392
}
393
if (gds->tokenp[0].token == tUNUMBER
394
&& gds->tokenp[1].token == tMONTH_UNIT) {
395
/* "2 years" */
396
gds->HaveRel++;
397
gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;
398
gds->tokenp += 2;
399
return 1;
400
}
401
if (gds->tokenp[0].token == tSEC_UNIT) {
402
/* "now", "tomorrow" */
403
gds->HaveRel++;
404
gds->RelSeconds += gds->tokenp[0].value;
405
gds->tokenp += 1;
406
return 1;
407
}
408
if (gds->tokenp[0].token == tMONTH_UNIT) {
409
/* "month" */
410
gds->HaveRel++;
411
gds->RelMonth += gds->tokenp[0].value;
412
gds->tokenp += 1;
413
return 1;
414
}
415
return 0;
416
}
417
418
/*
419
* Day of the week specification.
420
*/
421
static int
422
dayphrase(struct gdstate *gds)
423
{
424
if (gds->tokenp[0].token == tDAY) {
425
/* "tues", "wednesday," */
426
gds->HaveWeekDay++;
427
gds->DayOrdinal = 1;
428
gds->DayNumber = gds->tokenp[0].value;
429
gds->tokenp += 1;
430
if (gds->tokenp[0].token == ',')
431
gds->tokenp += 1;
432
return 1;
433
}
434
if (gds->tokenp[0].token == tUNUMBER
435
&& gds->tokenp[1].token == tDAY) {
436
/* "second tues" "3 wed" */
437
gds->HaveWeekDay++;
438
gds->DayOrdinal = gds->tokenp[0].value;
439
gds->DayNumber = gds->tokenp[1].value;
440
gds->tokenp += 2;
441
return 1;
442
}
443
return 0;
444
}
445
446
/*
447
* Try to match a phrase using one of the above functions.
448
* This layer also deals with a couple of generic issues.
449
*/
450
static int
451
phrase(struct gdstate *gds)
452
{
453
if (timephrase(gds))
454
return 1;
455
if (zonephrase(gds))
456
return 1;
457
if (datephrase(gds))
458
return 1;
459
if (dayphrase(gds))
460
return 1;
461
if (relunitphrase(gds)) {
462
if (gds->tokenp[0].token == tAGO) {
463
gds->RelSeconds = -gds->RelSeconds;
464
gds->RelMonth = -gds->RelMonth;
465
gds->tokenp += 1;
466
}
467
return 1;
468
}
469
470
/* Bare numbers sometimes have meaning. */
471
if (gds->tokenp[0].token == tUNUMBER) {
472
if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
473
gds->HaveYear++;
474
gds->Year = gds->tokenp[0].value;
475
gds->tokenp += 1;
476
return 1;
477
}
478
479
if(gds->tokenp[0].value > 10000) {
480
/* "20040301" */
481
gds->HaveYear++;
482
gds->HaveMonth++;
483
gds->HaveDay++;
484
gds->Day= (gds->tokenp[0].value)%100;
485
gds->Month= (gds->tokenp[0].value/100)%100;
486
gds->Year = gds->tokenp[0].value/10000;
487
gds->tokenp += 1;
488
return 1;
489
}
490
491
if (gds->tokenp[0].value < 24) {
492
gds->HaveTime++;
493
gds->Hour = gds->tokenp[0].value;
494
gds->Minutes = 0;
495
gds->Seconds = 0;
496
gds->tokenp += 1;
497
return 1;
498
}
499
500
if ((gds->tokenp[0].value / 100 < 24)
501
&& (gds->tokenp[0].value % 100 < 60)) {
502
/* "513" is same as "5:13" */
503
gds->Hour = gds->tokenp[0].value / 100;
504
gds->Minutes = gds->tokenp[0].value % 100;
505
gds->Seconds = 0;
506
gds->tokenp += 1;
507
return 1;
508
}
509
}
510
511
return 0;
512
}
513
514
/*
515
* A dictionary of time words.
516
*/
517
static struct LEXICON {
518
size_t abbrev;
519
const char *name;
520
int type;
521
time_t value;
522
} const TimeWords[] = {
523
/* am/pm */
524
{ 0, "am", tAMPM, tAM },
525
{ 0, "pm", tAMPM, tPM },
526
527
/* Month names. */
528
{ 3, "january", tMONTH, 1 },
529
{ 3, "february", tMONTH, 2 },
530
{ 3, "march", tMONTH, 3 },
531
{ 3, "april", tMONTH, 4 },
532
{ 3, "may", tMONTH, 5 },
533
{ 3, "june", tMONTH, 6 },
534
{ 3, "july", tMONTH, 7 },
535
{ 3, "august", tMONTH, 8 },
536
{ 3, "september", tMONTH, 9 },
537
{ 3, "october", tMONTH, 10 },
538
{ 3, "november", tMONTH, 11 },
539
{ 3, "december", tMONTH, 12 },
540
541
/* Days of the week. */
542
{ 2, "sunday", tDAY, 0 },
543
{ 3, "monday", tDAY, 1 },
544
{ 2, "tuesday", tDAY, 2 },
545
{ 3, "wednesday", tDAY, 3 },
546
{ 2, "thursday", tDAY, 4 },
547
{ 2, "friday", tDAY, 5 },
548
{ 2, "saturday", tDAY, 6 },
549
550
/* Timezones: Offsets are in seconds. */
551
{ 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */
552
{ 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */
553
{ 0, "utc", tZONE, 0*HOUR },
554
{ 0, "wet", tZONE, 0*HOUR }, /* Western European */
555
{ 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */
556
{ 0, "wat", tZONE, 1*HOUR }, /* West Africa */
557
{ 0, "at", tZONE, 2*HOUR }, /* Azores */
558
/* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */
559
/* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/
560
{ 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */
561
{ 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */
562
{ 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */
563
{ 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */
564
{ 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */
565
{ 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */
566
{ 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */
567
{ 0, "cst", tZONE, 6*HOUR }, /* Central Standard */
568
{ 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */
569
{ 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */
570
{ 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */
571
{ 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */
572
{ 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */
573
{ 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */
574
{ 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */
575
{ 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */
576
{ 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */
577
{ 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */
578
{ 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */
579
{ 0, "nt", tZONE, 11*HOUR }, /* Nome */
580
{ 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */
581
{ 0, "cet", tZONE, -1*HOUR }, /* Central European */
582
{ 0, "met", tZONE, -1*HOUR }, /* Middle European */
583
{ 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */
584
{ 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */
585
{ 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */
586
{ 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */
587
{ 0, "fwt", tZONE, -1*HOUR }, /* French Winter */
588
{ 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */
589
{ 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */
590
{ 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */
591
{ 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */
592
{ 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */
593
{ 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */
594
{ 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */
595
{ 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */
596
/* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */
597
/* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */
598
{ 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */
599
{ 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */
600
{ 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/
601
{ 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */
602
{ 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */
603
{ 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */
604
{ 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */
605
{ 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */
606
{ 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */
607
{ 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */
608
{ 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */
609
{ 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */
610
{ 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */
611
{ 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */
612
613
{ 0, "dst", tDST, 0 },
614
615
/* Time units. */
616
{ 4, "years", tMONTH_UNIT, 12 },
617
{ 5, "months", tMONTH_UNIT, 1 },
618
{ 9, "fortnights", tSEC_UNIT, 14 * DAY },
619
{ 4, "weeks", tSEC_UNIT, 7 * DAY },
620
{ 3, "days", tSEC_UNIT, DAY },
621
{ 4, "hours", tSEC_UNIT, HOUR },
622
{ 3, "minutes", tSEC_UNIT, MINUTE },
623
{ 3, "seconds", tSEC_UNIT, 1 },
624
625
/* Relative-time words. */
626
{ 0, "tomorrow", tSEC_UNIT, DAY },
627
{ 0, "yesterday", tSEC_UNIT, -DAY },
628
{ 0, "today", tSEC_UNIT, 0 },
629
{ 0, "now", tSEC_UNIT, 0 },
630
{ 0, "last", tUNUMBER, -1 },
631
{ 0, "this", tSEC_UNIT, 0 },
632
{ 0, "next", tUNUMBER, 2 },
633
{ 0, "first", tUNUMBER, 1 },
634
{ 0, "1st", tUNUMBER, 1 },
635
/* { 0, "second", tUNUMBER, 2 }, */
636
{ 0, "2nd", tUNUMBER, 2 },
637
{ 0, "third", tUNUMBER, 3 },
638
{ 0, "3rd", tUNUMBER, 3 },
639
{ 0, "fourth", tUNUMBER, 4 },
640
{ 0, "4th", tUNUMBER, 4 },
641
{ 0, "fifth", tUNUMBER, 5 },
642
{ 0, "5th", tUNUMBER, 5 },
643
{ 0, "sixth", tUNUMBER, 6 },
644
{ 0, "seventh", tUNUMBER, 7 },
645
{ 0, "eighth", tUNUMBER, 8 },
646
{ 0, "ninth", tUNUMBER, 9 },
647
{ 0, "tenth", tUNUMBER, 10 },
648
{ 0, "eleventh", tUNUMBER, 11 },
649
{ 0, "twelfth", tUNUMBER, 12 },
650
{ 0, "ago", tAGO, 1 },
651
652
/* Military timezones. */
653
{ 0, "a", tZONE, 1*HOUR },
654
{ 0, "b", tZONE, 2*HOUR },
655
{ 0, "c", tZONE, 3*HOUR },
656
{ 0, "d", tZONE, 4*HOUR },
657
{ 0, "e", tZONE, 5*HOUR },
658
{ 0, "f", tZONE, 6*HOUR },
659
{ 0, "g", tZONE, 7*HOUR },
660
{ 0, "h", tZONE, 8*HOUR },
661
{ 0, "i", tZONE, 9*HOUR },
662
{ 0, "k", tZONE, 10*HOUR },
663
{ 0, "l", tZONE, 11*HOUR },
664
{ 0, "m", tZONE, 12*HOUR },
665
{ 0, "n", tZONE, -1*HOUR },
666
{ 0, "o", tZONE, -2*HOUR },
667
{ 0, "p", tZONE, -3*HOUR },
668
{ 0, "q", tZONE, -4*HOUR },
669
{ 0, "r", tZONE, -5*HOUR },
670
{ 0, "s", tZONE, -6*HOUR },
671
{ 0, "t", tZONE, -7*HOUR },
672
{ 0, "u", tZONE, -8*HOUR },
673
{ 0, "v", tZONE, -9*HOUR },
674
{ 0, "w", tZONE, -10*HOUR },
675
{ 0, "x", tZONE, -11*HOUR },
676
{ 0, "y", tZONE, -12*HOUR },
677
{ 0, "z", tZONE, 0*HOUR },
678
679
/* End of table. */
680
{ 0, NULL, 0, 0 }
681
};
682
683
/*
684
* Year is either:
685
* = A number from 0 to 99, which means a year from 1970 to 2069, or
686
* = The actual year (>=100).
687
*/
688
static time_t
689
Convert(time_t Month, time_t Day, time_t Year,
690
time_t Hours, time_t Minutes, time_t Seconds,
691
time_t Timezone, enum DSTMODE DSTmode)
692
{
693
signed char DaysInMonth[12] = {
694
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
695
};
696
time_t Julian;
697
int i;
698
struct tm *ltime;
699
#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
700
struct tm tmbuf;
701
#endif
702
703
if (Year < 69)
704
Year += 2000;
705
else if (Year < 100)
706
Year += 1900;
707
DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
708
? 29 : 28;
709
/* Checking for 2038 bogusly assumes that time_t is 32 bits. But
710
I'm too lazy to try to check for time_t overflow in another way. */
711
if (Year < EPOCH || Year >= 2038
712
|| Month < 1 || Month > 12
713
/* Lint fluff: "conversion from long may lose accuracy" */
714
|| Day < 1 || Day > DaysInMonth[(int)--Month]
715
|| Hours < 0 || Hours > 23
716
|| Minutes < 0 || Minutes > 59
717
|| Seconds < 0 || Seconds > 59)
718
return -1;
719
720
Julian = Day - 1;
721
for (i = 0; i < Month; i++)
722
Julian += DaysInMonth[i];
723
for (i = EPOCH; i < Year; i++)
724
Julian += 365 + (i % 4 == 0);
725
Julian *= DAY;
726
Julian += Timezone;
727
Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
728
#if defined(HAVE_LOCALTIME_S)
729
ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
730
#elif defined(HAVE_LOCALTIME_R)
731
ltime = localtime_r(&Julian, &tmbuf);
732
#else
733
ltime = localtime(&Julian);
734
#endif
735
if (DSTmode == DSTon
736
|| (DSTmode == DSTmaybe && ltime->tm_isdst))
737
Julian -= HOUR;
738
return Julian;
739
}
740
741
static time_t
742
DSTcorrect(time_t Start, time_t Future)
743
{
744
time_t StartDay;
745
time_t FutureDay;
746
struct tm *ltime;
747
#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
748
struct tm tmbuf;
749
#endif
750
#if defined(HAVE_LOCALTIME_S)
751
ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
752
#elif defined(HAVE_LOCALTIME_R)
753
ltime = localtime_r(&Start, &tmbuf);
754
#else
755
ltime = localtime(&Start);
756
#endif
757
StartDay = (ltime->tm_hour + 1) % 24;
758
#if defined(HAVE_LOCALTIME_S)
759
ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
760
#elif defined(HAVE_LOCALTIME_R)
761
ltime = localtime_r(&Future, &tmbuf);
762
#else
763
ltime = localtime(&Future);
764
#endif
765
FutureDay = (ltime->tm_hour + 1) % 24;
766
return (Future - Start) + (StartDay - FutureDay) * HOUR;
767
}
768
769
770
static time_t
771
RelativeDate(time_t Start, time_t zone, int dstmode,
772
time_t DayOrdinal, time_t DayNumber)
773
{
774
struct tm *tm;
775
time_t t, now;
776
#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
777
struct tm tmbuf;
778
#endif
779
780
t = Start - zone;
781
#if defined(HAVE_GMTIME_S)
782
tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
783
#elif defined(HAVE_GMTIME_R)
784
tm = gmtime_r(&t, &tmbuf);
785
#else
786
tm = gmtime(&t);
787
#endif
788
now = Start;
789
now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
790
now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
791
if (dstmode == DSTmaybe)
792
return DSTcorrect(Start, now);
793
return now - Start;
794
}
795
796
797
static time_t
798
RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
799
{
800
struct tm *tm;
801
time_t Month;
802
time_t Year;
803
#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
804
struct tm tmbuf;
805
#endif
806
807
if (RelMonth == 0)
808
return 0;
809
#if defined(HAVE_LOCALTIME_S)
810
tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
811
#elif defined(HAVE_LOCALTIME_R)
812
tm = localtime_r(&Start, &tmbuf);
813
#else
814
tm = localtime(&Start);
815
#endif
816
Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
817
Year = Month / 12;
818
Month = Month % 12 + 1;
819
return DSTcorrect(Start,
820
Convert(Month, (time_t)tm->tm_mday, Year,
821
(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
822
Timezone, DSTmaybe));
823
}
824
825
/*
826
* Tokenizer.
827
*/
828
static int
829
nexttoken(const char **in, time_t *value)
830
{
831
char c;
832
char buff[64];
833
834
for ( ; ; ) {
835
while (isspace((unsigned char)**in))
836
++*in;
837
838
/* Skip parenthesized comments. */
839
if (**in == '(') {
840
int Count = 0;
841
do {
842
c = *(*in)++;
843
if (c == '\0')
844
return c;
845
if (c == '(')
846
Count++;
847
else if (c == ')')
848
Count--;
849
} while (Count > 0);
850
continue;
851
}
852
853
/* Try the next token in the word table first. */
854
/* This allows us to match "2nd", for example. */
855
{
856
const char *src = *in;
857
const struct LEXICON *tp;
858
unsigned i = 0;
859
860
/* Force to lowercase and strip '.' characters. */
861
while (*src != '\0'
862
&& (isalnum((unsigned char)*src) || *src == '.')
863
&& i < sizeof(buff)-1) {
864
if (*src != '.') {
865
if (isupper((unsigned char)*src))
866
buff[i++] = tolower((unsigned char)*src);
867
else
868
buff[i++] = *src;
869
}
870
src++;
871
}
872
buff[i] = '\0';
873
874
/*
875
* Find the first match. If the word can be
876
* abbreviated, make sure we match at least
877
* the minimum abbreviation.
878
*/
879
for (tp = TimeWords; tp->name; tp++) {
880
size_t abbrev = tp->abbrev;
881
if (abbrev == 0)
882
abbrev = strlen(tp->name);
883
if (strlen(buff) >= abbrev
884
&& strncmp(tp->name, buff, strlen(buff))
885
== 0) {
886
/* Skip over token. */
887
*in = src;
888
/* Return the match. */
889
*value = tp->value;
890
return tp->type;
891
}
892
}
893
}
894
895
/*
896
* Not in the word table, maybe it's a number. Note:
897
* Because '-' and '+' have other special meanings, I
898
* don't deal with signed numbers here.
899
*/
900
if (isdigit((unsigned char)(c = **in))) {
901
for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )
902
*value = 10 * *value + c - '0';
903
(*in)--;
904
return (tUNUMBER);
905
}
906
907
return *(*in)++;
908
}
909
}
910
911
#define TM_YEAR_ORIGIN 1900
912
913
/* Yield A - B, measured in seconds. */
914
static long
915
difftm (struct tm *a, struct tm *b)
916
{
917
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
918
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
919
int days = (
920
/* difference in day of year */
921
a->tm_yday - b->tm_yday
922
/* + intervening leap days */
923
+ ((ay >> 2) - (by >> 2))
924
- (ay/100 - by/100)
925
+ ((ay/100 >> 2) - (by/100 >> 2))
926
/* + difference in years * 365 */
927
+ (long)(ay-by) * 365
928
);
929
return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR
930
+ (a->tm_min - b->tm_min) * MINUTE
931
+ (a->tm_sec - b->tm_sec));
932
}
933
934
/*
935
*
936
* The public function.
937
*
938
* TODO: tokens[] array should be dynamically sized.
939
*/
940
time_t
941
__archive_get_date(time_t now, const char *p)
942
{
943
struct token tokens[256];
944
struct gdstate _gds;
945
struct token *lasttoken;
946
struct gdstate *gds;
947
struct tm local, *tm;
948
struct tm gmt, *gmt_ptr;
949
time_t Start;
950
time_t tod;
951
long tzone;
952
953
/* Clear out the parsed token array. */
954
memset(tokens, 0, sizeof(tokens));
955
/* Initialize the parser state. */
956
memset(&_gds, 0, sizeof(_gds));
957
gds = &_gds;
958
959
/* Look up the current time. */
960
#if defined(HAVE_LOCALTIME_S)
961
tm = localtime_s(&local, &now) ? NULL : &local;
962
#elif defined(HAVE_LOCALTIME_R)
963
tm = localtime_r(&now, &local);
964
#else
965
memset(&local, 0, sizeof(local));
966
tm = localtime(&now);
967
#endif
968
if (tm == NULL)
969
return -1;
970
#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
971
local = *tm;
972
#endif
973
974
/* Look up UTC if we can and use that to determine the current
975
* timezone offset. */
976
#if defined(HAVE_GMTIME_S)
977
gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
978
#elif defined(HAVE_GMTIME_R)
979
gmt_ptr = gmtime_r(&now, &gmt);
980
#else
981
memset(&gmt, 0, sizeof(gmt));
982
gmt_ptr = gmtime(&now);
983
if (gmt_ptr != NULL) {
984
/* Copy, in case localtime and gmtime use the same buffer. */
985
gmt = *gmt_ptr;
986
}
987
#endif
988
if (gmt_ptr != NULL)
989
tzone = difftm (&gmt, &local);
990
else
991
/* This system doesn't understand timezones; fake it. */
992
tzone = 0;
993
if(local.tm_isdst)
994
tzone += HOUR;
995
996
/* Tokenize the input string. */
997
lasttoken = tokens;
998
while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
999
++lasttoken;
1000
if (lasttoken > tokens + 255)
1001
return -1;
1002
}
1003
gds->tokenp = tokens;
1004
1005
/* Match phrases until we run out of input tokens. */
1006
while (gds->tokenp < lasttoken) {
1007
if (!phrase(gds))
1008
return -1;
1009
}
1010
1011
/* Use current local timezone if none was specified. */
1012
if (!gds->HaveZone) {
1013
gds->Timezone = tzone;
1014
gds->DSTmode = DSTmaybe;
1015
}
1016
1017
/* If a timezone was specified, use that for generating the default
1018
* time components instead of the local timezone. */
1019
if (gds->HaveZone && gmt_ptr != NULL) {
1020
now -= gds->Timezone;
1021
#if defined(HAVE_GMTIME_S)
1022
gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
1023
#elif defined(HAVE_GMTIME_R)
1024
gmt_ptr = gmtime_r(&now, &gmt);
1025
#else
1026
gmt_ptr = gmtime(&now);
1027
#endif
1028
if (gmt_ptr != NULL)
1029
local = *gmt_ptr;
1030
now += gds->Timezone;
1031
}
1032
1033
if (!gds->HaveYear)
1034
gds->Year = local.tm_year + 1900;
1035
if (!gds->HaveMonth)
1036
gds->Month = local.tm_mon + 1;
1037
if (!gds->HaveDay)
1038
gds->Day = local.tm_mday;
1039
/* Note: No default for hour/min/sec; a specifier that just
1040
* gives date always refers to 00:00 on that date. */
1041
1042
/* If we saw more than one time, timezone, weekday, year, month,
1043
* or day, then give up. */
1044
if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
1045
|| gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
1046
return -1;
1047
1048
/* Compute an absolute time based on whatever absolute information
1049
* we collected. */
1050
if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
1051
|| gds->HaveTime || gds->HaveWeekDay) {
1052
Start = Convert(gds->Month, gds->Day, gds->Year,
1053
gds->Hour, gds->Minutes, gds->Seconds,
1054
gds->Timezone, gds->DSTmode);
1055
if (Start < 0)
1056
return -1;
1057
} else {
1058
Start = now;
1059
if (!gds->HaveRel)
1060
Start -= local.tm_hour * HOUR + local.tm_min * MINUTE
1061
+ local.tm_sec;
1062
}
1063
1064
/* Add the relative offset. */
1065
Start += gds->RelSeconds;
1066
Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
1067
1068
/* Adjust for day-of-week offsets. */
1069
if (gds->HaveWeekDay
1070
&& !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
1071
tod = RelativeDate(Start, gds->Timezone,
1072
gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
1073
Start += tod;
1074
}
1075
1076
/* -1 is an error indicator, so return 0 instead of -1 if
1077
* that's the actual time. */
1078
return Start == -1 ? 0 : Start;
1079
}
1080
1081
1082
#if defined(TEST)
1083
1084
/* ARGSUSED */
1085
int
1086
main(int argc, char **argv)
1087
{
1088
time_t d;
1089
time_t now = time(NULL);
1090
1091
while (*++argv != NULL) {
1092
(void)printf("Input: %s\n", *argv);
1093
d = get_date(now, *argv);
1094
if (d == -1)
1095
(void)printf("Bad format - couldn't convert.\n");
1096
else
1097
(void)printf("Output: %s\n", ctime(&d));
1098
}
1099
exit(0);
1100
/* NOTREACHED */
1101
}
1102
#endif /* defined(TEST) */
1103
1104