Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Typedefs | Functions | Variables

dt_common.c File Reference

#include "postgres_fe.h"
#include <time.h>
#include <ctype.h>
#include <math.h>
#include "extern.h"
#include "dt.h"
#include "pgtypes_timestamp.h"
Include dependency graph for dt_common.c:

Go to the source code of this file.

Defines

#define ABS_SIGNBIT   ((char) 0200)
#define POS(n)   (n)
#define NEG(n)   ((n)|ABS_SIGNBIT)
#define FROMVAL(tp)   (-SIGNEDCHAR((tp)->value) * 15)
#define VALMASK   ((char) 0177)
#define SIGNEDCHAR(c)   ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))

Typedefs

typedef long AbsoluteTime

Functions

static datetkndatebsearch (char *key, datetkn *base, unsigned int nel)
int DecodeUnits (int field, char *lowtoken, int *val)
int date2j (int y, int m, int d)
void j2date (int jd, int *year, int *month, int *day)
static int DecodeSpecial (int field, char *lowtoken, int *val)
int EncodeDateOnly (struct tm *tm, int style, char *str, bool EuroDates)
void TrimTrailingZeros (char *str)
int EncodeDateTime (struct tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates)
int GetEpochTime (struct tm *tm)
static void abstime2tm (AbsoluteTime _time, int *tzp, struct tm *tm, char **tzn)
void GetCurrentDateTime (struct tm *tm)
void dt2time (double jd, int *hour, int *min, int *sec, fsec_t *fsec)
static int DecodeNumberField (int len, char *str, int fmask, int *tmask, struct tm *tm, fsec_t *fsec, int *is2digits)
static int DecodeNumber (int flen, char *str, int fmask, int *tmask, struct tm *tm, fsec_t *fsec, int *is2digits, bool EuroDates)
static int DecodeDate (char *str, int fmask, int *tmask, struct tm *tm, bool EuroDates)
int DecodeTime (char *str, int *tmask, struct tm *tm, fsec_t *fsec)
static int DecodeTimezone (char *str, int *tzp)
static int DecodePosixTimezone (char *str, int *tzp)
int ParseDateTime (char *timestr, char *lowstr, char **field, int *ftype, int *numfields, char **endstr)
int DecodeDateTime (char **field, int *ftype, int nf, int *dtype, struct tm *tm, fsec_t *fsec, bool EuroDates)
static char * find_end_token (char *str, char *fmt)
static int pgtypes_defmt_scan (union un_fmt_comb *scan_val, int scan_type, char **pstr, char *pfmt)
int PGTYPEStimestamp_defmt_scan (char **, char *, timestamp *, int *, int *, int *, int *, int *, int *, int *)

Variables

int day_tab [2][13]
static datetkn datetktbl []
static datetkn deltatktbl []
static const unsigned int szdatetktbl = lengthof(datetktbl)
static const unsigned int szdeltatktbl = lengthof(deltatktbl)
static datetkndatecache [MAXDATEFIELDS] = {NULL}
static datetkndeltacache [MAXDATEFIELDS] = {NULL}
char * months [] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}
char * days [] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL}
char * pgtypes_date_weekdays_short [] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL}
char * pgtypes_date_months [] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL}

Define Documentation

#define ABS_SIGNBIT   ((char) 0200)

Definition at line 19 of file dt_common.c.

#define FROMVAL (   tp  )     (-SIGNEDCHAR((tp)->value) * 15)

Definition at line 22 of file dt_common.c.

Referenced by DecodeSpecial(), and DecodeUnits().

#define NEG (   n  )     ((n)|ABS_SIGNBIT)

Definition at line 21 of file dt_common.c.

#define POS (   n  )     (n)

Definition at line 20 of file dt_common.c.

#define SIGNEDCHAR (   c  )     ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))

Definition at line 24 of file dt_common.c.

#define VALMASK   ((char) 0177)

Definition at line 23 of file dt_common.c.


Typedef Documentation

typedef long AbsoluteTime

Definition at line 17 of file dt_common.c.


Function Documentation

static void abstime2tm ( AbsoluteTime  _time,
int *  tzp,
struct tm tm,
char **  tzn 
) [static]

Definition at line 1035 of file dt_common.c.

References MAXTZLEN, NULL, SECS_PER_HOUR, StrNCpy, TIMEZONE_GLOBAL, and TZNAME_GLOBAL.

{
    time_t      time = (time_t) _time;
    struct tm  *tx;

    errno = 0;
    if (tzp != NULL)
        tx = localtime((time_t *) &time);
    else
        tx = gmtime((time_t *) &time);

    if (!tx)
    {
        errno = PGTYPES_TS_BAD_TIMESTAMP;
        return;
    }

    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;

#if defined(HAVE_TM_ZONE)
    tm->tm_gmtoff = tx->tm_gmtoff;
    tm->tm_zone = tx->tm_zone;

    if (tzp != NULL)
    {
        /*
         * We have a brute force time zone per SQL99? Then use it without
         * change since we have already rotated to the time zone.
         */
        *tzp = -tm->tm_gmtoff;  /* tm_gmtoff is Sun/DEC-ism */

        /*
         * FreeBSD man pages indicate that this should work - tgl 97/04/23
         */
        if (tzn != NULL)
        {
            /*
             * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
             * contains an error message, which doesn't fit in the buffer
             */
            StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
            if (strlen(tm->tm_zone) > MAXTZLEN)
                tm->tm_isdst = -1;
        }
    }
    else
        tm->tm_isdst = -1;
#elif defined(HAVE_INT_TIMEZONE)
    if (tzp != NULL)
    {
        *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;

        if (tzn != NULL)
        {
            /*
             * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
             * contains an error message, which doesn't fit in the buffer
             */
            StrNCpy(*tzn, TZNAME_GLOBAL[tm->tm_isdst], MAXTZLEN + 1);
            if (strlen(TZNAME_GLOBAL[tm->tm_isdst]) > MAXTZLEN)
                tm->tm_isdst = -1;
        }
    }
    else
        tm->tm_isdst = -1;
#else                           /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
    if (tzp != NULL)
    {
        /* default to UTC */
        *tzp = 0;
        if (tzn != NULL)
            *tzn = NULL;
    }
    else
        tm->tm_isdst = -1;
#endif
}

int date2j ( int  y,
int  m,
int  d 
)

Definition at line 592 of file dt_common.c.

{
    int         julian;
    int         century;

    if (m > 2)
    {
        m += 1;
        y += 4800;
    }
    else
    {
        m += 13;
        y += 4799;
    }

    century = y / 100;
    julian = y * 365 - 32167;
    julian += y / 4 - century + century / 4;
    julian += 7834 * m / 256 + d;

    return julian;
}   /* date2j() */

static datetkn* datebsearch ( char *  key,
datetkn base,
unsigned int  nel 
) [static]

Definition at line 513 of file dt_common.c.

References TOKMAXLEN.

Referenced by DecodeSpecial(), and DecodeUnits().

{
    if (nel > 0)
    {
        datetkn    *last = base + nel - 1,
                   *position;
        int         result;

        while (last >= base)
        {
            position = base + ((last - base) >> 1);
            result = key[0] - position->token[0];
            if (result == 0)
            {
                result = strncmp(key, position->token, TOKMAXLEN);
                if (result == 0)
                    return position;
            }
            if (result < 0)
                last = position - 1;
            else
                base = position + 1;
        }
    }
    return NULL;
}

static int DecodeDate ( char *  str,
int  fmask,
int *  tmask,
struct tm tm,
bool  EuroDates 
) [static]

Definition at line 1378 of file dt_common.c.

References ADBC, DecodeNumber(), DecodeSpecial(), DOY, DTK_M, i, IGNORE_DTF, MAXDATEFIELDS, MONTH, NULL, TZ, and val.

Referenced by DecodeDateTime().

