1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-12 05:01:15 +03:00
Files
postgres/src/backend/utils/adt/date.c
Thomas G. Lockhart 6456810078 Implement column aliases on views "CREATE VIEW name (collist)".
Implement TIME WITH TIME ZONE type (timetz internal type).
Remap length() for character strings to CHAR_LENGTH() for SQL92
 and to remove the ambiguity with geometric length() functions.
Keep length() for character strings for backward compatibility.
Shrink stored views by removing internal column name list from visible rte.
Implement min(), max() for time and timetz data types.
Implement conversion of TIME to INTERVAL.
Implement abs(), mod(), fac() for the int8 data type.
Rename some math functions to generic names:
 round(), sqrt(), cbrt(), pow(), etc.
Rename NUMERIC power() function to pow().
Fix int2 factorial to calculate result in int4.
Enhance the Oracle compatibility function translate() to work with string
 arguments (from Edwin Ramirez).
Modify pg_proc system table to remove OID holes.
2000-03-14 23:06:59 +00:00

862 lines
18 KiB
C

/*-------------------------------------------------------------------------
*
* date.c
* implements DATE and TIME data types specified in SQL-92 standard
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994-5, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.43 2000/03/14 23:06:35 thomas Exp $
*
*-------------------------------------------------------------------------
*/
#include <limits.h>
#include "postgres.h"
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#include "miscadmin.h"
#include "utils/builtins.h"
static int
date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn);
/*****************************************************************************
* Date ADT
*****************************************************************************/
/* date_in()
* Given date text string, convert to internal date format.
*/
DateADT
date_in(char *str)
{
DateADT date;
double fsec;
struct tm tt,
*tm = &tt;
int tzp;
int dtype;
int nf;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
if (!PointerIsValid(str))
elog(ERROR, "Bad (null) date external representation");
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
elog(ERROR, "Bad date external representation '%s'", str);
switch (dtype)
{
case DTK_DATE:
break;
case DTK_CURRENT:
GetCurrentTime(tm);
break;
case DTK_EPOCH:
tm->tm_year = 1970;
tm->tm_mon = 1;
tm->tm_mday = 1;
break;
default:
elog(ERROR, "Unrecognized date external representation '%s'", str);
}
date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
return date;
} /* date_in() */
/* date_out()
* Given internal format date, convert to text string.
*/
char *
date_out(DateADT date)
{
char *result;
struct tm tt,
*tm = &tt;
char buf[MAXDATELEN + 1];
j2date((date + date2j(2000, 1, 1)),
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
EncodeDateOnly(tm, DateStyle, buf);
result = palloc(strlen(buf) + 1);
strcpy(result, buf);
return result;
} /* date_out() */
bool
date_eq(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 == dateVal2;
}
bool
date_ne(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 != dateVal2;
}
bool
date_lt(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 < dateVal2;
} /* date_lt() */
bool
date_le(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 <= dateVal2;
} /* date_le() */
bool
date_gt(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 > dateVal2;
} /* date_gt() */
bool
date_ge(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 >= dateVal2;
} /* date_ge() */
int
date_cmp(DateADT dateVal1, DateADT dateVal2)
{
if (dateVal1 < dateVal2)
return -1;
else if (dateVal1 > dateVal2)
return 1;
return 0;
} /* date_cmp() */
DateADT
date_larger(DateADT dateVal1, DateADT dateVal2)
{
return date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
} /* date_larger() */
DateADT
date_smaller(DateADT dateVal1, DateADT dateVal2)
{
return date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
} /* date_smaller() */
/* Compute difference between two dates in days.
*/
int4
date_mi(DateADT dateVal1, DateADT dateVal2)
{
return dateVal1 - dateVal2;
} /* date_mi() */
/* Add a number of days to a date, giving a new date.
* Must handle both positive and negative numbers of days.
*/
DateADT
date_pli(DateADT dateVal, int4 days)
{
return dateVal + days;
} /* date_pli() */
/* Subtract a number of days from a date, giving a new date.
*/
DateADT
date_mii(DateADT dateVal, int4 days)
{
return date_pli(dateVal, -days);
} /* date_mii() */
/* date_timestamp()
* Convert date to timestamp data type.
*/
Timestamp *
date_timestamp(DateADT dateVal)
{
Timestamp *result;
struct tm tt,
*tm = &tt;
int tz;
double fsec = 0;
char *tzn;
result = palloc(sizeof(*result));
if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert date to timestamp");
if (tm2timestamp(tm, fsec, &tz, result) != 0)
elog(ERROR, "Timestamp out of range");
return result;
} /* date_timestamp() */
/* timestamp_date()
* Convert timestamp to date data type.
*/
DateADT
timestamp_date(Timestamp *timestamp)
{
DateADT result;
struct tm tt,
*tm = &tt;
int tz;
double fsec;
char *tzn;
if (!PointerIsValid(timestamp))
elog(ERROR, "Unable to convert null timestamp to date");
if (TIMESTAMP_NOT_FINITE(*timestamp))
elog(ERROR, "Unable to convert timestamp to date");
if (TIMESTAMP_IS_EPOCH(*timestamp))
{
timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL);
}
else if (TIMESTAMP_IS_CURRENT(*timestamp))
{
timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn);
}
else
{
if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert timestamp to date");
}
result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
return result;
} /* timestamp_date() */
/* abstime_date()
* Convert abstime to date data type.
*/
DateADT
abstime_date(AbsoluteTime abstime)
{
DateADT result;
struct tm tt,
*tm = &tt;
int tz;
switch (abstime)
{
case INVALID_ABSTIME:
case NOSTART_ABSTIME:
case NOEND_ABSTIME:
elog(ERROR, "Unable to convert reserved abstime value to date");
/*
* pretend to drop through to make compiler think that result
* will be set
*/
case EPOCH_ABSTIME:
result = date2j(1970, 1, 1) - date2j(2000, 1, 1);
break;
case CURRENT_ABSTIME:
GetCurrentTime(tm);
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
break;
default:
abstime2tm(abstime, &tz, tm, NULL);
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
break;
}
return result;
} /* abstime_date() */
/* date2tm()
* Convert date to time structure.
* Note that date is an implicit local time, but the system calls assume
* that everything is GMT. So, convert to GMT, rotate to local time,
* and then convert again to try to get the time zones correct.
*/
static int
date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn)
{
struct tm *tx;
time_t utime;
*fsec = 0;
j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1;
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
{
/* convert to system time */
utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400);
/* rotate to noon to get the right day in time zone */
utime += (12 * 60 * 60);
#ifdef USE_POSIX_TIME
tx = localtime(&utime);
tm->tm_year = tx->tm_year + 1900;
tm->tm_mon = tx->tm_mon + 1;
tm->tm_mday = tx->tm_mday;
tm->tm_isdst = tx->tm_isdst;
#if defined(HAVE_TM_ZONE)
tm->tm_gmtoff = tx->tm_gmtoff;
tm->tm_zone = tx->tm_zone;
/* tm_gmtoff is Sun/DEC-ism */
*tzp = -(tm->tm_gmtoff);
if (tzn != NULL)
*tzn = (char *) tm->tm_zone;
#elif defined(HAVE_INT_TIMEZONE)
#ifdef __CYGWIN__
*tzp = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
#else
*tzp = (tm->tm_isdst ? (timezone - 3600) : timezone);
#endif
if (tzn != NULL)
*tzn = tzname[(tm->tm_isdst > 0)];
#else
#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
#endif
#else /* !USE_POSIX_TIME */
*tzp = CTimeZone; /* V7 conventions; don't know timezone? */
if (tzn != NULL)
*tzn = CTZName;
#endif
/* otherwise, outside of timezone range so convert to GMT... */
}
else
{
*tzp = 0;
tm->tm_isdst = 0;
if (tzn != NULL)
*tzn = NULL;
}
return 0;
} /* date2tm() */
/*****************************************************************************
* Time ADT
*****************************************************************************/
TimeADT *
time_in(char *str)
{
TimeADT *time;
double fsec;
struct tm tt,
*tm = &tt;
int nf;
char lowstr[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
if (!PointerIsValid(str))
elog(ERROR, "Bad (null) time external representation");
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, NULL) != 0))
elog(ERROR, "Bad time external representation '%s'", str);
time = palloc(sizeof(*time));
*time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
return time;
} /* time_in() */
char *
time_out(TimeADT *time)
{
char *result;
struct tm tt,
*tm = &tt;
double fsec;
char buf[MAXDATELEN + 1];
if (!PointerIsValid(time))
return NULL;
tm->tm_hour = (*time / (60 * 60));
tm->tm_min = (((int) (*time / 60)) % 60);
tm->tm_sec = (((int) *time) % 60);
fsec = 0;
EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
result = palloc(strlen(buf) + 1);
strcpy(result, buf);
return result;
} /* time_out() */
bool
time_eq(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return *time1 == *time2;
} /* time_eq() */
bool
time_ne(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return *time1 != *time2;
} /* time_eq() */
bool
time_lt(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return (*time1 < *time2);
} /* time_lt() */
bool
time_le(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return (*time1 <= *time2);
} /* time_le() */
bool
time_gt(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return (*time1 > *time2);
} /* time_gt() */
bool
time_ge(TimeADT *time1, TimeADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return (*time1 >= *time2);
} /* time_ge() */
int
time_cmp(TimeADT *time1, TimeADT *time2)
{
return (*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0));
} /* time_cmp() */
TimeADT *
time_larger(TimeADT *time1, TimeADT *time2)
{
return time_gt(time1, time2)? time1: time2;
} /* time_larger() */
TimeADT *
time_smaller(TimeADT *time1, TimeADT *time2)
{
return time_lt(time1, time2)? time1: time2;
} /* time_smaller() */
/* overlaps_time()
* Implements the SQL92 OVERLAPS operator.
* Algorithm from Date and Darwen, 1997
*/
bool
overlaps_time(TimeADT *ts1, TimeADT *te1, TimeADT *ts2, TimeADT *te2)
{
/* Make sure we have ordered pairs... */
if (time_gt(ts1, te1))
{
TimeADT *tt = ts1;
ts1 = te1;
te1 = tt;
}
if (time_gt(ts2, te2))
{
TimeADT *tt = ts2;
ts2 = te2;
te2 = tt;
}
return ((time_gt(ts1, ts2) && (time_lt(ts1, te2) || time_lt(te1, te2)))
|| (time_gt(ts2, ts1) && (time_lt(ts2, te1) || time_lt(te2, te1)))
|| time_eq(ts1, ts2));
}
/* timestamp_time()
* Convert timestamp to time data type.
*/
TimeADT *
timestamp_time(Timestamp *timestamp)
{
TimeADT *result;
struct tm tt,
*tm = &tt;
int tz;
double fsec;
char *tzn;
if (!PointerIsValid(timestamp))
elog(ERROR, "Unable to convert null timestamp to date");
if (TIMESTAMP_NOT_FINITE(*timestamp))
elog(ERROR, "Unable to convert timestamp to date");
if (TIMESTAMP_IS_EPOCH(*timestamp))
{
timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL);
}
else if (TIMESTAMP_IS_CURRENT(*timestamp))
{
timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn);
}
else
{
if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert timestamp to date");
}
result = palloc(sizeof(*result));
*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
return result;
} /* timestamp_time() */
/* datetime_timestamp()
* Convert date and time to timestamp data type.
*/
Timestamp *
datetime_timestamp(DateADT date, TimeADT *time)
{
Timestamp *result;
if (!PointerIsValid(time))
{
result = palloc(sizeof(*result));
TIMESTAMP_INVALID(*result);
}
else
{
result = date_timestamp(date);
*result += *time;
}
return result;
} /* datetime_timestamp() */
/* time_interval()
* Convert time to interval data type.
*/
Interval *
time_interval(TimeADT *time)
{
Interval *result;
if (!PointerIsValid(time))
return NULL;
result = palloc(sizeof(*result));
result->time = *time;
result->month = 0;
return result;
} /* time_interval() */
/*****************************************************************************
* Time With Time Zone ADT
*****************************************************************************/
TimeTzADT *
timetz_in(char *str)
{
TimeTzADT *time;
double fsec;
struct tm tt,
*tm = &tt;
int tz;
int nf;
char lowstr[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
if (!PointerIsValid(str))
elog(ERROR, "Bad (null) time external representation");
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
elog(ERROR, "Bad time external representation '%s'", str);
time = palloc(sizeof(*time));
time->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
time->zone = tz;
return time;
} /* timetz_in() */
char *
timetz_out(TimeTzADT *time)
{
char *result;
struct tm tt,
*tm = &tt;
double fsec;
int tz;
char buf[MAXDATELEN + 1];
if (!PointerIsValid(time))
return NULL;
tm->tm_hour = (time->time / (60 * 60));
tm->tm_min = (((int) (time->time / 60)) % 60);
tm->tm_sec = (((int) time->time) % 60);
fsec = 0;
tz = time->zone;
EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
result = palloc(strlen(buf) + 1);
strcpy(result, buf);
return result;
} /* timetz_out() */
bool
timetz_eq(TimeTzADT *time1, TimeTzADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return ((time1->time + time1->zone) == (time2->time + time2->zone));
} /* timetz_eq() */
bool
timetz_ne(TimeTzADT *time1, TimeTzADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return ((time1->time + time1->zone) != (time2->time + time2->zone));
} /* timetz_ne() */
bool
timetz_lt(TimeTzADT *time1, TimeTzADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return ((time1->time + time1->zone) < (time2->time + time2->zone));
} /* timetz_lt() */
bool
timetz_le(TimeTzADT *time1, TimeTzADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return ((time1->time + time1->zone) <= (time2->time + time2->zone));
} /* timetz_le() */
bool
timetz_gt(TimeTzADT *time1, TimeTzADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return ((time1->time + time1->zone) > (time2->time + time2->zone));
} /* timetz_gt() */
bool
timetz_ge(TimeTzADT *time1, TimeTzADT *time2)
{
if (!PointerIsValid(time1) || !PointerIsValid(time2))
return FALSE;
return ((time1->time + time1->zone) >= (time2->time + time2->zone));
} /* timetz_ge() */
int
timetz_cmp(TimeTzADT *time1, TimeTzADT *time2)
{
return (timetz_lt(time1, time2) ? -1 : (timetz_gt(time1, time2)? 1: 0));
} /* timetz_cmp() */
TimeTzADT *
timetz_larger(TimeTzADT *time1, TimeTzADT *time2)
{
return timetz_gt(time1, time2)? time1: time2;
} /* timetz_larger() */
TimeTzADT *
timetz_smaller(TimeTzADT *time1, TimeTzADT *time2)
{
return timetz_lt(time1, time2)? time1: time2;
} /* timetz_smaller() */
/* overlaps_timetz()
* Implements the SQL92 OVERLAPS operator.
* Algorithm from Date and Darwen, 1997
*/
bool
overlaps_timetz(TimeTzADT *ts1, TimeTzADT *te1, TimeTzADT *ts2, TimeTzADT *te2)
{
/* Make sure we have ordered pairs... */
if (timetz_gt(ts1, te1))
{
TimeTzADT *tt = ts1;
ts1 = te1;
te1 = tt;
}
if (timetz_gt(ts2, te2))
{
TimeTzADT *tt = ts2;
ts2 = te2;
te2 = tt;
}
return ((timetz_gt(ts1, ts2) && (timetz_lt(ts1, te2) || timetz_lt(te1, te2)))
|| (timetz_gt(ts2, ts1) && (timetz_lt(ts2, te1) || timetz_lt(te2, te1)))
|| timetz_eq(ts1, ts2));
} /* overlaps_timetz() */
/* timestamp_timetz()
* Convert timestamp to timetz data type.
*/
TimeTzADT *
timestamp_timetz(Timestamp *timestamp)
{
TimeTzADT *result;
struct tm tt,
*tm = &tt;
int tz;
double fsec;
char *tzn;
if (!PointerIsValid(timestamp))
elog(ERROR, "Unable to convert null timestamp to date");
if (TIMESTAMP_NOT_FINITE(*timestamp))
elog(ERROR, "Unable to convert timestamp to date");
if (TIMESTAMP_IS_EPOCH(*timestamp))
{
timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL);
tz = 0;
}
else if (TIMESTAMP_IS_CURRENT(*timestamp))
{
timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn);
}
else
{
if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert timestamp to date");
}
result = palloc(sizeof(*result));
result->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
result->zone = tz;
return result;
} /* timestamp_timetz() */
/* datetimetz_timestamp()
* Convert date and timetz to timestamp data type.
* Timestamp is stored in GMT, so add the time zone
* stored with the timetz to the result.
* - thomas 2000-03-10
*/
Timestamp *
datetimetz_timestamp(DateADT date, TimeTzADT *time)
{
Timestamp *result;
struct tm tt,
*tm = &tt;
int tz;
double fsec = 0;
char *tzn;
result = palloc(sizeof(*result));
if (!PointerIsValid(date) || !PointerIsValid(time))
{
TIMESTAMP_INVALID(*result);
}
else
{
if (date2tm(date, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "Unable to convert date to timestamp");
if (tm2timestamp(tm, fsec, &time->zone, result) != 0)
elog(ERROR, "Timestamp out of range");
*result += time->time;
}
return result;
} /* datetimetz_timestamp() */