nabstime.c 12.1 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*-------------------------------------------------------------------------
 *
 * nabstime.c--
 *    parse almost any absolute date getdate(3) can (& some it can't)
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
10
 *    $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.31 1997/08/19 21:34:42 momjian Exp $
11 12 13 14 15 16 17
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
18

19
#include "postgres.h"
20
#include <miscadmin.h>
21 22 23
#ifdef HAVE_FLOAT_H
# include <float.h>
#endif
24 25 26
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
27 28 29
#ifndef USE_POSIX_TIME
#include <sys/timeb.h>
#endif
30
#include "utils/builtins.h"
31 32
#include "access/xact.h"

33
static AbsoluteTime tm2abstime(struct tm *tm, int tz);
34

35 36 37 38
#define MIN_DAYNUM -24856			/* December 13, 1901 */
#define MAX_DAYNUM 24854			/* January 18, 2038 */


39 40 41
/* GetCurrentAbsoluteTime()
 * Get the current system time. Set timezone parameters if not specified elsewhere.
 * Define HasTZSet to allow clients to specify the default timezone.
42 43 44 45
 *
 * Returns the number of seconds since epoch (January 1 1970 GMT)
 */
AbsoluteTime
46
GetCurrentAbsoluteTime(void)
47
{
48
    time_t now;
49

50
#ifdef USE_POSIX_TIME
51 52
    struct tm *tm;

53
    now = time(NULL);
54
#else /* ! USE_POSIX_TIME */
55
    struct timeb tb;		/* the old V7-ism */
56

57
    ftime(&tb);
58
    now = tb.time;
59
#endif
60

61 62
    if (! HasCTZSet) {
#ifdef USE_POSIX_TIME
63
#if defined(HAVE_TZSET) && defined(HAVE_INT_TIMEZONE)
64
	tm = localtime(&now);
65

66 67 68 69 70 71 72 73 74 75 76 77
	CDayLight = tm->tm_isdst;
	CTimeZone = (tm->tm_isdst? (timezone - 3600): timezone);
	strcpy( CTZName, tzname[tm->tm_isdst]);
#else /* !HAVE_TZSET */
	tm = localtime(&now);

	CTimeZone = - tm->tm_gmtoff;	/* tm_gmtoff is Sun/DEC-ism */
	CDayLight = (tm->tm_isdst > 0);
	/* XXX is there a better way to get local timezone string w/o tzname? - tgl 97/03/18 */
	strftime( CTZName, MAXTZLEN, "%Z", tm);
	/* XXX FreeBSD man pages indicate that this should work - tgl 97/04/23 */
	strcpy(CTZName, tm->tm_zone);
78
#endif
79
#else /* ! USE_POSIX_TIME */
80 81
	CTimeZone = tb.timezone * 60;
	CDayLight = (tb.dstflag != 0);
82
	/* XXX does this work to get the local timezone string in V7? - tgl 97/03/18 */
83
	strftime( CTZName, MAXTZLEN, "%Z", localtime(&now));
84
#endif 
85 86 87 88 89 90
    };

#ifdef DATEDEBUG
printf( "GetCurrentAbsoluteTime- timezone is %s -> %d seconds from UTC\n",
 CTZName, CTimeZone);
#endif
91 92 93

    return((AbsoluteTime) now);
} /* GetCurrentAbsoluteTime() */
94 95


96 97
void
GetCurrentTime(struct tm *tm)
98
{
99 100
    int tz;

101
    abstime2tm( GetCurrentTransactionStartTime(), &tz, tm, NULL);
102 103 104 105 106 107

    return;
} /* GetCurrentTime() */


void
108
abstime2tm(AbsoluteTime time, int *tzp, struct tm *tm, char *tzn)
109
{
110 111 112 113
#ifdef USE_POSIX_TIME
    struct tm *tx;
#else /* ! USE_POSIX_TIME */
    struct timeb tb;		/* the old V7-ism */
114

115
    ftime(&tb);
116
#endif
117 118

#ifdef USE_POSIX_TIME
119
    if (tzp != NULL) {
120
	tx = localtime((time_t *) &time);
121
    } else {
122
	tx = gmtime((time_t *) &time);
123
    };
124 125
#else
#endif
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
#ifdef DATEDEBUG
#ifdef HAVE_INT_TIMEZONE
printf( "datetime2tm- (localtime) %d.%02d.%02d %02d:%02d:%02d %s %s dst=%d\n",
 tx->tm_year, tx->tm_mon, tx->tm_mday, tx->tm_hour, tx->tm_min, tx->tm_sec,
 tzname[0], tzname[1], tx->tm_isdst);
#else
printf( "datetime2tm- (localtime) %d.%02d.%02d %02d:%02d:%02d %s dst=%d\n",
 tx->tm_year, tx->tm_mon, tx->tm_mday, tx->tm_hour, tx->tm_min, tx->tm_sec,
 tx->tm_zone, tx->tm_isdst);
#endif
#else
#endif

    tm->tm_year = tx->tm_year+1900;
    tm->tm_mon = tx->tm_mon+1;
    tm->tm_mday = tx->tm_mday;
    tm->tm_hour = tx->tm_hour;
    tm->tm_min = tx->tm_min;
    tm->tm_sec = tx->tm_sec;
    tm->tm_isdst = tx->tm_isdst;

#ifdef USE_POSIX_TIME
#ifdef HAVE_INT_TIMEZONE
    if (tzp != NULL) *tzp = (tm->tm_isdst? (timezone - 3600): timezone);
    if (tzn != NULL) strcpy( tzn, tzname[tm->tm_isdst]);
#else /* !HAVE_INT_TIMEZONE */
153 154 155
    tm->tm_gmtoff = tx->tm_gmtoff;
    tm->tm_zone = tx->tm_zone;

156 157 158 159 160 161 162 163 164
    if (tzp != NULL) *tzp = - tm->tm_gmtoff;	/* tm_gmtoff is Sun/DEC-ism */
    /* XXX FreeBSD man pages indicate that this should work - tgl 97/04/23 */
    if (tzn != NULL) strcpy( tzn, tm->tm_zone);
#endif
#else /* ! USE_POSIX_TIME */
    if (tzp != NULL) *tzp = tb.timezone * 60;
    /* XXX does this work to get the local timezone string in V7? - tgl 97/03/18 */
    if (tzn != NULL) strftime( tzn, MAXTZLEN, "%Z", localtime(&now));
#endif 
165 166

    return;
167
} /* abstime2tm() */
168 169


170 171 172 173
/* tm2abstime()
 * Convert a tm structure to abstime.
 * Note that tm has full year (not 1900-based) and 1-based month.
 */
174
static AbsoluteTime
175 176 177 178 179 180 181 182 183 184 185
tm2abstime( struct tm *tm, int tz)
{
    int day, sec;

    /* validate, before going out of range on some members */
    if (tm->tm_year < 1901 || tm->tm_year > 2038
      || tm->tm_mon < 1 || tm->tm_mon > 12
      || tm->tm_mday < 1 || tm->tm_mday > 31
      || tm->tm_hour < 0 || tm->tm_hour >= 24
      || tm->tm_min < 0 || tm->tm_min > 59
      || tm->tm_sec < 0 || tm->tm_sec > 59)
186
	return(INVALID_ABSTIME);
187 188 189 190 191

    day = (date2j( tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j( 1970, 1, 1));

    /* check for time out of range */
    if ((day < MIN_DAYNUM) || (day > MAX_DAYNUM))
192
	return(INVALID_ABSTIME);
193 194 195 196 197 198 199

    /* convert to seconds */
    sec = tm->tm_sec + tz + (tm->tm_min +(day*24 + tm->tm_hour)*60)*60;

    /* check for overflow */
    if ((day == MAX_DAYNUM && sec < 0) ||
      (day == MIN_DAYNUM && sec > 0))
200
	return(INVALID_ABSTIME);
201 202 203

    /* check for reserved values (e.g. "current" on edge of usual range */
    if (!AbsoluteTimeIsReal(sec))
204
	return(INVALID_ABSTIME);
205

206
    return(sec);
207 208 209
} /* tm2abstime() */


210 211
/* nabstimein()
 * Decode date/time string and return abstime.
212
 */
213 214
AbsoluteTime
nabstimein(char* str)
215
{
216 217
    AbsoluteTime result;

218
    double fsec;
219
    int tz = 0;
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
    struct tm date, *tm = &date;

    char *field[MAXDATEFIELDS];
    char lowstr[MAXDATELEN+1];
    int dtype;
    int nf, ftype[MAXDATEFIELDS];

    if (!PointerIsValid(str))
	elog(WARN,"Bad (null) abstime external representation",NULL);

    if (strlen(str) > MAXDATELEN)
	elog( WARN, "Bad (length) abstime external representation '%s'",str);

    if ((ParseDateTime( str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
     || (DecodeDateTime( field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
	elog( WARN, "Bad abstime external representation '%s'",str);

#ifdef DATEDEBUG
printf( "nabstimein- %d fields are type %d (DTK_DATE=%d)\n", nf, dtype, DTK_DATE);
#endif

    switch (dtype) {
    case DTK_DATE:
243 244
	result = tm2abstime(tm, tz);
	break;
245 246

    case DTK_EPOCH:
247 248
	result = EPOCH_ABSTIME;
	break;
249 250

    case DTK_CURRENT:
251 252
	result = CURRENT_ABSTIME;
	break;
253 254

    case DTK_LATE:
255 256
	result = NOEND_ABSTIME;
	break;
257 258

    case DTK_EARLY:
259 260
	result = NOSTART_ABSTIME;
	break;
261 262

    case DTK_INVALID:
263 264
	result = INVALID_ABSTIME;
	break;
265 266

    default:
267 268
	elog(WARN,"Bad abstime (internal coding error) '%s'",str);
	result = INVALID_ABSTIME;
269
	break;
270 271
    };

272
    return result;
273 274
} /* nabstimein() */

275

276
/* nabstimeout()
277 278 279 280 281 282
 * Given an AbsoluteTime return the English text version of the date
 */
char *
nabstimeout(AbsoluteTime time)
{
    char* result;
283 284 285 286 287
    int tz;
    double fsec = 0;
    struct tm tt, *tm = &tt;
    char buf[MAXDATELEN+1];
    char zone[MAXDATELEN+1], *tzn = zone;
288 289

    switch (time) {
290 291 292 293 294
    case EPOCH_ABSTIME:	  strcpy(buf, EPOCH);	break;
    case INVALID_ABSTIME: strcpy(buf, INVALID);	break;
    case CURRENT_ABSTIME: strcpy(buf, DCURRENT);	break;
    case NOEND_ABSTIME:   strcpy(buf, LATE);	break;
    case NOSTART_ABSTIME: strcpy(buf, EARLY);	break;
295
    default:
296 297 298 299
	abstime2tm( time, &tz, tm, tzn);
#if DATEDEBUG
#endif
	EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf);
300 301
	break;
    }
302

303 304
    result = PALLOC(strlen(buf) + 1);
    strcpy(result, buf);
305

306 307
    return(result);
} /* nabstimeout() */
308 309 310 311


/*
 *  AbsoluteTimeIsBefore -- true iff time1 is before time2.
312
 *  AbsoluteTimeIsBefore -- true iff time1 is after time2.
313 314 315 316 317 318
 */
bool
AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2)
{
    Assert(AbsoluteTimeIsValid(time1));
    Assert(AbsoluteTimeIsValid(time2));
319

320
    if (time1 == CURRENT_ABSTIME)
321 322
	time1 = GetCurrentTransactionStartTime();

323
    if (time2 == CURRENT_ABSTIME)
324 325
	time2 = GetCurrentTransactionStartTime();

326 327 328 329 330 331 332 333
    return (time1 < time2);
}

bool
AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2)
{
    Assert(AbsoluteTimeIsValid(time1));
    Assert(AbsoluteTimeIsValid(time2));
334

335
    if (time1 == CURRENT_ABSTIME)
336 337
	time1 = GetCurrentTransactionStartTime();

338
    if (time2 == CURRENT_ABSTIME)
339 340
	time2 = GetCurrentTransactionStartTime();

341 342 343 344
    return (time1 > time2);
}


345 346 347 348 349 350 351 352 353 354
/* abstime_finite()
 */
bool
abstime_finite(AbsoluteTime abstime)
{
    return((abstime != INVALID_ABSTIME)
      && (abstime != NOSTART_ABSTIME) && (abstime != NOEND_ABSTIME));
} /* abstime_datetime() */


355 356 357 358 359 360 361 362
/*
 *	abstimeeq	- returns 1, iff arguments are equal
 *	abstimene	- returns 1, iff arguments are not equal
 *	abstimelt	- returns 1, iff t1 less than t2
 *	abstimegt	- returns 1, iff t1 greater than t2
 *	abstimele	- returns 1, iff t1 less than or equal to t2
 *	abstimege	- returns 1, iff t1 greater than or equal to t2
 */
363
bool
364 365 366
abstimeeq(AbsoluteTime t1, AbsoluteTime t2)
{
    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
367
	return(FALSE);
368 369 370 371
    if (t1 == CURRENT_ABSTIME)
	t1 = GetCurrentTransactionStartTime();
    if (t2 == CURRENT_ABSTIME)
	t2 = GetCurrentTransactionStartTime();
372

373 374 375
    return(t1 == t2);
}

376
bool
377 378 379
abstimene(AbsoluteTime t1, AbsoluteTime t2)
{ 
    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
380
	return(FALSE);
381 382 383 384
    if (t1 == CURRENT_ABSTIME)
	t1 = GetCurrentTransactionStartTime();
    if (t2 == CURRENT_ABSTIME)
	t2 = GetCurrentTransactionStartTime();
385

386 387 388
    return(t1 != t2);
}

389
bool
390 391 392
abstimelt(AbsoluteTime t1, AbsoluteTime t2)
{ 
    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
393
	return(FALSE);
394 395 396 397
    if (t1 == CURRENT_ABSTIME)
	t1 = GetCurrentTransactionStartTime();
    if (t2 == CURRENT_ABSTIME)
	t2 = GetCurrentTransactionStartTime();
398

399 400 401
    return(t1 < t2);
}

402
bool
403 404 405
abstimegt(AbsoluteTime t1, AbsoluteTime t2)
{ 
    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
406
	return(FALSE);
407 408 409 410
    if (t1 == CURRENT_ABSTIME)
	t1 = GetCurrentTransactionStartTime();
    if (t2 == CURRENT_ABSTIME)
	t2 = GetCurrentTransactionStartTime();
411

412 413 414
    return(t1 > t2);
}

415
bool
416 417 418
abstimele(AbsoluteTime t1, AbsoluteTime t2)
{ 
    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
419
	return(FALSE);
420 421 422 423
    if (t1 == CURRENT_ABSTIME)
	t1 = GetCurrentTransactionStartTime();
    if (t2 == CURRENT_ABSTIME)
	t2 = GetCurrentTransactionStartTime();
424

425 426 427
    return(t1 <= t2);
}

428
bool
429 430 431
abstimege(AbsoluteTime t1, AbsoluteTime t2)
{ 
    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
432
	return(FALSE);
433 434 435 436
    if (t1 == CURRENT_ABSTIME)
	t1 = GetCurrentTransactionStartTime();
    if (t2 == CURRENT_ABSTIME)
	t2 = GetCurrentTransactionStartTime();
437

438 439
    return(t1 >= t2);
}
440

441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
/* datetime_abstime()
 * Convert datetime to abstime.
 */
AbsoluteTime
datetime_abstime(DateTime *datetime)
{
    AbsoluteTime result;

    double fsec;
    struct tm tt, *tm = &tt;

    if (!PointerIsValid(datetime)) {
	result = INVALID_ABSTIME;

    } else if (DATETIME_IS_INVALID(*datetime)) {
	result = INVALID_ABSTIME;

    } else if (DATETIME_IS_NOBEGIN(*datetime)) {
	result = NOSTART_ABSTIME;

    } else if (DATETIME_IS_NOEND(*datetime)) {
	result = NOEND_ABSTIME;

    } else {
	if (DATETIME_IS_RELATIVE(*datetime)) {
467
	    datetime2tm( SetDateTime(*datetime), NULL, tm, &fsec, NULL);
468 469
	    result = tm2abstime( tm, 0);

470
	} else if (datetime2tm( *datetime, NULL, tm, &fsec, NULL) == 0) {
471 472 473 474 475 476 477 478 479
	    result = tm2abstime( tm, 0);

	} else {
	    result = INVALID_ABSTIME;
	};
    };

    return(result);
} /* datetime_abstime() */
480 481 482 483 484 485 486 487 488 489 490 491 492 493

/* abstime_datetime()
 * Convert datetime to abstime.
 */
DateTime *
abstime_datetime(AbsoluteTime abstime)
{
    DateTime *result;

    if (!PointerIsValid(result = PALLOCTYPE(DateTime)))
	elog(WARN,"Unable to allocate space to convert abstime to datetime",NULL);

    switch (abstime) {
    case INVALID_ABSTIME:
494
	DATETIME_INVALID(*result);
495 496 497
	break;

    case NOSTART_ABSTIME:
498
	DATETIME_NOBEGIN(*result);
499 500 501
	break;

    case NOEND_ABSTIME:
502
	DATETIME_NOEND(*result);
503 504 505
	break;

    case EPOCH_ABSTIME:
506
	DATETIME_EPOCH(*result);
507 508 509
	break;

    case CURRENT_ABSTIME:
510
	DATETIME_CURRENT(*result);
511 512 513
	break;

    default:
514
	*result = abstime + ((date2j( 1970, 1, 1) - date2j( 2000, 1, 1))*86400);
515
	break;
516 517 518 519
    };

    return(result);
} /* abstime_datetime() */