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