2008-05-17 03:28:26 +02:00
|
|
|
/*
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/interfaces/ecpg/pgtypeslib/timestamp.c
|
2008-05-17 03:28:26 +02:00
|
|
|
*/
|
2003-07-02 09:57:36 +02:00
|
|
|
#include "postgres_fe.h"
|
2005-10-10 03:34:00 +02:00
|
|
|
|
2003-03-20 16:56:50 +01:00
|
|
|
#include <time.h>
|
|
|
|
#include <float.h>
|
2005-10-10 03:34:00 +02:00
|
|
|
#include <limits.h>
|
2003-07-02 09:57:36 +02:00
|
|
|
#include <math.h>
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
#ifdef __FAST_MATH__
|
|
|
|
#error -ffast-math is known to break this code
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "extern.h"
|
2003-06-25 19:55:50 +02:00
|
|
|
#include "dt.h"
|
2003-03-20 16:56:50 +01:00
|
|
|
#include "pgtypes_timestamp.h"
|
2003-04-01 16:37:25 +02:00
|
|
|
#include "pgtypes_date.h"
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2004-12-23 11:46:10 +01:00
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
int PGTYPEStimestamp_defmt_scan(char **, char *, timestamp *, int *, int *, int *,
|
2003-08-04 02:43:34 +02:00
|
|
|
int *, int *, int *, int *);
|
2003-08-01 10:21:04 +02:00
|
|
|
|
2003-03-20 16:56:50 +01:00
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
|
|
|
static int64
|
|
|
|
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
|
|
|
|
{
|
2005-07-21 20:06:13 +02:00
|
|
|
return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
|
2003-04-01 16:37:25 +02:00
|
|
|
} /* time2t() */
|
2003-03-20 16:56:50 +01:00
|
|
|
#else
|
|
|
|
static double
|
|
|
|
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
|
|
|
|
{
|
2005-07-21 20:06:13 +02:00
|
|
|
return (((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec + fsec;
|
2003-04-01 16:37:25 +02:00
|
|
|
} /* time2t() */
|
2003-03-20 16:56:50 +01:00
|
|
|
#endif
|
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
static timestamp
|
|
|
|
dt2local(timestamp dt, int tz)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
2005-05-23 23:54:02 +02:00
|
|
|
dt -= (tz * USECS_PER_SEC);
|
2003-03-20 16:56:50 +01:00
|
|
|
#else
|
2003-04-01 16:37:25 +02:00
|
|
|
dt -= tz;
|
2003-03-20 16:56:50 +01:00
|
|
|
#endif
|
2003-04-01 16:37:25 +02:00
|
|
|
return dt;
|
|
|
|
} /* dt2local() */
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
/* tm2timestamp()
|
|
|
|
* Convert a tm structure to a timestamp data type.
|
|
|
|
* Note that year is _not_ 1900-based, but is an explicit full value.
|
|
|
|
* Also, month is one-based, _not_ zero-based.
|
2003-07-04 20:21:14 +02:00
|
|
|
*
|
|
|
|
* Returns -1 on failure (overflow).
|
2003-03-20 16:56:50 +01:00
|
|
|
*/
|
2003-08-01 10:21:04 +02:00
|
|
|
int
|
2005-10-15 04:49:52 +02:00
|
|
|
tm2timestamp(struct tm * tm, fsec_t fsec, int *tzp, timestamp * result)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
2004-08-29 07:07:03 +02:00
|
|
|
int dDate;
|
2003-03-20 16:56:50 +01:00
|
|
|
int64 time;
|
|
|
|
#else
|
2004-08-29 07:07:03 +02:00
|
|
|
double dDate,
|
|
|
|
time;
|
2003-03-20 16:56:50 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Julian day routines are not correct for negative Julian days */
|
|
|
|
if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
|
|
|
|
return -1;
|
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
|
2003-03-20 16:56:50 +01:00
|
|
|
time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
2005-05-23 20:56:55 +02:00
|
|
|
*result = (dDate * USECS_PER_DAY) + time;
|
2003-07-04 20:21:14 +02:00
|
|
|
/* check for major overflow */
|
2005-05-23 20:56:55 +02:00
|
|
|
if ((*result - time) / USECS_PER_DAY != dDate)
|
2003-07-04 20:21:14 +02:00
|
|
|
return -1;
|
|
|
|
/* check for just-barely overflow (okay except time-of-day wraps) */
|
2005-05-24 04:09:45 +02:00
|
|
|
if ((*result < 0 && dDate >= 0) ||
|
|
|
|
(*result >= 0 && dDate < 0))
|
2003-07-04 20:21:14 +02:00
|
|
|
return -1;
|
2003-03-20 16:56:50 +01:00
|
|
|
#else
|
2005-05-24 04:09:45 +02:00
|
|
|
*result = dDate * SECS_PER_DAY + time;
|
2003-03-20 16:56:50 +01:00
|
|
|
#endif
|
|
|
|
if (tzp != NULL)
|
|
|
|
*result = dt2local(*result, -(*tzp));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} /* tm2timestamp() */
|
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
static timestamp
|
2003-03-20 16:56:50 +01:00
|
|
|
SetEpochTimestamp(void)
|
|
|
|
{
|
2009-02-04 09:51:10 +01:00
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
|
|
|
int64 noresult = 0;
|
|
|
|
#else
|
|
|
|
double noresult = 0.0;
|
|
|
|
#endif
|
2003-09-09 12:46:42 +02:00
|
|
|
timestamp dt;
|
2003-08-04 02:43:34 +02:00
|
|
|
struct tm tt,
|
|
|
|
*tm = &tt;
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2009-02-04 09:51:10 +01:00
|
|
|
if (GetEpochTime(tm) < 0)
|
|
|
|
return noresult;
|
|
|
|
|
2003-03-20 16:56:50 +01:00
|
|
|
tm2timestamp(tm, 0, NULL, &dt);
|
2003-04-01 16:37:25 +02:00
|
|
|
return dt;
|
|
|
|
} /* SetEpochTimestamp() */
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
/* timestamp2tm()
|
|
|
|
* Convert timestamp data type to POSIX time structure.
|
|
|
|
* Note that year is _not_ 1900-based, but is an explicit full value.
|
|
|
|
* Also, month is one-based, _not_ zero-based.
|
|
|
|
* Returns:
|
|
|
|
* 0 on success
|
|
|
|
* -1 on out of range
|
|
|
|
*
|
|
|
|
* For dates within the system-supported time_t range, convert to the
|
|
|
|
* local time zone. If out of this range, leave as GMT. - tgl 97/05/27
|
|
|
|
*/
|
|
|
|
static int
|
2005-10-15 04:49:52 +02:00
|
|
|
timestamp2tm(timestamp dt, int *tzp, struct tm * tm, fsec_t *fsec, char **tzn)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
2005-10-09 19:21:47 +02:00
|
|
|
int64 dDate,
|
2004-08-29 07:07:03 +02:00
|
|
|
date0;
|
2003-03-20 16:56:50 +01:00
|
|
|
int64 time;
|
|
|
|
#else
|
2004-08-29 07:07:03 +02:00
|
|
|
double dDate,
|
|
|
|
date0;
|
2003-03-20 16:56:50 +01:00
|
|
|
double time;
|
|
|
|
#endif
|
|
|
|
time_t utime;
|
|
|
|
|
|
|
|
#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
|
|
|
|
struct tm *tx;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
date0 = date2j(2000, 1, 1);
|
|
|
|
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
2005-10-09 19:21:47 +02:00
|
|
|
time = dt;
|
2005-05-23 20:56:55 +02:00
|
|
|
TMODULO(time, dDate, USECS_PER_DAY);
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
if (time < INT64CONST(0))
|
|
|
|
{
|
2005-05-23 20:56:55 +02:00
|
|
|
time += USECS_PER_DAY;
|
2003-09-09 12:46:42 +02:00
|
|
|
dDate -= 1;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
2005-10-09 19:21:47 +02:00
|
|
|
|
|
|
|
/* add offset to go from J2000 back to standard Julian date */
|
|
|
|
dDate += date0;
|
|
|
|
|
|
|
|
/* Julian day routine does not work for negative Julian days */
|
|
|
|
if (dDate < 0 || dDate > (timestamp) INT_MAX)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
|
|
|
|
dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
|
2003-03-20 16:56:50 +01:00
|
|
|
#else
|
2005-10-09 19:21:47 +02:00
|
|
|
time = dt;
|
2005-10-15 04:49:52 +02:00
|
|
|
TMODULO(time, dDate, (double) SECS_PER_DAY);
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
if (time < 0)
|
|
|
|
{
|
2005-05-23 23:54:02 +02:00
|
|
|
time += SECS_PER_DAY;
|
2003-09-09 12:46:42 +02:00
|
|
|
dDate -= 1;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* add offset to go from J2000 back to standard Julian date */
|
2003-09-09 12:46:42 +02:00
|
|
|
dDate += date0;
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2005-10-09 19:21:47 +02:00
|
|
|
recalc_d:
|
|
|
|
/* Julian day routine does not work for negative Julian days */
|
|
|
|
if (dDate < 0 || dDate > (timestamp) INT_MAX)
|
|
|
|
return -1;
|
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
|
2005-10-09 19:21:47 +02:00
|
|
|
recalc_t:
|
2003-03-20 16:56:50 +01:00
|
|
|
dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
|
|
|
|
|
2005-10-09 19:21:47 +02:00
|
|
|
*fsec = TSROUND(*fsec);
|
|
|
|
/* roundoff may need to propagate to higher-order fields */
|
|
|
|
if (*fsec >= 1.0)
|
|
|
|
{
|
|
|
|
time = ceil(time);
|
2005-10-15 04:49:52 +02:00
|
|
|
if (time >= (double) SECS_PER_DAY)
|
2005-10-09 19:21:47 +02:00
|
|
|
{
|
|
|
|
time = 0;
|
|
|
|
dDate += 1;
|
|
|
|
goto recalc_d;
|
|
|
|
}
|
|
|
|
goto recalc_t;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-03-20 16:56:50 +01:00
|
|
|
if (tzp != NULL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Does this fall within the capabilities of the localtime()
|
|
|
|
* interface? Then use this to rotate to the local time zone.
|
|
|
|
*/
|
|
|
|
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
|
|
|
|
{
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
2005-05-24 04:09:45 +02:00
|
|
|
utime = dt / USECS_PER_SEC +
|
|
|
|
((date0 - date2j(1970, 1, 1)) * INT64CONST(86400));
|
2003-03-20 16:56:50 +01:00
|
|
|
#else
|
2005-05-24 04:09:45 +02:00
|
|
|
utime = dt + (date0 - date2j(1970, 1, 1)) * SECS_PER_DAY;
|
2003-03-20 16:56:50 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
|
|
|
|
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_hour = tx->tm_hour;
|
|
|
|
tm->tm_min = tx->tm_min;
|
|
|
|
tm->tm_isdst = tx->tm_isdst;
|
|
|
|
|
|
|
|
#if defined(HAVE_TM_ZONE)
|
|
|
|
tm->tm_gmtoff = tx->tm_gmtoff;
|
|
|
|
tm->tm_zone = tx->tm_zone;
|
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
*tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
|
2003-03-20 16:56:50 +01:00
|
|
|
if (tzn != NULL)
|
|
|
|
*tzn = (char *) tm->tm_zone;
|
|
|
|
#elif defined(HAVE_INT_TIMEZONE)
|
2005-07-21 05:56:25 +02:00
|
|
|
*tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
|
2003-03-20 16:56:50 +01:00
|
|
|
if (tzn != NULL)
|
2004-09-09 02:24:12 +02:00
|
|
|
*tzn = TZNAME_GLOBAL[(tm->tm_isdst > 0)];
|
2003-03-20 16:56:50 +01:00
|
|
|
#endif
|
|
|
|
#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
|
|
|
|
*tzp = 0;
|
|
|
|
/* Mark this as *no* time zone available */
|
|
|
|
tm->tm_isdst = -1;
|
|
|
|
if (tzn != NULL)
|
|
|
|
*tzn = NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*tzp = 0;
|
|
|
|
/* Mark this as *no* time zone available */
|
|
|
|
tm->tm_isdst = -1;
|
|
|
|
if (tzn != NULL)
|
|
|
|
*tzn = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tm->tm_isdst = -1;
|
|
|
|
if (tzn != NULL)
|
|
|
|
*tzn = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} /* timestamp2tm() */
|
|
|
|
|
|
|
|
/* EncodeSpecialTimestamp()
|
2003-08-04 02:43:34 +02:00
|
|
|
* * Convert reserved timestamp data type to string.
|
|
|
|
* */
|
2003-03-20 16:56:50 +01:00
|
|
|
static int
|
2003-09-09 12:46:42 +02:00
|
|
|
EncodeSpecialTimestamp(timestamp dt, char *str)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
if (TIMESTAMP_IS_NOBEGIN(dt))
|
|
|
|
strcpy(str, EARLY);
|
|
|
|
else if (TIMESTAMP_IS_NOEND(dt))
|
|
|
|
strcpy(str, LATE);
|
|
|
|
else
|
|
|
|
return FALSE;
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2003-04-01 16:37:25 +02:00
|
|
|
return TRUE;
|
|
|
|
} /* EncodeSpecialTimestamp() */
|
2003-03-20 16:56:50 +01:00
|
|
|
|
2003-09-09 12:46:42 +02:00
|
|
|
timestamp
|
2003-04-01 16:37:25 +02:00
|
|
|
PGTYPEStimestamp_from_asc(char *str, char **endptr)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-09-09 12:46:42 +02:00
|
|
|
timestamp result;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-03-20 16:56:50 +01:00
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
|
|
|
int64 noresult = 0;
|
|
|
|
#else
|
|
|
|
double noresult = 0.0;
|
|
|
|
#endif
|
|
|
|
fsec_t fsec;
|
2003-08-04 02:43:34 +02:00
|
|
|
struct tm tt,
|
|
|
|
*tm = &tt;
|
|
|
|
int dtype;
|
|
|
|
int nf;
|
|
|
|
char *field[MAXDATEFIELDS];
|
|
|
|
int ftype[MAXDATEFIELDS];
|
2003-03-20 16:56:50 +01:00
|
|
|
char lowstr[MAXDATELEN + MAXDATEFIELDS];
|
2003-08-04 02:43:34 +02:00
|
|
|
char *realptr;
|
|
|
|
char **ptr = (endptr != NULL) ? endptr : &realptr;
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
if (strlen(str) >= sizeof(lowstr))
|
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_TS_BAD_TIMESTAMP;
|
2003-03-20 16:56:50 +01:00
|
|
|
return (noresult);
|
|
|
|
}
|
|
|
|
|
2009-05-20 18:13:18 +02:00
|
|
|
if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
|
2007-08-14 12:01:54 +02:00
|
|
|
DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, 0) != 0)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
errno = PGTYPES_TS_BAD_TIMESTAMP;
|
|
|
|
return (noresult);
|
|
|
|
}
|
|
|
|
|
2003-03-20 16:56:50 +01:00
|
|
|
switch (dtype)
|
|
|
|
{
|
|
|
|
case DTK_DATE:
|
|
|
|
if (tm2timestamp(tm, fsec, NULL, &result) != 0)
|
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_TS_BAD_TIMESTAMP;
|
2003-04-01 16:37:25 +02:00
|
|
|
return (noresult);
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DTK_EPOCH:
|
|
|
|
result = SetEpochTimestamp();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DTK_LATE:
|
|
|
|
TIMESTAMP_NOEND(result);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DTK_EARLY:
|
|
|
|
TIMESTAMP_NOBEGIN(result);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DTK_INVALID:
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_TS_BAD_TIMESTAMP;
|
2003-03-20 16:56:50 +01:00
|
|
|
return (noresult);
|
|
|
|
|
|
|
|
default:
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_TS_BAD_TIMESTAMP;
|
2003-03-20 16:56:50 +01:00
|
|
|
return (noresult);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* AdjustTimestampForTypmod(&result, typmod); */
|
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Since it's difficult to test for noresult, make sure errno is 0 if no
|
|
|
|
* error occured.
|
2004-08-29 07:07:03 +02:00
|
|
|
*/
|
2004-02-24 17:07:49 +01:00
|
|
|
errno = 0;
|
2003-03-20 16:56:50 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
2003-09-09 12:46:42 +02:00
|
|
|
PGTYPEStimestamp_to_asc(timestamp tstamp)
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
struct tm tt,
|
|
|
|
*tm = &tt;
|
2003-04-01 16:37:25 +02:00
|
|
|
char buf[MAXDATELEN + 1];
|
2003-08-04 02:43:34 +02:00
|
|
|
char *tzn = NULL;
|
2003-04-01 16:37:25 +02:00
|
|
|
fsec_t fsec;
|
2005-10-15 04:49:52 +02:00
|
|
|
int DateStyle = 1; /* this defaults to ISO_DATES, shall we make
|
|
|
|
* it an option? */
|
2003-03-20 16:56:50 +01:00
|
|
|
|
|
|
|
if (TIMESTAMP_NOT_FINITE(tstamp))
|
2003-04-01 16:37:25 +02:00
|
|
|
EncodeSpecialTimestamp(tstamp, buf);
|
|
|
|
else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
|
|
|
|
EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf, 0);
|
|
|
|
else
|
2003-03-20 16:56:50 +01:00
|
|
|
{
|
2003-03-30 13:48:19 +02:00
|
|
|
errno = PGTYPES_TS_BAD_TIMESTAMP;
|
2003-03-20 16:56:50 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2003-04-01 16:37:25 +02:00
|
|
|
return pgtypes_strdup(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2005-10-15 04:49:52 +02:00
|
|
|
PGTYPEStimestamp_current(timestamp * ts)
|
2003-04-01 16:37:25 +02:00
|
|
|
{
|
|
|
|
struct tm tm;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-04-01 16:37:25 +02:00
|
|
|
GetCurrentDateTime(&tm);
|
2009-02-04 09:51:10 +01:00
|
|
|
if (errno == 0)
|
|
|
|
tm2timestamp(&tm, 0, NULL, ts);
|
2003-04-01 16:37:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-10-15 04:49:52 +02:00
|
|
|
dttofmtasc_replace(timestamp * ts, date dDate, int dow, struct tm * tm,
|
2003-08-04 02:43:34 +02:00
|
|
|
char *output, int *pstr_len, char *fmtstr)
|
2003-04-01 16:37:25 +02:00
|
|
|
{
|
2003-08-01 10:21:04 +02:00
|
|
|
union un_fmt_comb replace_val;
|
2003-08-04 02:43:34 +02:00
|
|
|
int replace_type;
|
|
|
|
int i;
|
|
|
|
char *p = fmtstr;
|
|
|
|
char *q = output;
|
2003-04-01 16:37:25 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
while (*p)
|
|
|
|
{
|
|
|
|
if (*p == '%')
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
p++;
|
|
|
|
/* fix compiler warning */
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-08-04 02:43:34 +02:00
|
|
|
switch (*p)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the abbreviated name of the day in the week */
|
|
|
|
/* XXX should be locale aware */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'a':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = pgtypes_date_weekdays_short[dow];
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the full name of the day in the week */
|
|
|
|
/* XXX should be locale aware */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'A':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = days[dow];
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the abbreviated name of the month */
|
|
|
|
/* XXX should be locale aware */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'b':
|
|
|
|
case 'h':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = months[tm->tm_mon];
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the full name name of the month */
|
|
|
|
/* XXX should be locale aware */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'B':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = pgtypes_date_months[tm->tm_mon];
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The preferred date and time representation for
|
|
|
|
* the current locale.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'c':
|
|
|
|
/* XXX */
|
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the century number with leading zeroes */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'C':
|
2003-09-01 14:37:40 +02:00
|
|
|
replace_val.uint_val = tm->tm_year / 100;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* day with leading zeroes (01 - 31) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'd':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_mday;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the date in the format mm/dd/yy */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'D':
|
2003-08-04 02:43:34 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* ts, dDate, dow, tm is information about the timestamp
|
2003-04-01 16:37:25 +02:00
|
|
|
*
|
2003-08-04 02:43:34 +02:00
|
|
|
* q is the start of the current output buffer
|
2003-04-01 16:37:25 +02:00
|
|
|
*
|
2003-08-04 02:43:34 +02:00
|
|
|
* pstr_len is a pointer to the remaining size of output,
|
|
|
|
* i.e. the size of q
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
i = dttofmtasc_replace(ts, dDate, dow, tm,
|
2003-08-04 02:43:34 +02:00
|
|
|
q, pstr_len,
|
|
|
|
"%m/%d/%y");
|
|
|
|
if (i)
|
|
|
|
return i;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* day with leading spaces (01 - 31) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'e':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_mday;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LS;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* alternative format modifier
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'E':
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
char tmp[4] = "%Ex";
|
|
|
|
|
2003-04-01 16:37:25 +02:00
|
|
|
p++;
|
2003-08-04 02:43:34 +02:00
|
|
|
if (*p == '\0')
|
2003-04-01 16:37:25 +02:00
|
|
|
return -1;
|
|
|
|
tmp[2] = *p;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
/*
|
|
|
|
* strftime's month is 0 based, ours is 1 based
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, tmp, tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The ISO 8601 year with century as a decimal number. The
|
|
|
|
* 4-digit year corresponding to the ISO week number.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'G':
|
2003-08-04 02:43:34 +02:00
|
|
|
{
|
2011-04-29 01:58:49 +02:00
|
|
|
/* Keep compiler quiet - Don't use a literal format */
|
2011-06-09 20:32:50 +02:00
|
|
|
const char *fmt = "%G";
|
2011-04-29 01:58:49 +02:00
|
|
|
|
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, fmt, tm);
|
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
}
|
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Like %G, but without century, i.e., with a 2-digit year
|
|
|
|
* (00-99).
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'g':
|
2003-06-25 12:44:21 +02:00
|
|
|
{
|
2007-03-17 20:25:24 +01:00
|
|
|
const char *fmt = "%g"; /* Keep compiler quiet about
|
2005-10-15 04:49:52 +02:00
|
|
|
* 2-digit year */
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 12:44:21 +02:00
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, fmt, tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-06-25 12:44:21 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
}
|
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* hour (24 hour clock) with leading zeroes */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'H':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_hour;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* hour (12 hour clock) with leading zeroes */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'I':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_hour % 12;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The day of the year as a decimal number with leading
|
|
|
|
* zeroes. It ranges from 001 to 366.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'j':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_yday;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_3_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The hour (24 hour clock). Leading zeroes will be turned
|
|
|
|
* into spaces.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'k':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_hour;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LS;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The hour (12 hour clock). Leading zeroes will be turned
|
|
|
|
* into spaces.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'l':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_hour % 12;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LS;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The month as a decimal number with a leading zero */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'm':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_mon;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The minute as a decimal number with a leading zero */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'M':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_min;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* A newline character */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'n':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.char_val = '\n';
|
|
|
|
replace_type = PGTYPES_TYPE_CHAR;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the AM/PM specifier (uppercase) */
|
|
|
|
/* XXX should be locale aware */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'p':
|
2003-08-04 02:43:34 +02:00
|
|
|
if (tm->tm_hour < 12)
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = "AM";
|
2003-08-04 02:43:34 +02:00
|
|
|
else
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = "PM";
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the AM/PM specifier (lowercase) */
|
|
|
|
/* XXX should be locale aware */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'P':
|
2003-08-04 02:43:34 +02:00
|
|
|
if (tm->tm_hour < 12)
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = "am";
|
2003-08-04 02:43:34 +02:00
|
|
|
else
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.str_val = "pm";
|
|
|
|
replace_type = PGTYPES_TYPE_STRING_CONSTANT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* the time in the format %I:%M:%S %p */
|
|
|
|
/* XXX should be locale aware */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'r':
|
|
|
|
i = dttofmtasc_replace(ts, dDate, dow, tm,
|
2003-08-04 02:43:34 +02:00
|
|
|
q, pstr_len,
|
|
|
|
"%I:%M:%S %p");
|
|
|
|
if (i)
|
|
|
|
return i;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The time in 24 hour notation (%H:%M) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'R':
|
|
|
|
i = dttofmtasc_replace(ts, dDate, dow, tm,
|
2003-08-04 02:43:34 +02:00
|
|
|
q, pstr_len,
|
|
|
|
"%H:%M");
|
|
|
|
if (i)
|
|
|
|
return i;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The number of seconds since the Epoch (1970-01-01) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 's':
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
2005-07-12 18:05:12 +02:00
|
|
|
replace_val.int64_val = (*ts - SetEpochTimestamp()) / 1000000.0;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_INT64;
|
2003-04-01 16:37:25 +02:00
|
|
|
#else
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.double_val = *ts - SetEpochTimestamp();
|
|
|
|
replace_type = PGTYPES_TYPE_DOUBLE_NF;
|
2003-04-01 16:37:25 +02:00
|
|
|
#endif
|
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* seconds as a decimal number with leading zeroes */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'S':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_sec;
|
2003-09-20 11:10:09 +02:00
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* A tabulator */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 't':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.char_val = '\t';
|
|
|
|
replace_type = PGTYPES_TYPE_CHAR;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The time in 24 hour notation (%H:%M:%S) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'T':
|
|
|
|
i = dttofmtasc_replace(ts, dDate, dow, tm,
|
2003-08-04 02:43:34 +02:00
|
|
|
q, pstr_len,
|
|
|
|
"%H:%M:%S");
|
|
|
|
if (i)
|
|
|
|
return i;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The day of the week as a decimal, Monday = 1, Sunday =
|
|
|
|
* 7
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'u':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = dow;
|
2006-08-15 08:40:20 +02:00
|
|
|
if (replace_val.uint_val == 0)
|
|
|
|
replace_val.uint_val = 7;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_UINT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The week number of the year as a decimal number */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'U':
|
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, "%U", tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The ISO 8601:1988 week number of the current year as a
|
|
|
|
* decimal number.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'V':
|
2003-08-04 02:43:34 +02:00
|
|
|
{
|
2011-04-29 01:58:49 +02:00
|
|
|
/* Keep compiler quiet - Don't use a literal format */
|
2011-06-09 20:32:50 +02:00
|
|
|
const char *fmt = "%V";
|
2011-04-29 01:58:49 +02:00
|
|
|
|
|
|
|
i = strftime(q, *pstr_len, fmt, tm);
|
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
}
|
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The day of the week as a decimal, Sunday being 0 and
|
|
|
|
* Monday 1.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'w':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = dow;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The week number of the year (another definition) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'W':
|
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, "%U", tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The preferred date representation for the current
|
|
|
|
* locale without the time.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'x':
|
2003-06-25 12:44:21 +02:00
|
|
|
{
|
2007-03-17 20:25:24 +01:00
|
|
|
const char *fmt = "%x"; /* Keep compiler quiet about
|
2005-10-15 04:49:52 +02:00
|
|
|
* 2-digit year */
|
2003-06-25 12:44:21 +02:00
|
|
|
|
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, fmt, tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-06-25 12:44:21 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
}
|
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The preferred time representation for the current
|
|
|
|
* locale without the date.
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'X':
|
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, "%X", tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The year without the century (2 digits, leading zeroes) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'y':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.uint_val = tm->tm_year % 100;
|
|
|
|
replace_type = PGTYPES_TYPE_UINT_2_LZ;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The year with the century (4 digits) */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'Y':
|
2003-09-01 14:37:40 +02:00
|
|
|
replace_val.uint_val = tm->tm_year;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_UINT;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The time zone offset from GMT */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'z':
|
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, "%z", tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* The name or abbreviation of the time zone */
|
2003-04-01 16:37:25 +02:00
|
|
|
case 'Z':
|
|
|
|
tm->tm_mon -= 1;
|
|
|
|
i = strftime(q, *pstr_len, "%Z", tm);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i == 0)
|
|
|
|
return -1;
|
|
|
|
while (*q)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
tm->tm_mon += 1;
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_type = PGTYPES_TYPE_NOTHING;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
2006-10-04 02:30:14 +02:00
|
|
|
/* A % sign */
|
2003-04-01 16:37:25 +02:00
|
|
|
case '%':
|
2003-08-01 10:21:04 +02:00
|
|
|
replace_val.char_val = '%';
|
|
|
|
replace_type = PGTYPES_TYPE_CHAR;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
|
|
|
case '\0':
|
2006-08-15 08:40:20 +02:00
|
|
|
/* fmtstr: foo%' - The string ends with a % sign */
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
/*
|
|
|
|
* this is not compliant to the specification
|
|
|
|
*/
|
2003-04-01 16:37:25 +02:00
|
|
|
return -1;
|
|
|
|
default:
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
/*
|
|
|
|
* if we don't know the pattern, we just copy it
|
|
|
|
*/
|
|
|
|
if (*pstr_len > 1)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
*q = '%';
|
2003-08-04 02:43:34 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
if (*pstr_len > 1)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
*q = *p;
|
2003-08-04 02:43:34 +02:00
|
|
|
q++;
|
|
|
|
(*pstr_len)--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
*q = '\0';
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
else
|
|
|
|
return -1;
|
2003-04-01 16:37:25 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
|
2003-08-04 02:43:34 +02:00
|
|
|
if (i)
|
2003-04-01 16:37:25 +02:00
|
|
|
return i;
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (*pstr_len > 1)
|
|
|
|
{
|
2003-04-01 16:37:25 +02:00
|
|
|
*q = *p;
|
|
|
|
(*pstr_len)--;
|
|
|
|
q++;
|
|
|
|
*q = '\0';
|
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
else
|
|
|
|
return -1;
|
2003-04-01 16:37:25 +02:00
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2005-10-15 04:49:52 +02:00
|
|
|
PGTYPEStimestamp_fmt_asc(timestamp * ts, char *output, int str_len, char *fmtstr)
|
2003-04-01 16:37:25 +02:00
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
struct tm tm;
|
|
|
|
fsec_t fsec;
|
2003-09-09 12:46:42 +02:00
|
|
|
date dDate;
|
2003-08-04 02:43:34 +02:00
|
|
|
int dow;
|
2003-04-01 16:37:25 +02:00
|
|
|
|
|
|
|
dDate = PGTYPESdate_from_timestamp(*ts);
|
|
|
|
dow = PGTYPESdate_dayofweek(dDate);
|
|
|
|
timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
|
|
|
|
|
|
|
|
return dttofmtasc_replace(ts, dDate, dow, &tm, output, &str_len, fmtstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2005-10-15 04:49:52 +02:00
|
|
|
PGTYPEStimestamp_sub(timestamp * ts1, timestamp * ts2, interval * iv)
|
2003-04-01 16:37:25 +02:00
|
|
|
{
|
|
|
|
if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
|
|
|
|
return PGTYPES_TS_ERR_EINFTIME;
|
|
|
|
else
|
2008-04-10 12:43:35 +02:00
|
|
|
iv->time = (*ts1 - *ts2);
|
2003-04-01 16:37:25 +02:00
|
|
|
|
|
|
|
iv->month = 0;
|
|
|
|
|
|
|
|
return 0;
|
2003-03-20 16:56:50 +01:00
|
|
|
}
|
2003-04-01 16:37:25 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
int
|
2005-10-15 04:49:52 +02:00
|
|
|
PGTYPEStimestamp_defmt_asc(char *str, char *fmt, timestamp * d)
|
2003-08-04 02:43:34 +02:00
|
|
|
{
|
|
|
|
int year,
|
|
|
|
month,
|
|
|
|
day;
|
|
|
|
int hour,
|
|
|
|
minute,
|
|
|
|
second;
|
|
|
|
int tz;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
char *mstr;
|
|
|
|
char *mfmt;
|
|
|
|
|
|
|
|
if (!fmt)
|
2003-08-01 10:21:04 +02:00
|
|
|
fmt = "%Y-%m-%d %H:%M:%S";
|
2003-08-04 02:43:34 +02:00
|
|
|
if (!fmt[0])
|
2003-08-01 10:21:04 +02:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
mstr = pgtypes_strdup(str);
|
|
|
|
mfmt = pgtypes_strdup(fmt);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize with impossible values so that we can see if the fields
|
|
|
|
* where specified at all
|
|
|
|
*/
|
2003-08-01 10:21:04 +02:00
|
|
|
/* XXX ambiguity with 1 BC for year? */
|
2003-08-04 02:43:34 +02:00
|
|
|
year = -1;
|
|
|
|
month = -1;
|
|
|
|
day = -1;
|
|
|
|
hour = 0;
|
|
|
|
minute = -1;
|
|
|
|
second = -1;
|
2003-08-01 10:21:04 +02:00
|
|
|
tz = 0;
|
|
|
|
|
|
|
|
i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);
|
|
|
|
free(mstr);
|
|
|
|
free(mfmt);
|
|
|
|
return i;
|
|
|
|
}
|
2004-12-23 11:46:10 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* add an interval to a time stamp
|
|
|
|
*
|
2005-10-15 04:49:52 +02:00
|
|
|
* *tout = tin + span
|
2004-12-23 11:46:10 +01:00
|
|
|
*
|
2005-10-15 04:49:52 +02:00
|
|
|
* returns 0 if successful
|
|
|
|
* returns -1 if it fails
|
2004-12-23 11:46:10 +01:00
|
|
|
*
|
|
|
|
*/
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2004-12-23 11:46:10 +01:00
|
|
|
int
|
2005-10-15 04:49:52 +02:00
|
|
|
PGTYPEStimestamp_add_interval(timestamp * tin, interval * span, timestamp * tout)
|
2004-12-23 11:46:10 +01:00
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
if (TIMESTAMP_NOT_FINITE(*tin))
|
|
|
|
*tout = *tin;
|
|
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (span->month != 0)
|
|
|
|
{
|
|
|
|
struct tm tt,
|
|
|
|
*tm = &tt;
|
|
|
|
fsec_t fsec;
|
|
|
|
|
|
|
|
|
|
|
|
if (timestamp2tm(*tin, NULL, tm, &fsec, NULL) != 0)
|
|
|
|
return -1;
|
|
|
|
tm->tm_mon += span->month;
|
|
|
|
if (tm->tm_mon > MONTHS_PER_YEAR)
|
|
|
|
{
|
|
|
|
tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
|
|
|
|
tm->tm_mon = (tm->tm_mon - 1) % MONTHS_PER_YEAR + 1;
|
|
|
|
}
|
|
|
|
else if (tm->tm_mon < 1)
|
|
|
|
{
|
|
|
|
tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
|
|
|
|
tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* adjust for end of month boundary problems... */
|
|
|
|
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
|
|
|
|
tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
|
|
|
|
|
|
|
|
|
|
|
|
if (tm2timestamp(tm, fsec, NULL, tin) != 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*tin += span->time;
|
|
|
|
*tout = *tin;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
2004-12-23 11:46:10 +01:00
|
|
|
}
|
2005-10-15 04:49:52 +02:00
|
|
|
|
|
|
|
|
2004-12-23 11:46:10 +01:00
|
|
|
/*
|
|
|
|
* subtract an interval from a time stamp
|
|
|
|
*
|
2005-10-15 04:49:52 +02:00
|
|
|
* *tout = tin - span
|
2004-12-23 11:46:10 +01:00
|
|
|
*
|
2005-10-15 04:49:52 +02:00
|
|
|
* returns 0 if successful
|
|
|
|
* returns -1 if it fails
|
2004-12-23 11:46:10 +01:00
|
|
|
*
|
|
|
|
*/
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2004-12-23 11:46:10 +01:00
|
|
|
int
|
2005-10-15 04:49:52 +02:00
|
|
|
PGTYPEStimestamp_sub_interval(timestamp * tin, interval * span, timestamp * tout)
|
2004-12-23 11:46:10 +01:00
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
interval tspan;
|
|
|
|
|
|
|
|
tspan.month = -span->month;
|
|
|
|
tspan.time = -span->time;
|
2004-12-23 11:46:10 +01:00
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
|
|
|
|
return PGTYPEStimestamp_add_interval(tin, &tspan, tout);
|
|
|
|
}
|