{
    fsec_t      fsec;

    int         nf = 0;
    int         i,
                len;
    int         bc = FALSE;
    int         is2digits = FALSE;
    int         type,
                val,
                dmask = 0;
    char       *field[MAXDATEFIELDS];

    /* parse this string... */
    while (*str != '\0' && nf < MAXDATEFIELDS)
    {
        /* skip field separators */
        while (!isalnum((unsigned char) *str))
            str++;

        field[nf] = str;
        if (isdigit((unsigned char) *str))
        {
            while (isdigit((unsigned char) *str))
                str++;
        }
        else if (isalpha((unsigned char) *str))
        {
            while (isalpha((unsigned char) *str))
                str++;
        }

        /* Just get rid of any non-digit, non-alpha characters... */
        if (*str != '\0')
            *str++ = '\0';
        nf++;
    }

#if 0
    /* don't allow too many fields */
    if (nf > 3)
        return -1;
#endif

    *tmask = 0;

    /* look first for text fields, since that will be unambiguous month */
    for (i = 0; i < nf; i++)
    {
        if (isalpha((unsigned char) *field[i]))
        {
            type = DecodeSpecial(i, field[i], &val);
            if (type == IGNORE_DTF)
                continue;

            dmask = DTK_M(type);
            switch (type)
            {
                case MONTH:
                    tm->tm_mon = val;
                    break;

                case ADBC:
                    bc = (val == BC);
                    break;

                default:
                    return -1;
            }
            if (fmask & dmask)
                return -1;

            fmask |= dmask;
            *tmask |= dmask;

            /* mark this field as being completed */
            field[i] = NULL;
        }
    }

    /* now pick up remaining numeric fields */
    for (i = 0; i < nf; i++)
    {
        if (field[i] == NULL)
            continue;

        if ((len = strlen(field[i])) <= 0)
            return -1;

        if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
            return -1;

        if (fmask & dmask)
            return -1;

        fmask |= dmask;
        *tmask |= dmask;
    }

    if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
        return -1;

    /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
    if (bc)
    {
        if (tm->tm_year > 0)
            tm->tm_year = -(tm->tm_year - 1);
        else
            return -1;
    }
    else if (is2digits)
    {
        if (tm->tm_year < 70)
            tm->tm_year += 2000;
        else if (tm->tm_year < 100)
            tm->tm_year += 1900;
    }

    return 0;
}   /* DecodeDate() */

int DecodeDateTime ( char **  field,
int *  ftype,
int  nf,
int *  dtype,
struct tm tm,
fsec_t fsec,
bool  EuroDates 
)

Definition at line 1852 of file dt_common.c.

References ADBC, AM, AMPM, date2j(), DAY, day_tab, DecodeDate(), DecodeNumber(), DecodeNumberField(), DecodePosixTimezone(), DecodeSpecial(), DecodeTime(), DecodeTimezone(), DOW, dt2time(), DTK_DATE, DTK_DATE_M, DTK_DAY, DTK_HOUR, DTK_JULIAN, DTK_M, DTK_MINUTE, DTK_MONTH, DTK_NOW, DTK_NUMBER, DTK_SECOND, DTK_SPECIAL, DTK_STRING, DTK_TIME, DTK_TIME_M, DTK_TODAY, DTK_TOMORROW, DTK_TZ, DTK_YEAR, DTK_YESTERDAY, DTK_ZULU, DTZ, DTZMOD, GetCurrentDateTime(), HOUR, HR24, i, IGNORE_DTF, isleap, ISOTIME, j2date(), MINUTE, MONTH, NULL, PM, RESERV, SECOND, SECS_PER_DAY, TZ, UNITS, USECS_PER_DAY, val, and YEAR.

