1238825Smm/* 2238825Smm * This code is in the public domain and has no copyright. 3238825Smm * 4238825Smm * This is a plain C recursive-descent translation of an old 5238825Smm * public-domain YACC grammar that has been used for parsing dates in 6238825Smm * very many open-source projects. 7238825Smm * 8238825Smm * Since the original authors were generous enough to donate their 9238825Smm * work to the public domain, I feel compelled to match their 10238825Smm * generosity. 11238825Smm * 12238825Smm * Tim Kientzle, February 2009. 13238825Smm */ 14238825Smm 15238825Smm/* 16238825Smm * Header comment from original getdate.y: 17238825Smm */ 18238825Smm 19238825Smm/* 20238825Smm** Originally written by Steven M. Bellovin <smb@research.att.com> while 21238825Smm** at the University of North Carolina at Chapel Hill. Later tweaked by 22238825Smm** a couple of people on Usenet. Completely overhauled by Rich $alz 23238825Smm** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; 24238825Smm** 25238825Smm** This grammar has 10 shift/reduce conflicts. 26238825Smm** 27238825Smm** This code is in the public domain and has no copyright. 28238825Smm*/ 29238825Smm 30358088Smm#include "archive_platform.h" 31238825Smm#ifdef __FreeBSD__ 32238825Smm#include <sys/cdefs.h> 33238825Smm__FBSDID("$FreeBSD$"); 34238825Smm#endif 35238825Smm 36238825Smm#include <ctype.h> 37238825Smm#include <stdio.h> 38238825Smm#include <stdlib.h> 39238825Smm#include <string.h> 40238825Smm#include <time.h> 41238825Smm 42299529Smm#define __LIBARCHIVE_BUILD 1 43299529Smm#include "archive_getdate.h" 44238825Smm 45238825Smm/* Basic time units. */ 46238825Smm#define EPOCH 1970 47238825Smm#define MINUTE (60L) 48238825Smm#define HOUR (60L * MINUTE) 49238825Smm#define DAY (24L * HOUR) 50238825Smm 51238825Smm/* Daylight-savings mode: on, off, or not yet known. */ 52238825Smmenum DSTMODE { DSTon, DSToff, DSTmaybe }; 53238825Smm/* Meridian: am or pm. */ 54238825Smmenum { tAM, tPM }; 55238825Smm/* Token types returned by nexttoken() */ 56238825Smmenum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT, 57238825Smm tUNUMBER, tZONE, tDST }; 58238825Smmstruct token { int token; time_t value; }; 59238825Smm 60238825Smm/* 61238825Smm * Parser state. 62238825Smm */ 63238825Smmstruct gdstate { 64238825Smm struct token *tokenp; /* Pointer to next token. */ 65238825Smm /* HaveXxxx counts how many of this kind of phrase we've seen; 66238825Smm * it's a fatal error to have more than one time, zone, day, 67238825Smm * or date phrase. */ 68238825Smm int HaveYear; 69238825Smm int HaveMonth; 70238825Smm int HaveDay; 71238825Smm int HaveWeekDay; /* Day of week */ 72238825Smm int HaveTime; /* Hour/minute/second */ 73238825Smm int HaveZone; /* timezone and/or DST info */ 74238825Smm int HaveRel; /* time offset; we can have more than one */ 75238825Smm /* Absolute time values. */ 76238825Smm time_t Timezone; /* Seconds offset from GMT */ 77238825Smm time_t Day; 78238825Smm time_t Hour; 79238825Smm time_t Minutes; 80238825Smm time_t Month; 81238825Smm time_t Seconds; 82238825Smm time_t Year; 83238825Smm /* DST selection */ 84238825Smm enum DSTMODE DSTmode; 85238825Smm /* Day of week accounting, e.g., "3rd Tuesday" */ 86238825Smm time_t DayOrdinal; /* "3" in "3rd Tuesday" */ 87238825Smm time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */ 88238825Smm /* Relative time values: hour/day/week offsets are measured in 89238825Smm * seconds, month/year are counted in months. */ 90238825Smm time_t RelMonth; 91238825Smm time_t RelSeconds; 92238825Smm}; 93238825Smm 94238825Smm/* 95238825Smm * A series of functions that recognize certain common time phrases. 96238825Smm * Each function returns 1 if it managed to make sense of some of the 97238825Smm * tokens, zero otherwise. 98238825Smm */ 99238825Smm 100238825Smm/* 101238825Smm * hour:minute or hour:minute:second with optional AM, PM, or numeric 102238825Smm * timezone offset 103238825Smm */ 104238825Smmstatic int 105238825Smmtimephrase(struct gdstate *gds) 106238825Smm{ 107238825Smm if (gds->tokenp[0].token == tUNUMBER 108238825Smm && gds->tokenp[1].token == ':' 109238825Smm && gds->tokenp[2].token == tUNUMBER 110238825Smm && gds->tokenp[3].token == ':' 111238825Smm && gds->tokenp[4].token == tUNUMBER) { 112238825Smm /* "12:14:18" or "22:08:07" */ 113238825Smm ++gds->HaveTime; 114238825Smm gds->Hour = gds->tokenp[0].value; 115238825Smm gds->Minutes = gds->tokenp[2].value; 116238825Smm gds->Seconds = gds->tokenp[4].value; 117238825Smm gds->tokenp += 5; 118238825Smm } 119238825Smm else if (gds->tokenp[0].token == tUNUMBER 120238825Smm && gds->tokenp[1].token == ':' 121238825Smm && gds->tokenp[2].token == tUNUMBER) { 122238825Smm /* "12:14" or "22:08" */ 123238825Smm ++gds->HaveTime; 124238825Smm gds->Hour = gds->tokenp[0].value; 125238825Smm gds->Minutes = gds->tokenp[2].value; 126238825Smm gds->Seconds = 0; 127238825Smm gds->tokenp += 3; 128238825Smm } 129238825Smm else if (gds->tokenp[0].token == tUNUMBER 130238825Smm && gds->tokenp[1].token == tAMPM) { 131238825Smm /* "7" is a time if it's followed by "am" or "pm" */ 132238825Smm ++gds->HaveTime; 133238825Smm gds->Hour = gds->tokenp[0].value; 134238825Smm gds->Minutes = gds->Seconds = 0; 135238825Smm /* We'll handle the AM/PM below. */ 136238825Smm gds->tokenp += 1; 137238825Smm } else { 138238825Smm /* We can't handle this. */ 139238825Smm return 0; 140238825Smm } 141238825Smm 142238825Smm if (gds->tokenp[0].token == tAMPM) { 143238825Smm /* "7:12pm", "12:20:13am" */ 144238825Smm if (gds->Hour == 12) 145238825Smm gds->Hour = 0; 146238825Smm if (gds->tokenp[0].value == tPM) 147238825Smm gds->Hour += 12; 148238825Smm gds->tokenp += 1; 149238825Smm } 150238825Smm if (gds->tokenp[0].token == '+' 151238825Smm && gds->tokenp[1].token == tUNUMBER) { 152238825Smm /* "7:14+0700" */ 153238825Smm gds->HaveZone++; 154238825Smm gds->DSTmode = DSToff; 155238825Smm gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR 156238825Smm + (gds->tokenp[1].value % 100) * MINUTE); 157238825Smm gds->tokenp += 2; 158238825Smm } 159238825Smm if (gds->tokenp[0].token == '-' 160238825Smm && gds->tokenp[1].token == tUNUMBER) { 161238825Smm /* "19:14:12-0530" */ 162238825Smm gds->HaveZone++; 163238825Smm gds->DSTmode = DSToff; 164238825Smm gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR 165238825Smm + (gds->tokenp[1].value % 100) * MINUTE); 166238825Smm gds->tokenp += 2; 167238825Smm } 168238825Smm return 1; 169238825Smm} 170238825Smm 171238825Smm/* 172238825Smm * Timezone name, possibly including DST. 173238825Smm */ 174238825Smmstatic int 175238825Smmzonephrase(struct gdstate *gds) 176238825Smm{ 177238825Smm if (gds->tokenp[0].token == tZONE 178238825Smm && gds->tokenp[1].token == tDST) { 179238825Smm gds->HaveZone++; 180238825Smm gds->Timezone = gds->tokenp[0].value; 181238825Smm gds->DSTmode = DSTon; 182238825Smm gds->tokenp += 1; 183238825Smm return 1; 184238825Smm } 185238825Smm 186238825Smm if (gds->tokenp[0].token == tZONE) { 187238825Smm gds->HaveZone++; 188238825Smm gds->Timezone = gds->tokenp[0].value; 189238825Smm gds->DSTmode = DSToff; 190238825Smm gds->tokenp += 1; 191238825Smm return 1; 192238825Smm } 193238825Smm 194238825Smm if (gds->tokenp[0].token == tDAYZONE) { 195238825Smm gds->HaveZone++; 196238825Smm gds->Timezone = gds->tokenp[0].value; 197238825Smm gds->DSTmode = DSTon; 198238825Smm gds->tokenp += 1; 199238825Smm return 1; 200238825Smm } 201238825Smm return 0; 202238825Smm} 203238825Smm 204238825Smm/* 205238825Smm * Year/month/day in various combinations. 206238825Smm */ 207238825Smmstatic int 208238825Smmdatephrase(struct gdstate *gds) 209238825Smm{ 210238825Smm if (gds->tokenp[0].token == tUNUMBER 211238825Smm && gds->tokenp[1].token == '/' 212238825Smm && gds->tokenp[2].token == tUNUMBER 213238825Smm && gds->tokenp[3].token == '/' 214238825Smm && gds->tokenp[4].token == tUNUMBER) { 215238825Smm gds->HaveYear++; 216238825Smm gds->HaveMonth++; 217238825Smm gds->HaveDay++; 218238825Smm if (gds->tokenp[0].value >= 13) { 219238825Smm /* First number is big: 2004/01/29, 99/02/17 */ 220238825Smm gds->Year = gds->tokenp[0].value; 221238825Smm gds->Month = gds->tokenp[2].value; 222238825Smm gds->Day = gds->tokenp[4].value; 223238825Smm } else if ((gds->tokenp[4].value >= 13) 224238825Smm || (gds->tokenp[2].value >= 13)) { 225238825Smm /* Last number is big: 01/07/98 */ 226238825Smm /* Middle number is big: 01/29/04 */ 227238825Smm gds->Month = gds->tokenp[0].value; 228238825Smm gds->Day = gds->tokenp[2].value; 229238825Smm gds->Year = gds->tokenp[4].value; 230238825Smm } else { 231238825Smm /* No significant clues: 02/03/04 */ 232238825Smm gds->Month = gds->tokenp[0].value; 233238825Smm gds->Day = gds->tokenp[2].value; 234238825Smm gds->Year = gds->tokenp[4].value; 235238825Smm } 236238825Smm gds->tokenp += 5; 237238825Smm return 1; 238238825Smm } 239238825Smm 240238825Smm if (gds->tokenp[0].token == tUNUMBER 241238825Smm && gds->tokenp[1].token == '/' 242238825Smm && gds->tokenp[2].token == tUNUMBER) { 243238825Smm /* "1/15" */ 244238825Smm gds->HaveMonth++; 245238825Smm gds->HaveDay++; 246238825Smm gds->Month = gds->tokenp[0].value; 247238825Smm gds->Day = gds->tokenp[2].value; 248238825Smm gds->tokenp += 3; 249238825Smm return 1; 250238825Smm } 251238825Smm 252238825Smm if (gds->tokenp[0].token == tUNUMBER 253238825Smm && gds->tokenp[1].token == '-' 254238825Smm && gds->tokenp[2].token == tUNUMBER 255238825Smm && gds->tokenp[3].token == '-' 256238825Smm && gds->tokenp[4].token == tUNUMBER) { 257238825Smm /* ISO 8601 format. yyyy-mm-dd. */ 258238825Smm gds->HaveYear++; 259238825Smm gds->HaveMonth++; 260238825Smm gds->HaveDay++; 261238825Smm gds->Year = gds->tokenp[0].value; 262238825Smm gds->Month = gds->tokenp[2].value; 263238825Smm gds->Day = gds->tokenp[4].value; 264238825Smm gds->tokenp += 5; 265238825Smm return 1; 266238825Smm } 267238825Smm 268238825Smm if (gds->tokenp[0].token == tUNUMBER 269238825Smm && gds->tokenp[1].token == '-' 270238825Smm && gds->tokenp[2].token == tMONTH 271238825Smm && gds->tokenp[3].token == '-' 272238825Smm && gds->tokenp[4].token == tUNUMBER) { 273238825Smm gds->HaveYear++; 274238825Smm gds->HaveMonth++; 275238825Smm gds->HaveDay++; 276238825Smm if (gds->tokenp[0].value > 31) { 277238825Smm /* e.g. 1992-Jun-17 */ 278238825Smm gds->Year = gds->tokenp[0].value; 279238825Smm gds->Month = gds->tokenp[2].value; 280238825Smm gds->Day = gds->tokenp[4].value; 281238825Smm } else { 282238825Smm /* e.g. 17-JUN-1992. */ 283238825Smm gds->Day = gds->tokenp[0].value; 284238825Smm gds->Month = gds->tokenp[2].value; 285238825Smm gds->Year = gds->tokenp[4].value; 286238825Smm } 287238825Smm gds->tokenp += 5; 288238825Smm return 1; 289238825Smm } 290238825Smm 291238825Smm if (gds->tokenp[0].token == tMONTH 292238825Smm && gds->tokenp[1].token == tUNUMBER 293238825Smm && gds->tokenp[2].token == ',' 294238825Smm && gds->tokenp[3].token == tUNUMBER) { 295238825Smm /* "June 17, 2001" */ 296238825Smm gds->HaveYear++; 297238825Smm gds->HaveMonth++; 298238825Smm gds->HaveDay++; 299238825Smm gds->Month = gds->tokenp[0].value; 300238825Smm gds->Day = gds->tokenp[1].value; 301238825Smm gds->Year = gds->tokenp[3].value; 302238825Smm gds->tokenp += 4; 303238825Smm return 1; 304238825Smm } 305238825Smm 306238825Smm if (gds->tokenp[0].token == tMONTH 307238825Smm && gds->tokenp[1].token == tUNUMBER) { 308238825Smm /* "May 3" */ 309238825Smm gds->HaveMonth++; 310238825Smm gds->HaveDay++; 311238825Smm gds->Month = gds->tokenp[0].value; 312238825Smm gds->Day = gds->tokenp[1].value; 313238825Smm gds->tokenp += 2; 314238825Smm return 1; 315238825Smm } 316238825Smm 317238825Smm if (gds->tokenp[0].token == tUNUMBER 318238825Smm && gds->tokenp[1].token == tMONTH 319238825Smm && gds->tokenp[2].token == tUNUMBER) { 320238825Smm /* "12 Sept 1997" */ 321238825Smm gds->HaveYear++; 322238825Smm gds->HaveMonth++; 323238825Smm gds->HaveDay++; 324238825Smm gds->Day = gds->tokenp[0].value; 325238825Smm gds->Month = gds->tokenp[1].value; 326238825Smm gds->Year = gds->tokenp[2].value; 327238825Smm gds->tokenp += 3; 328238825Smm return 1; 329238825Smm } 330238825Smm 331238825Smm if (gds->tokenp[0].token == tUNUMBER 332238825Smm && gds->tokenp[1].token == tMONTH) { 333238825Smm /* "12 Sept" */ 334238825Smm gds->HaveMonth++; 335238825Smm gds->HaveDay++; 336238825Smm gds->Day = gds->tokenp[0].value; 337238825Smm gds->Month = gds->tokenp[1].value; 338238825Smm gds->tokenp += 2; 339238825Smm return 1; 340238825Smm } 341238825Smm 342238825Smm return 0; 343238825Smm} 344238825Smm 345238825Smm/* 346238825Smm * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc. 347238825Smm */ 348238825Smmstatic int 349238825Smmrelunitphrase(struct gdstate *gds) 350238825Smm{ 351238825Smm if (gds->tokenp[0].token == '-' 352238825Smm && gds->tokenp[1].token == tUNUMBER 353238825Smm && gds->tokenp[2].token == tSEC_UNIT) { 354238825Smm /* "-3 hours" */ 355238825Smm gds->HaveRel++; 356238825Smm gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value; 357238825Smm gds->tokenp += 3; 358238825Smm return 1; 359238825Smm } 360238825Smm if (gds->tokenp[0].token == '+' 361238825Smm && gds->tokenp[1].token == tUNUMBER 362238825Smm && gds->tokenp[2].token == tSEC_UNIT) { 363238825Smm /* "+1 minute" */ 364238825Smm gds->HaveRel++; 365238825Smm gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value; 366238825Smm gds->tokenp += 3; 367238825Smm return 1; 368238825Smm } 369238825Smm if (gds->tokenp[0].token == tUNUMBER 370238825Smm && gds->tokenp[1].token == tSEC_UNIT) { 371238825Smm /* "1 day" */ 372238825Smm gds->HaveRel++; 373246229Skientzle gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value; 374246229Skientzle gds->tokenp += 2; 375238825Smm return 1; 376238825Smm } 377238825Smm if (gds->tokenp[0].token == '-' 378238825Smm && gds->tokenp[1].token == tUNUMBER 379238825Smm && gds->tokenp[2].token == tMONTH_UNIT) { 380238825Smm /* "-3 months" */ 381238825Smm gds->HaveRel++; 382238825Smm gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value; 383238825Smm gds->tokenp += 3; 384238825Smm return 1; 385238825Smm } 386238825Smm if (gds->tokenp[0].token == '+' 387238825Smm && gds->tokenp[1].token == tUNUMBER 388238825Smm && gds->tokenp[2].token == tMONTH_UNIT) { 389238825Smm /* "+5 years" */ 390238825Smm gds->HaveRel++; 391238825Smm gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value; 392238825Smm gds->tokenp += 3; 393238825Smm return 1; 394238825Smm } 395238825Smm if (gds->tokenp[0].token == tUNUMBER 396238825Smm && gds->tokenp[1].token == tMONTH_UNIT) { 397238825Smm /* "2 years" */ 398238825Smm gds->HaveRel++; 399238825Smm gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value; 400238825Smm gds->tokenp += 2; 401238825Smm return 1; 402238825Smm } 403238825Smm if (gds->tokenp[0].token == tSEC_UNIT) { 404238825Smm /* "now", "tomorrow" */ 405238825Smm gds->HaveRel++; 406238825Smm gds->RelSeconds += gds->tokenp[0].value; 407246229Skientzle gds->tokenp += 1; 408238825Smm return 1; 409238825Smm } 410238825Smm if (gds->tokenp[0].token == tMONTH_UNIT) { 411238825Smm /* "month" */ 412238825Smm gds->HaveRel++; 413238825Smm gds->RelMonth += gds->tokenp[0].value; 414238825Smm gds->tokenp += 1; 415238825Smm return 1; 416238825Smm } 417238825Smm return 0; 418238825Smm} 419238825Smm 420238825Smm/* 421238825Smm * Day of the week specification. 422238825Smm */ 423238825Smmstatic int 424238825Smmdayphrase(struct gdstate *gds) 425238825Smm{ 426238825Smm if (gds->tokenp[0].token == tDAY) { 427238825Smm /* "tues", "wednesday," */ 428238825Smm gds->HaveWeekDay++; 429238825Smm gds->DayOrdinal = 1; 430238825Smm gds->DayNumber = gds->tokenp[0].value; 431238825Smm gds->tokenp += 1; 432238825Smm if (gds->tokenp[0].token == ',') 433238825Smm gds->tokenp += 1; 434238825Smm return 1; 435238825Smm } 436238825Smm if (gds->tokenp[0].token == tUNUMBER 437238825Smm && gds->tokenp[1].token == tDAY) { 438238825Smm /* "second tues" "3 wed" */ 439238825Smm gds->HaveWeekDay++; 440238825Smm gds->DayOrdinal = gds->tokenp[0].value; 441238825Smm gds->DayNumber = gds->tokenp[1].value; 442238825Smm gds->tokenp += 2; 443238825Smm return 1; 444238825Smm } 445238825Smm return 0; 446238825Smm} 447238825Smm 448238825Smm/* 449238825Smm * Try to match a phrase using one of the above functions. 450238825Smm * This layer also deals with a couple of generic issues. 451238825Smm */ 452238825Smmstatic int 453238825Smmphrase(struct gdstate *gds) 454238825Smm{ 455238825Smm if (timephrase(gds)) 456238825Smm return 1; 457238825Smm if (zonephrase(gds)) 458238825Smm return 1; 459238825Smm if (datephrase(gds)) 460238825Smm return 1; 461238825Smm if (dayphrase(gds)) 462238825Smm return 1; 463238825Smm if (relunitphrase(gds)) { 464238825Smm if (gds->tokenp[0].token == tAGO) { 465238825Smm gds->RelSeconds = -gds->RelSeconds; 466238825Smm gds->RelMonth = -gds->RelMonth; 467238825Smm gds->tokenp += 1; 468238825Smm } 469238825Smm return 1; 470238825Smm } 471238825Smm 472238825Smm /* Bare numbers sometimes have meaning. */ 473238825Smm if (gds->tokenp[0].token == tUNUMBER) { 474238825Smm if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) { 475238825Smm gds->HaveYear++; 476238825Smm gds->Year = gds->tokenp[0].value; 477238825Smm gds->tokenp += 1; 478238825Smm return 1; 479238825Smm } 480238825Smm 481238825Smm if(gds->tokenp[0].value > 10000) { 482238825Smm /* "20040301" */ 483238825Smm gds->HaveYear++; 484238825Smm gds->HaveMonth++; 485238825Smm gds->HaveDay++; 486238825Smm gds->Day= (gds->tokenp[0].value)%100; 487238825Smm gds->Month= (gds->tokenp[0].value/100)%100; 488238825Smm gds->Year = gds->tokenp[0].value/10000; 489238825Smm gds->tokenp += 1; 490238825Smm return 1; 491238825Smm } 492238825Smm 493238825Smm if (gds->tokenp[0].value < 24) { 494238825Smm gds->HaveTime++; 495238825Smm gds->Hour = gds->tokenp[0].value; 496238825Smm gds->Minutes = 0; 497238825Smm gds->Seconds = 0; 498238825Smm gds->tokenp += 1; 499238825Smm return 1; 500238825Smm } 501238825Smm 502238825Smm if ((gds->tokenp[0].value / 100 < 24) 503238825Smm && (gds->tokenp[0].value % 100 < 60)) { 504238825Smm /* "513" is same as "5:13" */ 505238825Smm gds->Hour = gds->tokenp[0].value / 100; 506238825Smm gds->Minutes = gds->tokenp[0].value % 100; 507238825Smm gds->Seconds = 0; 508238825Smm gds->tokenp += 1; 509238825Smm return 1; 510238825Smm } 511238825Smm } 512238825Smm 513238825Smm return 0; 514238825Smm} 515238825Smm 516238825Smm/* 517238825Smm * A dictionary of time words. 518238825Smm */ 519238825Smmstatic struct LEXICON { 520238825Smm size_t abbrev; 521238825Smm const char *name; 522238825Smm int type; 523238825Smm time_t value; 524238825Smm} const TimeWords[] = { 525238825Smm /* am/pm */ 526238825Smm { 0, "am", tAMPM, tAM }, 527238825Smm { 0, "pm", tAMPM, tPM }, 528238825Smm 529238825Smm /* Month names. */ 530238825Smm { 3, "january", tMONTH, 1 }, 531238825Smm { 3, "february", tMONTH, 2 }, 532238825Smm { 3, "march", tMONTH, 3 }, 533238825Smm { 3, "april", tMONTH, 4 }, 534238825Smm { 3, "may", tMONTH, 5 }, 535238825Smm { 3, "june", tMONTH, 6 }, 536238825Smm { 3, "july", tMONTH, 7 }, 537238825Smm { 3, "august", tMONTH, 8 }, 538238825Smm { 3, "september", tMONTH, 9 }, 539238825Smm { 3, "october", tMONTH, 10 }, 540238825Smm { 3, "november", tMONTH, 11 }, 541238825Smm { 3, "december", tMONTH, 12 }, 542238825Smm 543238825Smm /* Days of the week. */ 544238825Smm { 2, "sunday", tDAY, 0 }, 545238825Smm { 3, "monday", tDAY, 1 }, 546238825Smm { 2, "tuesday", tDAY, 2 }, 547238825Smm { 3, "wednesday", tDAY, 3 }, 548238825Smm { 2, "thursday", tDAY, 4 }, 549238825Smm { 2, "friday", tDAY, 5 }, 550238825Smm { 2, "saturday", tDAY, 6 }, 551238825Smm 552238825Smm /* Timezones: Offsets are in seconds. */ 553238825Smm { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */ 554238825Smm { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */ 555238825Smm { 0, "utc", tZONE, 0*HOUR }, 556238825Smm { 0, "wet", tZONE, 0*HOUR }, /* Western European */ 557238825Smm { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */ 558238825Smm { 0, "wat", tZONE, 1*HOUR }, /* West Africa */ 559238825Smm { 0, "at", tZONE, 2*HOUR }, /* Azores */ 560238825Smm /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */ 561238825Smm /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/ 562238825Smm { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */ 563238825Smm { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */ 564238825Smm { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */ 565238825Smm { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */ 566238825Smm { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */ 567238825Smm { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */ 568238825Smm { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */ 569238825Smm { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */ 570238825Smm { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */ 571238825Smm { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */ 572238825Smm { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */ 573238825Smm { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */ 574238825Smm { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */ 575238825Smm { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */ 576238825Smm { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */ 577238825Smm { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */ 578238825Smm { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */ 579238825Smm { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */ 580238825Smm { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */ 581238825Smm { 0, "nt", tZONE, 11*HOUR }, /* Nome */ 582238825Smm { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */ 583238825Smm { 0, "cet", tZONE, -1*HOUR }, /* Central European */ 584238825Smm { 0, "met", tZONE, -1*HOUR }, /* Middle European */ 585238825Smm { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */ 586238825Smm { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */ 587238825Smm { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */ 588238825Smm { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */ 589238825Smm { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */ 590238825Smm { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */ 591238825Smm { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */ 592238825Smm { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */ 593238825Smm { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */ 594238825Smm { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */ 595238825Smm { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */ 596238825Smm { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */ 597238825Smm { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */ 598238825Smm /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */ 599238825Smm /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */ 600238825Smm { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */ 601238825Smm { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */ 602238825Smm { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/ 603238825Smm { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */ 604238825Smm { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */ 605238825Smm { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */ 606238825Smm { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */ 607238825Smm { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */ 608238825Smm { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */ 609238825Smm { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */ 610238825Smm { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */ 611238825Smm { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */ 612238825Smm { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */ 613238825Smm { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */ 614238825Smm 615238825Smm { 0, "dst", tDST, 0 }, 616238825Smm 617238825Smm /* Time units. */ 618238825Smm { 4, "years", tMONTH_UNIT, 12 }, 619238825Smm { 5, "months", tMONTH_UNIT, 1 }, 620238825Smm { 9, "fortnights", tSEC_UNIT, 14 * DAY }, 621238825Smm { 4, "weeks", tSEC_UNIT, 7 * DAY }, 622238825Smm { 3, "days", tSEC_UNIT, DAY }, 623238825Smm { 4, "hours", tSEC_UNIT, HOUR }, 624238825Smm { 3, "minutes", tSEC_UNIT, MINUTE }, 625238825Smm { 3, "seconds", tSEC_UNIT, 1 }, 626238825Smm 627238825Smm /* Relative-time words. */ 628238825Smm { 0, "tomorrow", tSEC_UNIT, DAY }, 629238825Smm { 0, "yesterday", tSEC_UNIT, -DAY }, 630238825Smm { 0, "today", tSEC_UNIT, 0 }, 631238825Smm { 0, "now", tSEC_UNIT, 0 }, 632238825Smm { 0, "last", tUNUMBER, -1 }, 633238825Smm { 0, "this", tSEC_UNIT, 0 }, 634238825Smm { 0, "next", tUNUMBER, 2 }, 635238825Smm { 0, "first", tUNUMBER, 1 }, 636238825Smm { 0, "1st", tUNUMBER, 1 }, 637238825Smm/* { 0, "second", tUNUMBER, 2 }, */ 638238825Smm { 0, "2nd", tUNUMBER, 2 }, 639238825Smm { 0, "third", tUNUMBER, 3 }, 640238825Smm { 0, "3rd", tUNUMBER, 3 }, 641238825Smm { 0, "fourth", tUNUMBER, 4 }, 642238825Smm { 0, "4th", tUNUMBER, 4 }, 643238825Smm { 0, "fifth", tUNUMBER, 5 }, 644238825Smm { 0, "5th", tUNUMBER, 5 }, 645238825Smm { 0, "sixth", tUNUMBER, 6 }, 646238825Smm { 0, "seventh", tUNUMBER, 7 }, 647238825Smm { 0, "eighth", tUNUMBER, 8 }, 648238825Smm { 0, "ninth", tUNUMBER, 9 }, 649238825Smm { 0, "tenth", tUNUMBER, 10 }, 650238825Smm { 0, "eleventh", tUNUMBER, 11 }, 651238825Smm { 0, "twelfth", tUNUMBER, 12 }, 652238825Smm { 0, "ago", tAGO, 1 }, 653238825Smm 654238825Smm /* Military timezones. */ 655238825Smm { 0, "a", tZONE, 1*HOUR }, 656238825Smm { 0, "b", tZONE, 2*HOUR }, 657238825Smm { 0, "c", tZONE, 3*HOUR }, 658238825Smm { 0, "d", tZONE, 4*HOUR }, 659238825Smm { 0, "e", tZONE, 5*HOUR }, 660238825Smm { 0, "f", tZONE, 6*HOUR }, 661238825Smm { 0, "g", tZONE, 7*HOUR }, 662238825Smm { 0, "h", tZONE, 8*HOUR }, 663238825Smm { 0, "i", tZONE, 9*HOUR }, 664238825Smm { 0, "k", tZONE, 10*HOUR }, 665238825Smm { 0, "l", tZONE, 11*HOUR }, 666238825Smm { 0, "m", tZONE, 12*HOUR }, 667238825Smm { 0, "n", tZONE, -1*HOUR }, 668238825Smm { 0, "o", tZONE, -2*HOUR }, 669238825Smm { 0, "p", tZONE, -3*HOUR }, 670238825Smm { 0, "q", tZONE, -4*HOUR }, 671238825Smm { 0, "r", tZONE, -5*HOUR }, 672238825Smm { 0, "s", tZONE, -6*HOUR }, 673238825Smm { 0, "t", tZONE, -7*HOUR }, 674238825Smm { 0, "u", tZONE, -8*HOUR }, 675238825Smm { 0, "v", tZONE, -9*HOUR }, 676238825Smm { 0, "w", tZONE, -10*HOUR }, 677238825Smm { 0, "x", tZONE, -11*HOUR }, 678238825Smm { 0, "y", tZONE, -12*HOUR }, 679238825Smm { 0, "z", tZONE, 0*HOUR }, 680238825Smm 681238825Smm /* End of table. */ 682238825Smm { 0, NULL, 0, 0 } 683238825Smm}; 684238825Smm 685238825Smm/* 686238825Smm * Year is either: 687238825Smm * = A number from 0 to 99, which means a year from 1970 to 2069, or 688238825Smm * = The actual year (>=100). 689238825Smm */ 690238825Smmstatic time_t 691238825SmmConvert(time_t Month, time_t Day, time_t Year, 692238825Smm time_t Hours, time_t Minutes, time_t Seconds, 693238825Smm time_t Timezone, enum DSTMODE DSTmode) 694238825Smm{ 695318482Smm signed char DaysInMonth[12] = { 696238825Smm 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 697238825Smm }; 698358088Smm time_t Julian; 699358088Smm int i; 700358088Smm struct tm *ltime; 701358088Smm#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) 702358088Smm struct tm tmbuf; 703358088Smm#endif 704358088Smm#if defined(HAVE__LOCALTIME64_S) 705358088Smm errno_t terr; 706358088Smm __time64_t tmptime; 707358088Smm#endif 708238825Smm 709238825Smm if (Year < 69) 710238825Smm Year += 2000; 711238825Smm else if (Year < 100) 712238825Smm Year += 1900; 713238825Smm DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) 714238825Smm ? 29 : 28; 715238825Smm /* Checking for 2038 bogusly assumes that time_t is 32 bits. But 716238825Smm I'm too lazy to try to check for time_t overflow in another way. */ 717238825Smm if (Year < EPOCH || Year > 2038 718238825Smm || Month < 1 || Month > 12 719238825Smm /* Lint fluff: "conversion from long may lose accuracy" */ 720238825Smm || Day < 1 || Day > DaysInMonth[(int)--Month] 721238825Smm || Hours < 0 || Hours > 23 722238825Smm || Minutes < 0 || Minutes > 59 723238825Smm || Seconds < 0 || Seconds > 59) 724238825Smm return -1; 725238825Smm 726238825Smm Julian = Day - 1; 727238825Smm for (i = 0; i < Month; i++) 728238825Smm Julian += DaysInMonth[i]; 729238825Smm for (i = EPOCH; i < Year; i++) 730238825Smm Julian += 365 + (i % 4 == 0); 731238825Smm Julian *= DAY; 732238825Smm Julian += Timezone; 733238825Smm Julian += Hours * HOUR + Minutes * MINUTE + Seconds; 734358088Smm#if defined(HAVE_LOCALTIME_R) 735358088Smm ltime = localtime_r(&Julian, &tmbuf); 736358088Smm#elif defined(HAVE__LOCALTIME64_S) 737358088Smm tmptime = Julian; 738358088Smm terr = _localtime64_s(&tmbuf, &tmptime); 739358088Smm if (terr) 740358088Smm ltime = NULL; 741358088Smm else 742358088Smm ltime = &tmbuf; 743358088Smm#else 744358088Smm ltime = localtime(&Julian); 745358088Smm#endif 746238825Smm if (DSTmode == DSTon 747358088Smm || (DSTmode == DSTmaybe && ltime->tm_isdst)) 748238825Smm Julian -= HOUR; 749238825Smm return Julian; 750238825Smm} 751238825Smm 752238825Smmstatic time_t 753238825SmmDSTcorrect(time_t Start, time_t Future) 754238825Smm{ 755358088Smm time_t StartDay; 756358088Smm time_t FutureDay; 757358088Smm struct tm *ltime; 758358088Smm#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) 759358088Smm struct tm tmbuf; 760358088Smm#endif 761358088Smm#if defined(HAVE__LOCALTIME64_S) 762358088Smm errno_t terr; 763358088Smm __time64_t tmptime; 764358088Smm#endif 765238825Smm 766358088Smm#if defined(HAVE_LOCALTIME_R) 767358088Smm ltime = localtime_r(&Start, &tmbuf); 768358088Smm#elif defined(HAVE__LOCALTIME64_S) 769358088Smm tmptime = Start; 770358088Smm terr = _localtime64_s(&tmbuf, &tmptime); 771358088Smm if (terr) 772358088Smm ltime = NULL; 773358088Smm else 774358088Smm ltime = &tmbuf; 775358088Smm#else 776358088Smm ltime = localtime(&Start); 777358088Smm#endif 778358088Smm StartDay = (ltime->tm_hour + 1) % 24; 779358088Smm#if defined(HAVE_LOCALTIME_R) 780358088Smm ltime = localtime_r(&Future, &tmbuf); 781358088Smm#elif defined(HAVE__LOCALTIME64_S) 782358088Smm tmptime = Future; 783358088Smm terr = _localtime64_s(&tmbuf, &tmptime); 784358088Smm if (terr) 785358088Smm ltime = NULL; 786358088Smm else 787358088Smm ltime = &tmbuf; 788358088Smm#else 789358088Smm ltime = localtime(&Future); 790358088Smm#endif 791358088Smm FutureDay = (ltime->tm_hour + 1) % 24; 792238825Smm return (Future - Start) + (StartDay - FutureDay) * HOUR; 793238825Smm} 794238825Smm 795238825Smm 796238825Smmstatic time_t 797238825SmmRelativeDate(time_t Start, time_t zone, int dstmode, 798238825Smm time_t DayOrdinal, time_t DayNumber) 799238825Smm{ 800238825Smm struct tm *tm; 801238825Smm time_t t, now; 802358088Smm#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) 803358088Smm struct tm tmbuf; 804358088Smm#endif 805358088Smm#if defined(HAVE__GMTIME64_S) 806358088Smm errno_t terr; 807358088Smm __time64_t tmptime; 808358088Smm#endif 809238825Smm 810238825Smm t = Start - zone; 811358088Smm#if defined(HAVE_GMTIME_R) 812358088Smm tm = gmtime_r(&t, &tmbuf); 813358088Smm#elif defined(HAVE__GMTIME64_S) 814358088Smm tmptime = t; 815358088Smm terr = _gmtime64_s(&tmbuf, &tmptime); 816358088Smm if (terr) 817358088Smm tm = NULL; 818358088Smm else 819358088Smm tm = &tmbuf; 820358088Smm#else 821238825Smm tm = gmtime(&t); 822358088Smm#endif 823238825Smm now = Start; 824238825Smm now += DAY * ((DayNumber - tm->tm_wday + 7) % 7); 825238825Smm now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); 826238825Smm if (dstmode == DSTmaybe) 827238825Smm return DSTcorrect(Start, now); 828238825Smm return now - Start; 829238825Smm} 830238825Smm 831238825Smm 832238825Smmstatic time_t 833238825SmmRelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) 834238825Smm{ 835238825Smm struct tm *tm; 836238825Smm time_t Month; 837238825Smm time_t Year; 838358088Smm#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) 839358088Smm struct tm tmbuf; 840358088Smm#endif 841358088Smm#if defined(HAVE__LOCALTIME64_S) 842358088Smm errno_t terr; 843358088Smm __time64_t tmptime; 844358088Smm#endif 845238825Smm 846238825Smm if (RelMonth == 0) 847238825Smm return 0; 848358088Smm#if defined(HAVE_LOCALTIME_R) 849358088Smm tm = localtime_r(&Start, &tmbuf); 850358088Smm#elif defined(HAVE__LOCALTIME64_S) 851358088Smm tmptime = Start; 852358088Smm terr = _localtime64_s(&tmbuf, &tmptime); 853358088Smm if (terr) 854358088Smm tm = NULL; 855358088Smm else 856358088Smm tm = &tmbuf; 857358088Smm#else 858238825Smm tm = localtime(&Start); 859358088Smm#endif 860238825Smm Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; 861238825Smm Year = Month / 12; 862238825Smm Month = Month % 12 + 1; 863238825Smm return DSTcorrect(Start, 864238825Smm Convert(Month, (time_t)tm->tm_mday, Year, 865238825Smm (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, 866238825Smm Timezone, DSTmaybe)); 867238825Smm} 868238825Smm 869238825Smm/* 870238825Smm * Tokenizer. 871238825Smm */ 872238825Smmstatic int 873299529Smmnexttoken(const char **in, time_t *value) 874238825Smm{ 875238825Smm char c; 876238825Smm char buff[64]; 877238825Smm 878238825Smm for ( ; ; ) { 879238825Smm while (isspace((unsigned char)**in)) 880238825Smm ++*in; 881238825Smm 882238825Smm /* Skip parenthesized comments. */ 883238825Smm if (**in == '(') { 884238825Smm int Count = 0; 885238825Smm do { 886238825Smm c = *(*in)++; 887238825Smm if (c == '\0') 888238825Smm return c; 889238825Smm if (c == '(') 890238825Smm Count++; 891238825Smm else if (c == ')') 892238825Smm Count--; 893238825Smm } while (Count > 0); 894238825Smm continue; 895238825Smm } 896238825Smm 897238825Smm /* Try the next token in the word table first. */ 898238825Smm /* This allows us to match "2nd", for example. */ 899238825Smm { 900299529Smm const char *src = *in; 901238825Smm const struct LEXICON *tp; 902238825Smm unsigned i = 0; 903238825Smm 904238825Smm /* Force to lowercase and strip '.' characters. */ 905238825Smm while (*src != '\0' 906238825Smm && (isalnum((unsigned char)*src) || *src == '.') 907238825Smm && i < sizeof(buff)-1) { 908238825Smm if (*src != '.') { 909238825Smm if (isupper((unsigned char)*src)) 910238825Smm buff[i++] = tolower((unsigned char)*src); 911238825Smm else 912238825Smm buff[i++] = *src; 913238825Smm } 914238825Smm src++; 915238825Smm } 916238825Smm buff[i] = '\0'; 917238825Smm 918238825Smm /* 919238825Smm * Find the first match. If the word can be 920238825Smm * abbreviated, make sure we match at least 921238825Smm * the minimum abbreviation. 922238825Smm */ 923238825Smm for (tp = TimeWords; tp->name; tp++) { 924238825Smm size_t abbrev = tp->abbrev; 925238825Smm if (abbrev == 0) 926238825Smm abbrev = strlen(tp->name); 927238825Smm if (strlen(buff) >= abbrev 928238825Smm && strncmp(tp->name, buff, strlen(buff)) 929238825Smm == 0) { 930238825Smm /* Skip over token. */ 931238825Smm *in = src; 932238825Smm /* Return the match. */ 933238825Smm *value = tp->value; 934238825Smm return tp->type; 935238825Smm } 936238825Smm } 937238825Smm } 938238825Smm 939238825Smm /* 940238825Smm * Not in the word table, maybe it's a number. Note: 941238825Smm * Because '-' and '+' have other special meanings, I 942238825Smm * don't deal with signed numbers here. 943238825Smm */ 944238825Smm if (isdigit((unsigned char)(c = **in))) { 945238825Smm for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); ) 946238825Smm *value = 10 * *value + c - '0'; 947238825Smm (*in)--; 948238825Smm return (tUNUMBER); 949238825Smm } 950238825Smm 951238825Smm return *(*in)++; 952238825Smm } 953238825Smm} 954238825Smm 955238825Smm#define TM_YEAR_ORIGIN 1900 956238825Smm 957238825Smm/* Yield A - B, measured in seconds. */ 958238825Smmstatic long 959238825Smmdifftm (struct tm *a, struct tm *b) 960238825Smm{ 961238825Smm int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); 962238825Smm int by = b->tm_year + (TM_YEAR_ORIGIN - 1); 963238825Smm int days = ( 964238825Smm /* difference in day of year */ 965238825Smm a->tm_yday - b->tm_yday 966238825Smm /* + intervening leap days */ 967238825Smm + ((ay >> 2) - (by >> 2)) 968238825Smm - (ay/100 - by/100) 969238825Smm + ((ay/100 >> 2) - (by/100 >> 2)) 970238825Smm /* + difference in years * 365 */ 971238825Smm + (long)(ay-by) * 365 972238825Smm ); 973238825Smm return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR 974238825Smm + (a->tm_min - b->tm_min) * MINUTE 975238825Smm + (a->tm_sec - b->tm_sec)); 976238825Smm} 977238825Smm 978238825Smm/* 979238825Smm * 980238825Smm * The public function. 981238825Smm * 982238825Smm * TODO: tokens[] array should be dynamically sized. 983238825Smm */ 984238825Smmtime_t 985299529Smm__archive_get_date(time_t now, const char *p) 986238825Smm{ 987238825Smm struct token tokens[256]; 988238825Smm struct gdstate _gds; 989238825Smm struct token *lasttoken; 990238825Smm struct gdstate *gds; 991238825Smm struct tm local, *tm; 992238825Smm struct tm gmt, *gmt_ptr; 993238825Smm time_t Start; 994238825Smm time_t tod; 995238825Smm long tzone; 996358088Smm#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S) 997358088Smm errno_t terr; 998358088Smm __time64_t tmptime; 999358088Smm#endif 1000238825Smm 1001238825Smm /* Clear out the parsed token array. */ 1002238825Smm memset(tokens, 0, sizeof(tokens)); 1003238825Smm /* Initialize the parser state. */ 1004238825Smm memset(&_gds, 0, sizeof(_gds)); 1005238825Smm gds = &_gds; 1006238825Smm 1007238825Smm /* Look up the current time. */ 1008358088Smm#if defined(HAVE_LOCALTIME_R) 1009358088Smm tm = localtime_r(&now, &local); 1010358088Smm#elif defined(HAVE__LOCALTIME64_S) 1011358088Smm tmptime = now; 1012358088Smm terr = _localtime64_s(&local, &tmptime); 1013358088Smm if (terr) 1014358088Smm tm = NULL; 1015358088Smm else 1016358088Smm tm = &local; 1017358088Smm#else 1018238825Smm memset(&local, 0, sizeof(local)); 1019358088Smm tm = localtime(&now); 1020358088Smm#endif 1021238825Smm if (tm == NULL) 1022238825Smm return -1; 1023358088Smm#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S) 1024238825Smm local = *tm; 1025358088Smm#endif 1026238825Smm 1027238825Smm /* Look up UTC if we can and use that to determine the current 1028238825Smm * timezone offset. */ 1029358088Smm#if defined(HAVE_GMTIME_R) 1030358088Smm gmt_ptr = gmtime_r(&now, &gmt); 1031358088Smm#elif defined(HAVE__GMTIME64_S) 1032358088Smm tmptime = now; 1033358088Smm terr = _gmtime64_s(&gmt, &tmptime); 1034358088Smm if (terr) 1035358088Smm gmt_ptr = NULL; 1036358088Smm else 1037358088Smm gmt_ptr = &gmt; 1038358088Smm#else 1039238825Smm memset(&gmt, 0, sizeof(gmt)); 1040358088Smm gmt_ptr = gmtime(&now); 1041238825Smm if (gmt_ptr != NULL) { 1042238825Smm /* Copy, in case localtime and gmtime use the same buffer. */ 1043238825Smm gmt = *gmt_ptr; 1044238825Smm } 1045358088Smm#endif 1046238825Smm if (gmt_ptr != NULL) 1047238825Smm tzone = difftm (&gmt, &local); 1048238825Smm else 1049238825Smm /* This system doesn't understand timezones; fake it. */ 1050238825Smm tzone = 0; 1051238825Smm if(local.tm_isdst) 1052238825Smm tzone += HOUR; 1053238825Smm 1054238825Smm /* Tokenize the input string. */ 1055238825Smm lasttoken = tokens; 1056238825Smm while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) { 1057238825Smm ++lasttoken; 1058238825Smm if (lasttoken > tokens + 255) 1059238825Smm return -1; 1060238825Smm } 1061238825Smm gds->tokenp = tokens; 1062238825Smm 1063238825Smm /* Match phrases until we run out of input tokens. */ 1064238825Smm while (gds->tokenp < lasttoken) { 1065238825Smm if (!phrase(gds)) 1066238825Smm return -1; 1067238825Smm } 1068238825Smm 1069238825Smm /* Use current local timezone if none was specified. */ 1070238825Smm if (!gds->HaveZone) { 1071238825Smm gds->Timezone = tzone; 1072238825Smm gds->DSTmode = DSTmaybe; 1073238825Smm } 1074238825Smm 1075238825Smm /* If a timezone was specified, use that for generating the default 1076238825Smm * time components instead of the local timezone. */ 1077238825Smm if (gds->HaveZone && gmt_ptr != NULL) { 1078238825Smm now -= gds->Timezone; 1079358088Smm#if defined(HAVE_GMTIME_R) 1080358088Smm gmt_ptr = gmtime_r(&now, &gmt); 1081358088Smm#elif defined(HAVE__GMTIME64_S) 1082358088Smm tmptime = now; 1083358088Smm terr = _gmtime64_s(&gmt, &tmptime); 1084358088Smm if (terr) 1085358088Smm gmt_ptr = NULL; 1086358088Smm else 1087358088Smm gmt_ptr = &gmt; 1088358088Smm#else 1089358088Smm gmt_ptr = gmtime(&now); 1090358088Smm#endif 1091238825Smm if (gmt_ptr != NULL) 1092238825Smm local = *gmt_ptr; 1093238825Smm now += gds->Timezone; 1094238825Smm } 1095238825Smm 1096238825Smm if (!gds->HaveYear) 1097238825Smm gds->Year = local.tm_year + 1900; 1098238825Smm if (!gds->HaveMonth) 1099238825Smm gds->Month = local.tm_mon + 1; 1100238825Smm if (!gds->HaveDay) 1101238825Smm gds->Day = local.tm_mday; 1102238825Smm /* Note: No default for hour/min/sec; a specifier that just 1103238825Smm * gives date always refers to 00:00 on that date. */ 1104238825Smm 1105238825Smm /* If we saw more than one time, timezone, weekday, year, month, 1106238825Smm * or day, then give up. */ 1107238825Smm if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1 1108238825Smm || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1) 1109238825Smm return -1; 1110238825Smm 1111238825Smm /* Compute an absolute time based on whatever absolute information 1112238825Smm * we collected. */ 1113238825Smm if (gds->HaveYear || gds->HaveMonth || gds->HaveDay 1114238825Smm || gds->HaveTime || gds->HaveWeekDay) { 1115238825Smm Start = Convert(gds->Month, gds->Day, gds->Year, 1116238825Smm gds->Hour, gds->Minutes, gds->Seconds, 1117238825Smm gds->Timezone, gds->DSTmode); 1118238825Smm if (Start < 0) 1119238825Smm return -1; 1120238825Smm } else { 1121238825Smm Start = now; 1122238825Smm if (!gds->HaveRel) 1123238825Smm Start -= local.tm_hour * HOUR + local.tm_min * MINUTE 1124238825Smm + local.tm_sec; 1125238825Smm } 1126238825Smm 1127238825Smm /* Add the relative offset. */ 1128238825Smm Start += gds->RelSeconds; 1129238825Smm Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth); 1130238825Smm 1131238825Smm /* Adjust for day-of-week offsets. */ 1132238825Smm if (gds->HaveWeekDay 1133238825Smm && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) { 1134238825Smm tod = RelativeDate(Start, gds->Timezone, 1135238825Smm gds->DSTmode, gds->DayOrdinal, gds->DayNumber); 1136238825Smm Start += tod; 1137238825Smm } 1138238825Smm 1139238825Smm /* -1 is an error indicator, so return 0 instead of -1 if 1140238825Smm * that's the actual time. */ 1141238825Smm return Start == -1 ? 0 : Start; 1142238825Smm} 1143238825Smm 1144238825Smm 1145238825Smm#if defined(TEST) 1146238825Smm 1147238825Smm/* ARGSUSED */ 1148238825Smmint 1149238825Smmmain(int argc, char **argv) 1150238825Smm{ 1151238825Smm time_t d; 1152246229Skientzle time_t now = time(NULL); 1153238825Smm 1154238825Smm while (*++argv != NULL) { 1155238825Smm (void)printf("Input: %s\n", *argv); 1156246229Skientzle d = get_date(now, *argv); 1157238825Smm if (d == -1) 1158238825Smm (void)printf("Bad format - couldn't convert.\n"); 1159238825Smm else 1160238825Smm (void)printf("Output: %s\n", ctime(&d)); 1161238825Smm } 1162238825Smm exit(0); 1163238825Smm /* NOTREACHED */ 1164238825Smm} 1165238825Smm#endif /* defined(TEST) */ 1166