{
    int         fmask = 0,
                tmask,
                type;
    int         ptype = 0;      /* "prefix type" for ISO y2001m02d04 format */
    int         i;
    int         val;
    int         mer = HR24;
    int         haveTextMonth = FALSE;
    int         is2digits = FALSE;
    int         bc = FALSE;
    int         t = 0;
    int        *tzp = &t;

    /***
     * We'll insist on at least all of the date fields, but initialize the
     * remaining fields in case they are not set later...
     ***/
    *dtype = DTK_DATE;
    tm->tm_hour = 0;
    tm->tm_min = 0;
    tm->tm_sec = 0;
    *fsec = 0;
    /* don't know daylight savings time status apriori */
    tm->tm_isdst = -1;
    if (tzp != NULL)
        *tzp = 0;

    for (i = 0; i < nf; i++)
    {
        switch (ftype[i])
        {
            case DTK_DATE:
                /***
                 * Integral julian day with attached time zone?
                 * All other forms with JD will be separated into
                 * distinct fields, so we handle just this case here.
                 ***/
                if (ptype == DTK_JULIAN)
                {
                    char       *cp;
                    int         val;

                    if (tzp == NULL)
                        return -1;

                    val = strtol(field[i], &cp, 10);
                    if (*cp != '-')
                        return -1;

                    j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
                    /* Get the time zone from the end of the string */
                    if (DecodeTimezone(cp, tzp) != 0)
                        return -1;

                    tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
                    ptype = 0;
                    break;
                }
                /***
                 * Already have a date? Then this might be a POSIX time
                 * zone with an embedded dash (e.g. "PST-3" == "EST") or
                 * a run-together time with trailing time zone (e.g. hhmmss-zz).
                 * - thomas 2001-12-25
                 ***/
                else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
                         || (ptype != 0))
                {
                    /* No time zone accepted? Then quit... */
                    if (tzp == NULL)
                        return -1;

                    if (isdigit((unsigned char) *field[i]) || ptype != 0)
                    {
                        char       *cp;

                        if (ptype != 0)
                        {
                            /* Sanity check; should not fail this test */
                            if (ptype != DTK_TIME)
                                return -1;
                            ptype = 0;
                        }

                        /*
                         * Starts with a digit but we already have a time
                         * field? Then we are in trouble with a date and time
                         * already...
                         */
                        if ((fmask & DTK_TIME_M) == DTK_TIME_M)
                            return -1;

                        if ((cp = strchr(field[i], '-')) == NULL)
                            return -1;

                        /* Get the time zone from the end of the string */
                        if (DecodeTimezone(cp, tzp) != 0)
                            return -1;
                        *cp = '\0';

                        /*
                         * Then read the rest of the field as a concatenated
                         * time
                         */
                        if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
                                          &tmask, tm, fsec, &is2digits)) < 0)
                            return -1;

                        /*
                         * modify tmask after returning from
                         * DecodeNumberField()
                         */
                        tmask |= DTK_M(TZ);
                    }
                    else
                    {
                        if (DecodePosixTimezone(field[i], tzp) != 0)
                            return -1;

                        ftype[i] = DTK_TZ;
                        tmask = DTK_M(TZ);
                    }
                }
                else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
                    return -1;
                break;

            case DTK_TIME:
                if (DecodeTime(field[i], &tmask, tm, fsec) != 0)
                    return -1;

                /*
                 * Check upper limit on hours; other limits checked in
                 * DecodeTime()
                 */
                /* test for > 24:00:00 */
                if (tm->tm_hour > 24 ||
                    (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)))
                    return -1;
                break;

            case DTK_TZ:
                {
                    int         tz;

                    if (tzp == NULL)
                        return -1;

                    if (DecodeTimezone(field[i], &tz) != 0)
                        return -1;

                    /*
                     * Already have a time zone? Then maybe this is the second
                     * field of a POSIX time: EST+3 (equivalent to PST)
                     */
                    if (i > 0 && (fmask & DTK_M(TZ)) != 0 &&
                        ftype[i - 1] == DTK_TZ &&
                        isalpha((unsigned char) *field[i - 1]))
                    {
                        *tzp -= tz;
                        tmask = 0;
                    }
                    else
                    {
                        *tzp = tz;
                        tmask = DTK_M(TZ);
                    }
                }
                break;

            case DTK_NUMBER:

                /*
                 * Was this an "ISO date" with embedded field labels? An
                 * example is "y2001m02d04" - thomas 2001-02-04
                 */
                if (ptype != 0)
                {
                    char       *cp;
                    int         val;

                    val = strtol(field[i], &cp, 10);

                    /*
                     * only a few kinds are allowed to have an embedded
                     * decimal
                     */
                    if (*cp == '.')
                        switch (ptype)
                        {
                            case DTK_JULIAN:
                            case DTK_TIME:
                            case DTK_SECOND:
                                break;
                            default:
                                return 1;
                                break;
                        }
                    else if (*cp != '\0')
                        return -1;

                    switch (ptype)
                    {
                        case DTK_YEAR:
                            tm->tm_year = val;
                            tmask = DTK_M(YEAR);
                            break;

                        case DTK_MONTH:

                            /*
                             * already have a month and hour? then assume
                             * minutes
                             */
                            if ((fmask & DTK_M(MONTH)) != 0 &&
                                (fmask & DTK_M(HOUR)) != 0)
                            {
                                tm->tm_min = val;
                                tmask = DTK_M(MINUTE);
                            }
                            else
                            {
                                tm->tm_mon = val;
                                tmask = DTK_M(MONTH);
                            }
                            break;

                        case DTK_DAY:
                            tm->tm_mday = val;
                            tmask = DTK_M(DAY);
                            break;

                        case DTK_HOUR:
                            tm->tm_hour = val;
                            tmask = DTK_M(HOUR);
                            break;

                        case DTK_MINUTE:
                            tm->tm_min = val;
                            tmask = DTK_M(MINUTE);
                            break;

                        case DTK_SECOND:
                            tm->tm_sec = val;
                            tmask = DTK_M(SECOND);
                            if (*cp == '.')
                            {
                                double      frac;

                                frac = strtod(cp, &cp);
                                if (*cp != '\0')
                                    return -1;
#ifdef HAVE_INT64_TIMESTAMP
                                *fsec = frac * 1000000;
#else
                                *fsec = frac;
#endif
                            }
                            break;

                        case DTK_TZ:
                            tmask = DTK_M(TZ);
                            if (DecodeTimezone(field[i], tzp) != 0)
                                return -1;
                            break;

                        case DTK_JULIAN:
                            /***
                             * previous field was a label for "julian date"?
                             ***/
                            tmask = DTK_DATE_M;
                            j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
                            /* fractional Julian Day? */
                            if (*cp == '.')
                            {
                                double      time;

                                time = strtod(cp, &cp);
                                if (*cp != '\0')
                                    return -1;

                                tmask |= DTK_TIME_M;
#ifdef HAVE_INT64_TIMESTAMP
                                dt2time((time * USECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
#else
                                dt2time((time * SECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
#endif
                            }
                            break;

                        case DTK_TIME:
                            /* previous field was "t" for ISO time */
                            if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
                                          &tmask, tm, fsec, &is2digits)) < 0)
                                return -1;

                            if (tmask != DTK_TIME_M)
                                return -1;
                            break;

                        default:
                            return -1;
                            break;
                    }

                    ptype = 0;
                    *dtype = DTK_DATE;
                }
                else
                {
                    char       *cp;
                    int         flen;

                    flen = strlen(field[i]);
                    cp = strchr(field[i], '.');

                    /* Embedded decimal and no date yet? */
                    if (cp != NULL && !(fmask & DTK_DATE_M))
                    {
                        if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
                            return -1;
                    }
                    /* embedded decimal and several digits before? */
                    else if (cp != NULL && flen - strlen(cp) > 2)
                    {
                        /*
                         * Interpret as a concatenated date or time Set the
                         * type field to allow decoding other fields later.
                         * Example: 20011223 or 040506
                         */
                        if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
                                          &tmask, tm, fsec, &is2digits)) < 0)
                            return -1;
                    }
                    else if (flen > 4)
                    {
                        if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
                                          &tmask, tm, fsec, &is2digits)) < 0)
                            return -1;
                    }
                    /* otherwise it is a single date/time field... */
                    else if (DecodeNumber(flen, field[i], fmask,
                               &tmask, tm, fsec, &is2digits, EuroDates) != 0)
                        return -1;
                }
                break;

            case DTK_STRING:
            case DTK_SPECIAL:
                type = DecodeSpecial(i, field[i], &val);
                if (type == IGNORE_DTF)
                    continue;

                tmask = DTK_M(type);
                switch (type)
                {
                    case RESERV:
                        switch (val)
                        {
                            case DTK_NOW:
                                tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
                                *dtype = DTK_DATE;
                                GetCurrentDateTime(tm);
                                break;

                            case DTK_YESTERDAY:
                                tmask = DTK_DATE_M;
                                *dtype = DTK_DATE;
                                GetCurrentDateTime(tm);
                                j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1,
                                    &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
                                tm->tm_hour = 0;
                                tm->tm_min = 0;
                                tm->tm_sec = 0;
                                break;

                            case DTK_TODAY:
                                tmask = DTK_DATE_M;
                                *dtype = DTK_DATE;
                                GetCurrentDateTime(tm);
                                tm->tm_hour = 0;
                                tm->tm_min = 0;
                                tm->tm_sec = 0;
                                break;

                            case DTK_TOMORROW:
                                tmask = DTK_DATE_M;
                                *dtype = DTK_DATE;
                                GetCurrentDateTime(tm);
                                j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1,
                                    &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
                                tm->tm_hour = 0;
                                tm->tm_min = 0;
                                tm->tm_sec = 0;
                                break;

                            case DTK_ZULU:
                                tmask = (DTK_TIME_M | DTK_M(TZ));
                                *dtype = DTK_DATE;
                                tm->tm_hour = 0;
                                tm->tm_min = 0;
                                tm->tm_sec = 0;
                                if (tzp != NULL)
                                    *tzp = 0;
                                break;

                            default:
                                *dtype = val;
                        }

                        break;

                    case MONTH:

                        /*
                         * already have a (numeric) month? then see if we can
                         * substitute...
                         */
                        if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
                            !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && tm->tm_mon <= 31)
                        {
                            tm->tm_mday = tm->tm_mon;
                            tmask = DTK_M(DAY);
                        }
                        haveTextMonth = TRUE;
                        tm->tm_mon = val;
                        break;

                    case DTZMOD:

                        /*
                         * daylight savings time modifier (solves "MET DST"
                         * syntax)
                         */
                        tmask |= DTK_M(DTZ);
                        tm->tm_isdst = 1;
                        if (tzp == NULL)
                            return -1;
                        *tzp += val * MINS_PER_HOUR;
                        break;

                    case DTZ:

                        /*
                         * set mask for TZ here _or_ check for DTZ later when
                         * getting default timezone
                         */
                        tmask |= DTK_M(TZ);
                        tm->tm_isdst = 1;
                        if (tzp == NULL)
                            return -1;
                        *tzp = val * MINS_PER_HOUR;
                        ftype[i] = DTK_TZ;
                        break;

                    case TZ:
                        tm->tm_isdst = 0;
                        if (tzp == NULL)
                            return -1;
                        *tzp = val * MINS_PER_HOUR;
                        ftype[i] = DTK_TZ;
                        break;

                    case IGNORE_DTF:
                        break;

                    case AMPM:
                        mer = val;
                        break;

                    case ADBC:
                        bc = (val == BC);
                        break;

                    case DOW:
                        tm->tm_wday = val;
                        break;

                    case UNITS:
                        tmask = 0;
                        ptype = val;
                        break;

                    case ISOTIME:

                        /*
                         * This is a filler field "t" indicating that the next
                         * field is time. Try to verify that this is sensible.
                         */
                        tmask = 0;

                        /* No preceding date? Then quit... */
                        if ((fmask & DTK_DATE_M) != DTK_DATE_M)
                            return -1;

                        /***
                         * We will need one of the following fields:
                         *  DTK_NUMBER should be hhmmss.fff
                         *  DTK_TIME should be hh:mm:ss.fff
                         *  DTK_DATE should be hhmmss-zz
                         ***/
                        if (i >= nf - 1 ||
                            (ftype[i + 1] != DTK_NUMBER &&
                             ftype[i + 1] != DTK_TIME &&
                             ftype[i + 1] != DTK_DATE))
                            return -1;

                        ptype = val;
                        break;

                    default:
                        return -1;
                }
                break;

            default:
                return -1;
        }

        if (tmask & fmask)
            return -1;
        fmask |= tmask;
    }

    /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
    if (bc)
    {
        if (tm->tm_year > 0)
            tm->tm_year = -(tm->tm_year - 1);
        else
            return -1;
    }
    else if (is2digits)
    {
        if (tm->tm_year < 70)
            tm->tm_year += 2000;
        else if (tm->tm_year < 100)
            tm->tm_year += 1900;
    }

    if (mer != HR24 && tm->tm_hour > 12)
        return -1;
    if (mer == AM && tm->tm_hour == 12)
        tm->tm_hour = 0;
    else if (mer == PM && tm->tm_hour != 12)
        tm->tm_hour += 12;

    /* do additional checking for full date specs... */
    if (*dtype == DTK_DATE)
    {
        if ((fmask & DTK_DATE_M) != DTK_DATE_M)
            return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;

        /*
         * check for valid day of month, now that we know for sure the month
         * and year...
         */
        if (tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
            return -1;

        /*
         * backend tried to find local timezone here but we don't use the
         * result afterwards anyway so we only check for this error: daylight
         * savings time modifier but no standard timezone?
         */
        if ((fmask & DTK_DATE_M) == DTK_DATE_M && tzp != NULL && !(fmask & DTK_M(TZ)) && (fmask & DTK_M(DTZMOD)))
            return -1;
    }

    return 0;
}   /* DecodeDateTime() */

static int DecodeNumber ( int  flen,
char *  str,
int  fmask,
int *  tmask,
struct tm tm,
fsec_t fsec,
int *  is2digits,
bool  EuroDates 
) [static]

Definition at line 1269 of file dt_common.c.

References date2j(), DAY, DecodeNumberField(), DOY, DTK_DATE_M, DTK_M, j2date(), MONTH, MONTHS_PER_YEAR, val, and YEAR.

Referenced by DecodeDate(), and DecodeDateTime().

{
    int         val;
    char       *cp;

    *tmask = 0;

    val = strtol(str, &cp, 10);
    if (cp == str)
        return -1;

    if (*cp == '.')
    {
        /*
         * More than two digits? Then could be a date or a run-together time:
         * 2001.360 20011225 040506.789
         */
        if (cp - str > 2)
            return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
                                     tmask, tm, fsec, is2digits);

        *fsec = strtod(cp, &cp);
        if (*cp != '\0')
            return -1;
    }
    else if (*cp != '\0')
        return -1;

    /* Special case day of year? */
    if (flen == 3 && (fmask & DTK_M(YEAR)) && val >= 1 && val <= 366)
    {
        *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
        tm->tm_yday = val;
        j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
               &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    }

    /***
     * Enough digits to be unequivocal year? Used to test for 4 digits or
     * more, but we now test first for a three-digit doy so anything
     * bigger than two digits had better be an explicit year.
     * - thomas 1999-01-09
     * Back to requiring a 4 digit year. We accept a two digit
     * year farther down. - thomas 2000-03-28
     ***/
    else if (flen >= 4)
    {
        *tmask = DTK_M(YEAR);

        /* already have a year? then see if we can substitute... */
        if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
            tm->tm_year >= 1 && tm->tm_year <= 31)
        {
            tm->tm_mday = tm->tm_year;
            *tmask = DTK_M(DAY);
        }

        tm->tm_year = val;
    }

    /* already have year? then could be month */
    else if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
    {
        *tmask = DTK_M(MONTH);
        tm->tm_mon = val;
    }
    /* no year and EuroDates enabled? then could be day */
    else if ((EuroDates || (fmask & DTK_M(MONTH))) &&
             !(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
             val >= 1 && val <= 31)
    {
        *tmask = DTK_M(DAY);
        tm->tm_mday = val;
    }
    else if (!(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
    {
        *tmask = DTK_M(MONTH);
        tm->tm_mon = val;
    }
    else if (!(fmask & DTK_M(DAY)) && val >= 1 && val <= 31)
    {
        *tmask = DTK_M(DAY);
        tm->tm_mday = val;
    }

    /*
     * Check for 2 or 4 or more digits, but currently we reach here only if
     * two digits. - thomas 2000-03-28
     */
    else if (!(fmask & DTK_M(YEAR)) && (flen >= 4 || flen == 2))
    {
        *tmask = DTK_M(YEAR);
        tm->tm_year = val;

        /* adjust ONLY if exactly two digits... */
        *is2digits = (flen == 2);
    }
    else
        return -1;

    return 0;
}   /* DecodeNumber() */

static int DecodeNumberField ( int  len,
char *  str,
int  fmask,
int *  tmask,
struct tm tm,
fsec_t fsec,
int *  is2digits 
) [static]

Definition at line 1162 of file dt_common.c.

References DTK_DATE_M, DTK_TIME_M, MAXDATELEN, and NULL.

Referenced by DecodeDateTime(), and DecodeNumber().

{
    char       *cp;

    /*
     * Have a decimal point? Then this is a date or something with a seconds
     * field...
     */
    if ((cp = strchr(str, '.')) != NULL)
    {
#ifdef HAVE_INT64_TIMESTAMP
        char        fstr[MAXDATELEN + 1];

        /*
         * OK, we have at most six digits to care about. Let's construct a
         * string and then do the conversion to an integer.
         */
        strcpy(fstr, (cp + 1));
        strcpy(fstr + strlen(fstr), "000000");
        *(fstr + 6) = '\0';
        *fsec = strtol(fstr, NULL, 10);
#else
        *fsec = strtod(cp, NULL);
#endif
        *cp = '\0';
        len = strlen(str);
    }
    /* No decimal point and no complete date yet? */
    else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
    {
        /* yyyymmdd? */
        if (len == 8)
        {
            *tmask = DTK_DATE_M;

            tm->tm_mday = atoi(str + 6);
            *(str + 6) = '\0';
            tm->tm_mon = atoi(str + 4);
            *(str + 4) = '\0';
            tm->tm_year = atoi(str + 0);

            return DTK_DATE;
        }
        /* yymmdd? */
        else if (len == 6)
        {
            *tmask = DTK_DATE_M;
            tm->tm_mday = atoi(str + 4);
            *(str + 4) = '\0';
            tm->tm_mon = atoi(str + 2);
            *(str + 2) = '\0';
            tm->tm_year = atoi(str + 0);
            *is2digits = TRUE;

            return DTK_DATE;
        }
        /* yyddd? */
        else if (len == 5)
        {
            *tmask = DTK_DATE_M;
            tm->tm_mday = atoi(str + 2);
            *(str + 2) = '\0';
            tm->tm_mon = 1;
            tm->tm_year = atoi(str + 0);
            *is2digits = TRUE;

            return DTK_DATE;
        }
    }

    /* not all time fields are specified? */
    if ((fmask & DTK_TIME_M) != DTK_TIME_M)
    {
        /* hhmmss */
        if (len == 6)
        {
            *tmask = DTK_TIME_M;
            tm->tm_sec = atoi(str + 4);
            *(str + 4) = '\0';
            tm->tm_min = atoi(str + 2);
            *(str + 2) = '\0';
            tm->tm_hour = atoi(str + 0);

            return DTK_TIME;
        }
        /* hhmm? */
        else if (len == 4)
        {
            *tmask = DTK_TIME_M;
            tm->tm_sec = 0;
            tm->tm_min = atoi(str + 2);
            *(str + 2) = '\0';
            tm->tm_hour = atoi(str + 0);

            return DTK_TIME;
        }
    }

    return -1;
}   /* DecodeNumberField() */

static int DecodePosixTimezone ( char *  str,
int *  tzp 
) [static]

Definition at line 1621 of file dt_common.c.

References DecodeSpecial(), DecodeTimezone(), DTZ, MAXDATEFIELDS, TZ, and val.

Referenced by DecodeDateTime().

{
    int         val,
                tz;
    int         type;
    char       *cp;
    char        delim;

    cp = str;
    while (*cp != '\0' && isalpha((unsigned char) *cp))
        cp++;

    if (DecodeTimezone(cp, &tz) != 0)
        return -1;

    delim = *cp;
    *cp = '\0';
    type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
    *cp = delim;

    switch (type)
    {
        case DTZ:
        case TZ:
            *tzp = (val * MINS_PER_HOUR) - tz;
            break;

        default:
            return -1;
    }

    return 0;
}   /* DecodePosixTimezone() */

static int DecodeSpecial ( int  field,
char *  lowtoken,
int *  val 
) [static]

Definition at line 648 of file dt_common.c.

References datebsearch(), DTZ, DTZMOD, FROMVAL, NULL, szdatetktbl, TOKMAXLEN, datetkn::type, TZ, and datetkn::value.

{
    int         type;
    datetkn    *tp;

    if (datecache[field] != NULL &&
        strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)
        tp = datecache[field];
    else
    {
        tp = NULL;
        if (!tp)
            tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
    }
    datecache[field] = tp;
    if (tp == NULL)
    {
        type = UNKNOWN_FIELD;
        *val = 0;
    }
    else
    {
        type = tp->type;
        switch (type)
        {
            case TZ:
            case DTZ:
            case DTZMOD:
                *val = FROMVAL(tp);
                break;

            default:
                *val = tp->value;
                break;
        }
    }

    return type;
}   /* DecodeSpecial() */

int DecodeTime ( char *  str,
int *  tmask,
struct tm tm,
fsec_t fsec 
)

Definition at line 1507 of file dt_common.c.

References MAXDATELEN, and USECS_PER_SEC.

{
    char       *cp;

    *tmask = DTK_TIME_M;

    tm->tm_hour = strtol(str, &cp, 10);
    if (*cp != ':')
        return -1;
    str = cp + 1;
    tm->tm_min = strtol(str, &cp, 10);
    if (*cp == '\0')
    {
        tm->tm_sec = 0;
        *fsec = 0;
    }
    else if (*cp != ':')
        return -1;
    else
    {
        str = cp + 1;
        tm->tm_sec = strtol(str, &cp, 10);
        if (*cp == '\0')
            *fsec = 0;
        else if (*cp == '.')
        {
#ifdef HAVE_INT64_TIMESTAMP
            char        fstr[MAXDATELEN + 1];

            /*
             * OK, we have at most six digits to work with. Let's construct a
             * string and then do the conversion to an integer.
             */
            strncpy(fstr, (cp + 1), 7);
            strcpy(fstr + strlen(fstr), "000000");
            *(fstr + 6) = '\0';
            *fsec = strtol(fstr, &cp, 10);
#else
            str = cp;
            *fsec = strtod(str, &cp);
#endif
            if (*cp != '\0')
                return -1;
        }
        else
            return -1;
    }

    /* do a sanity check */
#ifdef HAVE_INT64_TIMESTAMP
    if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
        tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
        return -1;
#else
    if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
        tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= 1)
        return -1;
#endif

    return 0;
}   /* DecodeTime() */

static int DecodeTimezone ( char *  str,
int *  tzp 
) [static]

Definition at line 1576 of file dt_common.c.

References MINS_PER_HOUR, and SECS_PER_MINUTE.

Referenced by DecodeDateTime(), DecodePosixTimezone(), and PGTYPEStimestamp_defmt_scan().

{
    int         tz;
    int         hr,
                min;
    char       *cp;
    int         len;

    /* assume leading character is "+" or "-" */
    hr = strtol(str + 1, &cp, 10);

    /* explicit delimiter? */
    if (*cp == ':')
        min = strtol(cp + 1, &cp, 10);
    /* otherwise, might have run things together... */
    else if (*cp == '\0' && (len = strlen(str)) > 3)
    {
        min = strtol(str + len - 2, &cp, 10);
        if (min < 0 || min >= 60)
            return -1;

        *(str + len - 2) = '\0';
        hr = strtol(str + 1, &cp, 10);
        if (hr < 0 || hr > 13)
            return -1;
    }
    else
        min = 0;

    tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE;
    if (*str == '-')
        tz = -tz;

    *tzp = -tz;
    return *cp != '\0';
}   /* DecodeTimezone() */

int DecodeUnits ( int  field,
char *  lowtoken,
int *  val 
)

Definition at line 545 of file dt_common.c.

References datebsearch(), DTZ, FROMVAL, NULL, szdeltatktbl, datetkn::token, TOKMAXLEN, datetkn::type, TZ, and datetkn::value.

{
    int         type;
    datetkn    *tp;

    if (deltacache[field] != NULL &&
        strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)
        tp = deltacache[field];
    else
        tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
    deltacache[field] = tp;
    if (tp == NULL)
    {
        type = UNKNOWN_FIELD;
        *val = 0;
    }
    else
    {
        type = tp->type;
        if (type == TZ || type == DTZ)
            *val = FROMVAL(tp);
        else
            *val = tp->value;
    }

    return type;
}   /* DecodeUnits() */

void dt2time ( double  jd,
int *  hour,
int *  min,
int *  sec,
fsec_t fsec 
)

Definition at line 1128 of file dt_common.c.

{
#ifdef HAVE_INT64_TIMESTAMP
    int64       time;
#else
    double      time;
#endif

    time = jd;
#ifdef HAVE_INT64_TIMESTAMP
    *hour = time / USECS_PER_HOUR;
    time -= (*hour) * USECS_PER_HOUR;
    *min = time / USECS_PER_MINUTE;
    time -= (*min) * USECS_PER_MINUTE;
    *sec = time / USECS_PER_SEC;
    *fsec = time - (*sec * USECS_PER_SEC);
#else
    *hour = time / SECS_PER_HOUR;
    time -= (*hour) * SECS_PER_HOUR;
    *min = time / SECS_PER_MINUTE;
    time -= (*min) * SECS_PER_MINUTE;
    *sec = time;
    *fsec = time - *sec;
#endif
}   /* dt2time() */

int EncodeDateOnly ( struct tm tm,
int  style,
char *  str,
bool  EuroDates 
)

Definition at line 692 of file dt_common.c.

References MONTHS_PER_YEAR, USE_GERMAN_DATES, USE_ISO_DATES, USE_POSTGRES_DATES, and USE_SQL_DATES.

{
    if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
        return -1;

    switch (style)
    {
        case USE_ISO_DATES:
            /* compatible with ISO date formats */
            if (tm->tm_year > 0)
                sprintf(str, "%04d-%02d-%02d",
                        tm->tm_year, tm->tm_mon, tm->tm_mday);
            else
                sprintf(str, "%04d-%02d-%02d %s",
                        -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
            break;

        case USE_SQL_DATES:
            /* compatible with Oracle/Ingres date formats */
            if (EuroDates)
                sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
            else
                sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
            if (tm->tm_year > 0)
                sprintf(str + 5, "/%04d", tm->tm_year);
            else
                sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
            break;

        case USE_GERMAN_DATES:
            /* German-style date format */
            sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
            if (tm->tm_year > 0)
                sprintf(str + 5, ".%04d", tm->tm_year);
            else
                sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
            break;

        case USE_POSTGRES_DATES:
        default:
            /* traditional date-only style for Postgres */
            if (EuroDates)
                sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
            else
                sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
            if (tm->tm_year > 0)
                sprintf(str + 5, "-%04d", tm->tm_year);
            else
                sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
            break;
    }

    return TRUE;
}   /* EncodeDateOnly() */

int EncodeDateTime ( struct tm tm,
fsec_t  fsec,
bool  print_tz,
int  tz,
const char *  tzn,
int  style,
char *  str,
bool  EuroDates 
)

Definition at line 779 of file dt_common.c.

References date2j(), days, MAXTZLEN, MINS_PER_HOUR, months, TrimTrailingZeros(), USE_GERMAN_DATES, USE_ISO_DATES, USE_POSTGRES_DATES, and USE_SQL_DATES.

{
    int         day,
                hour,
                min;

    /*
     * Negative tm_isdst means we have no valid time zone translation.
     */
    if (tm->tm_isdst < 0)
        print_tz = false;

    switch (style)
    {
        case USE_ISO_DATES:
            /* Compatible with ISO-8601 date formats */

            sprintf(str, "%04d-%02d-%02d %02d:%02d",
                    (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
                    tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);

            /*
             * Print fractional seconds if any.  The field widths here should
             * be at least equal to MAX_TIMESTAMP_PRECISION.
             *
             * In float mode, don't print fractional seconds before 1 AD,
             * since it's unlikely there's any precision left ...
             */
#ifdef HAVE_INT64_TIMESTAMP
            if (fsec != 0)
            {
                sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
#else
            if ((fsec != 0) && (tm->tm_year > 0))
            {
                sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
#endif
                TrimTrailingZeros(str);
            }
            else
                sprintf(str + strlen(str), ":%02d", tm->tm_sec);

            if (tm->tm_year <= 0)
                sprintf(str + strlen(str), " BC");

            if (print_tz)
            {
                hour = -(tz / SECS_PER_HOUR);
                min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
                if (min != 0)
                    sprintf(str + strlen(str), "%+03d:%02d", hour, min);
                else
                    sprintf(str + strlen(str), "%+03d", hour);
            }
            break;

        case USE_SQL_DATES:
            /* Compatible with Oracle/Ingres date formats */

            if (EuroDates)
                sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
            else
                sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);

            sprintf(str + 5, "/%04d %02d:%02d",
                    (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
                    tm->tm_hour, tm->tm_min);

            /*
             * Print fractional seconds if any.  The field widths here should
             * be at least equal to MAX_TIMESTAMP_PRECISION.
             *
             * In float mode, don't print fractional seconds before 1 AD,
             * since it's unlikely there's any precision left ...
             */
#ifdef HAVE_INT64_TIMESTAMP
            if (fsec != 0)
            {
                sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
#else
            if (fsec != 0 && tm->tm_year > 0)
            {
                sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
#endif
                TrimTrailingZeros(str);
            }
            else
                sprintf(str + strlen(str), ":%02d", tm->tm_sec);

            if (tm->tm_year <= 0)
                sprintf(str + strlen(str), " BC");

            /*
             * Note: the uses of %.*s in this function would be risky if the
             * timezone names ever contain non-ASCII characters.  However, all
             * TZ abbreviations in the Olson database are plain ASCII.
             */

            if (print_tz)
            {
                if (tzn)
                    sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
                else
                {
                    hour = -(tz / SECS_PER_HOUR);
                    min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
                    if (min != 0)
                        sprintf(str + strlen(str), "%+03d:%02d", hour, min);
                    else
                        sprintf(str + strlen(str), "%+03d", hour);
                }
            }
            break;

        case USE_GERMAN_DATES:
            /* German variant on European style */

            sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);

            sprintf(str + 5, ".%04d %02d:%02d",
                    (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
                    tm->tm_hour, tm->tm_min);

            /*
             * Print fractional seconds if any.  The field widths here should
             * be at least equal to MAX_TIMESTAMP_PRECISION.
             *
             * In float mode, don't print fractional seconds before 1 AD,
             * since it's unlikely there's any precision left ...
             */
#ifdef HAVE_INT64_TIMESTAMP
            if (fsec != 0)
            {
                sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
#else
            if (fsec != 0 && tm->tm_year > 0)
            {
                sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
#endif
                TrimTrailingZeros(str);
            }
            else
                sprintf(str + strlen(str), ":%02d", tm->tm_sec);

            if (tm->tm_year <= 0)
                sprintf(str + strlen(str), " BC");

            if (print_tz)
            {
                if (tzn)
                    sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
                else
                {
                    hour = -(tz / SECS_PER_HOUR);
                    min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
                    if (min != 0)
                        sprintf(str + strlen(str), "%+03d:%02d", hour, min);
                    else
                        sprintf(str + strlen(str), "%+03d", hour);
                }
            }
            break;

        case USE_POSTGRES_DATES:
        default:
            /* Backward-compatible with traditional Postgres abstime dates */

            day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
            tm->tm_wday = (int) ((day + date2j(2000, 1, 1) + 1) % 7);

            strncpy(str, days[tm->tm_wday], 3);
            strcpy(str + 3, " ");

            if (EuroDates)
                sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
            else
                sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);

            sprintf(str + 10, " %02d:%02d", tm->tm_hour, tm->tm_min);

            /*
             * Print fractional seconds if any.  The field widths here should
             * be at least equal to MAX_TIMESTAMP_PRECISION.
             *
             * In float mode, don't print fractional seconds before 1 AD,
             * since it's unlikely there's any precision left ...
             */
#ifdef HAVE_INT64_TIMESTAMP
            if (fsec != 0)
            {
                sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
#else
            if (fsec != 0 && tm->tm_year > 0)
            {
                sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
#endif
                TrimTrailingZeros(str);
            }
            else
                sprintf(str + strlen(str), ":%02d", tm->tm_sec);

            sprintf(str + strlen(str), " %04d",
                    (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
            if (tm->tm_year <= 0)
                sprintf(str + strlen(str), " BC");

            if (print_tz)
            {
                if (tzn)
                    sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
                else
                {
                    /*
                     * We have a time zone, but no string version. Use the
                     * numeric form, but be sure to include a leading space to
                     * avoid formatting something which would be rejected by
                     * the date/time parser later. - thomas 2001-10-19
                     */
                    hour = -(tz / SECS_PER_HOUR);
                    min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
                    if (min != 0)
                        sprintf(str + strlen(str), " %+03d:%02d", hour, min);
                    else
                        sprintf(str + strlen(str), " %+03d", hour);
                }
            }
            break;
    }

    return TRUE;
}   /* EncodeDateTime() */

static char* find_end_token ( char *  str,
char *  fmt 
) [static]

Definition at line 2432 of file dt_common.c.

Referenced by pgtypes_defmt_scan().

{
    /*
     * str: here is28the day12the hour fmt: here is%dthe day%hthe hour
     *
     * we extract the 28, we read the percent sign and the type "d" then this
     * functions gets called as find_end_token("28the day12the hour", "the
     * day%hthehour")
     *
     * fmt points to "the day%hthehour", next_percent points to %hthehour and
     * we have to find a match for everything between these positions ("the
     * day"). We look for "the day" in str and know that the pattern we are
     * about to scan ends where this string starts (right after the "28")
     *
     * At the end, *fmt is '\0' and *str isn't. end_position then is
     * unchanged.
     */
    char       *end_position = NULL;
    char       *next_percent,
               *subst_location = NULL;
    int         scan_offset = 0;
    char        last_char;

    /* are we at the end? */
    if (!*fmt)
    {
        end_position = fmt;
        return end_position;
    }

    /* not at the end */
    while (fmt[scan_offset] == '%' && fmt[scan_offset + 1])
    {
        /*
         * there is no delimiter, skip to the next delimiter if we're reading
         * a number and then something that is not a number "9:15pm", we might
         * be able to recover with the strtol end pointer. Go for the next
         * percent sign
         */
        scan_offset += 2;
    }
    next_percent = strchr(fmt + scan_offset, '%');
    if (next_percent)
    {
        /*
         * we don't want to allocate extra memory, so we temporarily set the
         * '%' sign to '\0' and call strstr However since we allow whitespace
         * to float around everything, we have to shorten the pattern until we
         * reach a non-whitespace character
         */

        subst_location = next_percent;
        while (*(subst_location - 1) == ' ' && subst_location - 1 > fmt + scan_offset)
            subst_location--;
        last_char = *subst_location;
        *subst_location = '\0';

        /*
         * the haystack is the str and the needle is the original fmt but it
         * ends at the position where the next percent sign would be
         */

        /*
         * There is one special case. Imagine: str = " 2", fmt = "%d %...",
         * since we want to allow blanks as "dynamic" padding we have to
         * accept this. Now, we are called with a fmt of " %..." and look for
         * " " in str. We find it at the first position and never read the
         * 2...
         */
        while (*str == ' ')
            str++;
        end_position = strstr(str, fmt + scan_offset);
        *subst_location = last_char;
    }
    else
    {
        /*
         * there is no other percent sign. So everything up to the end has to
         * match.
         */
        end_position = str + strlen(str);
    }
    if (!end_position)
    {
        /*
         * maybe we have the following case:
         *
         * str = "4:15am" fmt = "%M:%S %p"
         *
         * at this place we could have
         *
         * str = "15am" fmt = " %p"
         *
         * and have set fmt to " " because overwrote the % sign with a NULL
         *
         * In this case where we would have to match a space but can't find
         * it, set end_position to the end of the string
         */
        if ((fmt + scan_offset)[0] == ' ' && fmt + scan_offset + 1 == subst_location)
            end_position = str + strlen(str);
    }
    return end_position;
}

void GetCurrentDateTime ( struct tm tm  ) 

Definition at line 1120 of file dt_common.c.

References abstime2tm(), and NULL.

{
    int         tz;

    abstime2tm(time(NULL), &tz, tm, NULL);
}

int GetEpochTime ( struct tm tm  ) 

Definition at line 1012 of file dt_common.c.

References epoch.

{
    struct tm  *t0;
    time_t      epoch = 0;

    t0 = gmtime(&epoch);

    if (t0)
    {
        tm->tm_year = t0->tm_year + 1900;
        tm->tm_mon = t0->tm_mon + 1;
        tm->tm_mday = t0->tm_mday;
        tm->tm_hour = t0->tm_hour;
        tm->tm_min = t0->tm_min;
        tm->tm_sec = t0->tm_sec;

        return 0;
    }

    return -1;
}   /* GetEpochTime() */

void j2date ( int  jd,
int *  year,
int *  month,
int *  day 
)

Definition at line 617 of file dt_common.c.

References MONTHS_PER_YEAR.

{
    unsigned int julian;
    unsigned int quad;
    unsigned int extra;
    int         y;

    julian = jd;
    julian += 32044;
    quad = julian / 146097;
    extra = (julian - quad * 146097) * 4 + 3;
    julian += 60 + quad * 3 + extra / 146097;
    quad = julian / 1461;
    julian -= quad * 1461;
    y = julian * 4 / 1461;
    julian = ((y != 0) ? (julian + 305) % 365 : (julian + 306) % 366) + 123;
    y += quad * 4;
    *year = y - 4800;
    quad = julian * 2141 / 65536;
    *day = julian - 7834 * quad / 256;
    *month = (quad + 10) % 12 + 1;

    return;
}   /* j2date() */

int ParseDateTime ( char *  timestr,
char *  lowstr,
char **  field,
int *  ftype,
int *  numfields,
char **  endstr 
)

Definition at line 1670 of file dt_common.c.

References DTK_DATE, DTK_NUMBER, MAXDATEFIELDS, and pg_tolower().

{
    int         nf = 0;
    char       *lp = lowstr;

    *endstr = timestr;
    /* outer loop through fields */
    while (*(*endstr) != '\0')
    {
        field[nf] = lp;

        /* leading digit? then date or time */
        if (isdigit((unsigned char) *(*endstr)))
        {
            *lp++ = *(*endstr)++;
            while (isdigit((unsigned char) *(*endstr)))
                *lp++ = *(*endstr)++;

            /* time field? */
            if (*(*endstr) == ':')
            {
                ftype[nf] = DTK_TIME;
                *lp++ = *(*endstr)++;
                while (isdigit((unsigned char) *(*endstr)) ||
                       (*(*endstr) == ':') || (*(*endstr) == '.'))
                    *lp++ = *(*endstr)++;
            }
            /* date field? allow embedded text month */
            else if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
            {
                /* save delimiting character to use later */
                char       *dp = (*endstr);

                *lp++ = *(*endstr)++;
                /* second field is all digits? then no embedded text month */
                if (isdigit((unsigned char) *(*endstr)))
                {
                    ftype[nf] = (*dp == '.') ? DTK_NUMBER : DTK_DATE;
                    while (isdigit((unsigned char) *(*endstr)))
                        *lp++ = *(*endstr)++;

                    /*
                     * insist that the delimiters match to get a three-field
                     * date.
                     */
                    if (*(*endstr) == *dp)
                    {
                        ftype[nf] = DTK_DATE;
                        *lp++ = *(*endstr)++;
                        while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
                            *lp++ = *(*endstr)++;
                    }
                }
                else
                {
                    ftype[nf] = DTK_DATE;
                    while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
                        *lp++ = pg_tolower((unsigned char) *(*endstr)++);
                }
            }

            /*
             * otherwise, number only and will determine year, month, day, or
             * concatenated fields later...
             */
            else
                ftype[nf] = DTK_NUMBER;
        }
        /* Leading decimal point? Then fractional seconds... */
        else if (*(*endstr) == '.')
        {
            *lp++ = *(*endstr)++;
            while (isdigit((unsigned char) *(*endstr)))
                *lp++ = *(*endstr)++;

            ftype[nf] = DTK_NUMBER;
        }

        /*
         * text? then date string, month, day of week, special, or timezone
         */
        else if (isalpha((unsigned char) *(*endstr)))
        {
            ftype[nf] = DTK_STRING;
            *lp++ = pg_tolower((unsigned char) *(*endstr)++);
            while (isalpha((unsigned char) *(*endstr)))
                *lp++ = pg_tolower((unsigned char) *(*endstr)++);

            /*
             * Full date string with leading text month? Could also be a POSIX
             * time zone...
             */
            if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
            {
                char       *dp = (*endstr);

                ftype[nf] = DTK_DATE;
                *lp++ = *(*endstr)++;
                while (isdigit((unsigned char) *(*endstr)) || *(*endstr) == *dp)
                    *lp++ = *(*endstr)++;
            }
        }
        /* skip leading spaces */
        else if (isspace((unsigned char) *(*endstr)))
        {
            (*endstr)++;
            continue;
        }
        /* sign? then special or numeric timezone */
        else if (*(*endstr) == '+' || *(*endstr) == '-')
        {
            *lp++ = *(*endstr)++;
            /* soak up leading whitespace */
            while (isspace((unsigned char) *(*endstr)))
                (*endstr)++;
            /* numeric timezone? */
            if (isdigit((unsigned char) *(*endstr)))
            {
                ftype[nf] = DTK_TZ;
                *lp++ = *(*endstr)++;
                while (isdigit((unsigned char) *(*endstr)) ||
                       (*(*endstr) == ':') || (*(*endstr) == '.'))
                    *lp++ = *(*endstr)++;
            }
            /* special? */
            else if (isalpha((unsigned char) *(*endstr)))
            {
                ftype[nf] = DTK_SPECIAL;
                *lp++ = pg_tolower((unsigned char) *(*endstr)++);
                while (isalpha((unsigned char) *(*endstr)))
                    *lp++ = pg_tolower((unsigned char) *(*endstr)++);
            }
            /* otherwise something wrong... */
            else
                return -1;
        }
        /* ignore punctuation but use as delimiter */
        else if (ispunct((unsigned char) *(*endstr)))
        {
            (*endstr)++;
            continue;

        }
        /* otherwise, something is not right... */
        else
            return -1;

        /* force in a delimiter after each field */
        *lp++ = '\0';
        nf++;
        if (nf > MAXDATEFIELDS)
            return -1;
    }

    *numfields = nf;

    return 0;
}   /* ParseDateTime() */

static int pgtypes_defmt_scan ( union un_fmt_comb scan_val,
int  scan_type,
char **  pstr,
char *  pfmt 
) [static]

Definition at line 2537 of file dt_common.c.

References find_end_token(), un_fmt_comb::luint_val, NULL, pgtypes_strdup(), PGTYPES_TYPE_STRING_MALLOCED, PGTYPES_TYPE_UINT, PGTYPES_TYPE_UINT_LONG, un_fmt_comb::str_val, and un_fmt_comb::uint_val.

Referenced by PGTYPEStimestamp_defmt_scan().

{
    /*
     * scan everything between pstr and pstr_end. This is not including the
     * last character so we might set it to '\0' for the parsing
     */

    char        last_char;
    int         err = 0;
    char       *pstr_end;
    char       *strtol_end = NULL;

    while (**pstr == ' ')
        pstr++;
    pstr_end = find_end_token(*pstr, pfmt);
    if (!pstr_end)
    {
        /* there was an error, no match */
        return 1;
    }
    last_char = *pstr_end;
    *pstr_end = '\0';

    switch (scan_type)
    {
        case PGTYPES_TYPE_UINT:

            /*
             * numbers may be blank-padded, this is the only deviation from
             * the fmt-string we accept
             */
            while (**pstr == ' ')
                (*pstr)++;
            errno = 0;
            scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
            if (errno)
                err = 1;
            break;
        case PGTYPES_TYPE_UINT_LONG:
            while (**pstr == ' ')
                (*pstr)++;
            errno = 0;
            scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
            if (errno)
                err = 1;
            break;
        case PGTYPES_TYPE_STRING_MALLOCED:
            scan_val->str_val = pgtypes_strdup(*pstr);
            if (scan_val->str_val == NULL)
                err = 1;
            break;
    }
    if (strtol_end && *strtol_end)
        *pstr = strtol_end;
    else
        *pstr = pstr_end;
    *pstr_end = last_char;
    return err;
}

int PGTYPEStimestamp_defmt_scan ( char **  str,
char *  fmt,
timestamp d,
int *  year,
int *  month,
int *  day,
int *  hour,
int *  minute,
int *  second,
int *  tz 
)

Definition at line 2602 of file dt_common.c.

References day_tab, days, DecodeTimezone(), free, isleap, un_fmt_comb::luint_val, MINS_PER_HOUR, months, MONTHS_PER_YEAR, pg_strcasecmp(), pgtypes_alloc(), pgtypes_date_months, pgtypes_date_weekdays_short, pgtypes_defmt_scan(), un_fmt_comb::str_val, szdatetktbl, tm2timestamp(), un_fmt_comb::uint_val, and datetkn::value.

Referenced by PGTYPEStimestamp_defmt_asc().

{
    union un_fmt_comb scan_val;
    int         scan_type;

    char       *pstr,
               *pfmt,
               *tmp;
    int         err = 1;
    unsigned int j;
    struct tm   tm;

    pfmt = fmt;
    pstr = *str;

    while (*pfmt)
    {
        err = 0;
        while (*pfmt == ' ')
            pfmt++;
        while (*pstr == ' ')
            pstr++;
        if (*pfmt != '%')
        {
            if (*pfmt == *pstr)
            {
                pfmt++;
                pstr++;
            }
            else
            {
                /* Error: no match */
                err = 1;
                return err;
            }
            continue;
        }
        /* here *pfmt equals '%' */
        pfmt++;
        switch (*pfmt)
        {
            case 'a':
                pfmt++;

                /*
                 * we parse the day and see if it is a week day but we do not
                 * check if the week day really matches the date
                 */
                err = 1;
                j = 0;
                while (pgtypes_date_weekdays_short[j])
                {
                    if (strncmp(pgtypes_date_weekdays_short[j], pstr,
                                strlen(pgtypes_date_weekdays_short[j])) == 0)
                    {
                        /* found it */
                        err = 0;
                        pstr += strlen(pgtypes_date_weekdays_short[j]);
                        break;
                    }
                    j++;
                }
                break;
            case 'A':
                /* see note above */
                pfmt++;
                err = 1;
                j = 0;
                while (days[j])
                {
                    if (strncmp(days[j], pstr, strlen(days[j])) == 0)
                    {
                        /* found it */
                        err = 0;
                        pstr += strlen(days[j]);
                        break;
                    }
                    j++;
                }
                break;
            case 'b':
            case 'h':
                pfmt++;
                err = 1;
                j = 0;
                while (months[j])
                {
                    if (strncmp(months[j], pstr, strlen(months[j])) == 0)
                    {
                        /* found it */
                        err = 0;
                        pstr += strlen(months[j]);
                        *month = j + 1;
                        break;
                    }
                    j++;
                }
                break;
            case 'B':
                /* see note above */
                pfmt++;
                err = 1;
                j = 0;
                while (pgtypes_date_months[j])
                {
                    if (strncmp(pgtypes_date_months[j], pstr, strlen(pgtypes_date_months[j])) == 0)
                    {
                        /* found it */
                        err = 0;
                        pstr += strlen(pgtypes_date_months[j]);
                        *month = j + 1;
                        break;
                    }
                    j++;
                }
                break;
            case 'c':
                /* XXX */
                break;
            case 'C':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *year = scan_val.uint_val * 100;
                break;
            case 'd':
            case 'e':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *day = scan_val.uint_val;
                break;
            case 'D':

                /*
                 * we have to concatenate the strings in order to be able to
                 * find the end of the substitution
                 */
                pfmt++;
                tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
                strcpy(tmp, "%m/%d/%y");
                strcat(tmp, pfmt);
                err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
                free(tmp);
                return err;
            case 'm':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *month = scan_val.uint_val;
                break;
            case 'y':
            case 'g':           /* XXX difference to y (ISO) */
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                if (*year < 0)
                {
                    /* not yet set */
                    *year = scan_val.uint_val;
                }
                else
                    *year += scan_val.uint_val;
                if (*year < 100)
                    *year += 1900;
                break;
            case 'G':
                /* XXX difference to %V (ISO) */
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *year = scan_val.uint_val;
                break;
            case 'H':
            case 'I':
            case 'k':
            case 'l':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *hour += scan_val.uint_val;
                break;
            case 'j':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);

                /*
                 * XXX what should we do with that? We could say that it's
                 * sufficient if we have the year and the day within the year
                 * to get at least a specific day.
                 */
                break;
            case 'M':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *minute = scan_val.uint_val;
                break;
            case 'n':
                pfmt++;
                if (*pstr == '\n')
                    pstr++;
                else
                    err = 1;
                break;
            case 'p':
                err = 1;
                pfmt++;
                if (strncmp(pstr, "am", 2) == 0)
                {
                    *hour += 0;
                    err = 0;
                    pstr += 2;
                }
                if (strncmp(pstr, "a.m.", 4) == 0)
                {
                    *hour += 0;
                    err = 0;
                    pstr += 4;
                }
                if (strncmp(pstr, "pm", 2) == 0)
                {
                    *hour += 12;
                    err = 0;
                    pstr += 2;
                }
                if (strncmp(pstr, "p.m.", 4) == 0)
                {
                    *hour += 12;
                    err = 0;
                    pstr += 4;
                }
                break;
            case 'P':
                err = 1;
                pfmt++;
                if (strncmp(pstr, "AM", 2) == 0)
                {
                    *hour += 0;
                    err = 0;
                    pstr += 2;
                }
                if (strncmp(pstr, "A.M.", 4) == 0)
                {
                    *hour += 0;
                    err = 0;
                    pstr += 4;
                }
                if (strncmp(pstr, "PM", 2) == 0)
                {
                    *hour += 12;
                    err = 0;
                    pstr += 2;
                }
                if (strncmp(pstr, "P.M.", 4) == 0)
                {
                    *hour += 12;
                    err = 0;
                    pstr += 4;
                }
                break;
            case 'r':
                pfmt++;
                tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
                strcpy(tmp, "%I:%M:%S %p");
                strcat(tmp, pfmt);
                err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
                free(tmp);
                return err;
            case 'R':
                pfmt++;
                tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
                strcpy(tmp, "%H:%M");
                strcat(tmp, pfmt);
                err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
                free(tmp);
                return err;
            case 's':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT_LONG;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                /* number of seconds in scan_val.luint_val */
                {
                    struct tm  *tms;
                    time_t      et = (time_t) scan_val.luint_val;

                    tms = gmtime(&et);

                    if (tms)
                    {
                        *year = tms->tm_year + 1900;
                        *month = tms->tm_mon + 1;
                        *day = tms->tm_mday;
                        *hour = tms->tm_hour;
                        *minute = tms->tm_min;
                        *second = tms->tm_sec;
                    }
                    else
                        err = 1;
                }
                break;
            case 'S':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *second = scan_val.uint_val;
                break;
            case 't':
                pfmt++;
                if (*pstr == '\t')
                    pstr++;
                else
                    err = 1;
                break;
            case 'T':
                pfmt++;
                tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
                strcpy(tmp, "%H:%M:%S");
                strcat(tmp, pfmt);
                err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
                free(tmp);
                return err;
            case 'u':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                if (scan_val.uint_val < 1 || scan_val.uint_val > 7)
                    err = 1;
                break;
            case 'U':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                if (scan_val.uint_val > 53)
                    err = 1;
                break;
            case 'V':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                if (scan_val.uint_val < 1 || scan_val.uint_val > 53)
                    err = 1;
                break;
            case 'w':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                if (scan_val.uint_val > 6)
                    err = 1;
                break;
            case 'W':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                if (scan_val.uint_val > 53)
                    err = 1;
                break;
            case 'x':
            case 'X':
                /* XXX */
                break;
            case 'Y':
                pfmt++;
                scan_type = PGTYPES_TYPE_UINT;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                *year = scan_val.uint_val;
                break;
            case 'z':
                pfmt++;
                scan_type = PGTYPES_TYPE_STRING_MALLOCED;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
                if (!err)
                {
                    err = DecodeTimezone(scan_val.str_val, tz);
                    free(scan_val.str_val);
                }
                break;
            case 'Z':
                pfmt++;
                scan_type = PGTYPES_TYPE_STRING_MALLOCED;
                err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);

                /*
                 * XXX use DecodeSpecial instead ? - it's declared static but
                 * the arrays as well. :-(
                 */
                for (j = 0; !err && j < szdatetktbl; j++)
                {
                    if (pg_strcasecmp(datetktbl[j].token, scan_val.str_val) == 0)
                    {
                        /*
                         * tz calculates the offset for the seconds, the
                         * timezone value of the datetktbl table is in quarter
                         * hours
                         */
                        *tz = -15 * MINS_PER_HOUR * datetktbl[j].value;
                        break;
                    }
                }
                free(scan_val.str_val);
                break;
            case '+':
                /* XXX */
                break;
            case '%':
                pfmt++;
                if (*pstr == '%')
                    pstr++;
                else
                    err = 1;
                break;
            default:
                err = 1;
        }
    }
    if (!err)
    {
        if (*second < 0)
            *second = 0;
        if (*minute < 0)
            *minute = 0;
        if (*hour < 0)
            *hour = 0;
        if (*day < 0)
        {
            err = 1;
            *day = 1;
        }
        if (*month < 0)
        {
            err = 1;
            *month = 1;
        }
        if (*year < 0)
        {
            err = 1;
            *year = 1970;
        }

        if (*second > 59)
        {
            err = 1;
            *second = 0;
        }
        if (*minute > 59)
        {
            err = 1;
            *minute = 0;
        }
        if (*hour > 24 ||       /* test for > 24:00:00 */
            (*hour == 24 && (*minute > 0 || *second > 0)))
        {
            err = 1;
            *hour = 0;
        }
        if (*month > MONTHS_PER_YEAR)
        {
            err = 1;
            *month = 1;
        }
        if (*day > day_tab[isleap(*year)][*month - 1])
        {
            *day = day_tab[isleap(*year)][*month - 1];
            err = 1;
        }

        tm.tm_sec = *second;
        tm.tm_min = *minute;
        tm.tm_hour = *hour;
        tm.tm_mday = *day;
        tm.tm_mon = *month;
        tm.tm_year = *year;

        tm2timestamp(&tm, 0, tz, d);
    }
    return err;
}

void TrimTrailingZeros ( char *  str  ) 

Definition at line 748 of file dt_common.c.

{
    int         len = strlen(str);

    /* chop off trailing zeros... but leave at least 2 fractional digits */
    while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
    {
        len--;
        *(str + len) = '\0';
    }
}


Variable Documentation

datetkn* datecache[MAXDATEFIELDS] = {NULL} [static]

Definition at line 500 of file dt_common.c.

datetkn datetktbl[] [static]

Definition at line 26 of file dt_common.c.

int day_tab[2][13]
Initial value:
 {
    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}}

Definition at line 13 of file dt_common.c.

char* days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL}

Definition at line 506 of file dt_common.c.

datetkn* deltacache[MAXDATEFIELDS] = {NULL} [static]

Definition at line 502 of file dt_common.c.

datetkn deltatktbl[] [static]

Definition at line 430 of file dt_common.c.

char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}

Definition at line 504 of file dt_common.c.

char* pgtypes_date_months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL}
char* pgtypes_date_weekdays_short[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL}
const unsigned int szdatetktbl = lengthof(datetktbl) [static]

Definition at line 497 of file dt_common.c.

Referenced by DecodeSpecial(), and PGTYPEStimestamp_defmt_scan().

const unsigned int szdeltatktbl = lengthof(deltatktbl) [static]

Definition at line 498 of file dt_common.c.

Referenced by DecodeUnits().