Header And Logo

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

Defines | Functions

datetime.c File Reference

#include "postgres_fe.h"
#include <time.h>
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include "extern.h"
#include "dt.h"
#include "pgtypes_error.h"
#include "pgtypes_date.h"
Include dependency graph for datetime.c:

Go to the source code of this file.

Defines

#define PGTYPES_DATE_NUM_MAX_DIGITS   20
#define PGTYPES_FMTDATE_DAY_DIGITS_LZ   1
#define PGTYPES_FMTDATE_DOW_LITERAL_SHORT   2
#define PGTYPES_FMTDATE_MONTH_DIGITS_LZ   3
#define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT   4
#define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT   5
#define PGTYPES_FMTDATE_YEAR_DIGITS_LONG   6
#define PGTYPES_DATE_MONTH_MAXLENGTH   20

Functions

datePGTYPESdate_new (void)
void PGTYPESdate_free (date *d)
date PGTYPESdate_from_timestamp (timestamp dt)
date PGTYPESdate_from_asc (char *str, char **endptr)
char * PGTYPESdate_to_asc (date dDate)
void PGTYPESdate_julmdy (date jd, int *mdy)
void PGTYPESdate_mdyjul (int *mdy, date *jdate)
int PGTYPESdate_dayofweek (date dDate)
void PGTYPESdate_today (date *d)
int PGTYPESdate_fmt_asc (date dDate, const char *fmtstring, char *outbuf)
int PGTYPESdate_defmt_asc (date *d, const char *fmt, char *str)

Define Documentation

#define PGTYPES_DATE_MONTH_MAXLENGTH   20

Definition at line 336 of file datetime.c.

Referenced by PGTYPESdate_defmt_asc().

#define PGTYPES_DATE_NUM_MAX_DIGITS   20

Definition at line 165 of file datetime.c.

Referenced by PGTYPESdate_fmt_asc().

#define PGTYPES_FMTDATE_DAY_DIGITS_LZ   1

Definition at line 168 of file datetime.c.

Referenced by PGTYPESdate_fmt_asc().

#define PGTYPES_FMTDATE_DOW_LITERAL_SHORT   2

Definition at line 169 of file datetime.c.

Referenced by PGTYPESdate_fmt_asc().

#define PGTYPES_FMTDATE_MONTH_DIGITS_LZ   3

Definition at line 170 of file datetime.c.

Referenced by PGTYPESdate_fmt_asc().

#define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT   4

Definition at line 171 of file datetime.c.

Referenced by PGTYPESdate_fmt_asc().

#define PGTYPES_FMTDATE_YEAR_DIGITS_LONG   6

Definition at line 173 of file datetime.c.

Referenced by PGTYPESdate_fmt_asc().

#define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT   5

Definition at line 172 of file datetime.c.

Referenced by PGTYPESdate_fmt_asc().


Function Documentation

int PGTYPESdate_dayofweek ( date  dDate  ) 

Definition at line 145 of file datetime.c.

References date2j().

Referenced by main(), PGTYPESdate_fmt_asc(), PGTYPEStimestamp_fmt_asc(), and rdayofweek().

{
    /*
     * Sunday:  0 Monday:      1 Tuesday:     2 Wednesday:   3 Thursday: 4
     * Friday:      5 Saturday:    6
     */
    return (int) (dDate + date2j(2000, 1, 1) + 1) % 7;
}

int PGTYPESdate_defmt_asc ( date d,
const char *  fmt,
char *  str 
)

Definition at line 338 of file datetime.c.

References date2j(), free, i, sort-test::list, months, MONTHS_PER_YEAR, NULL, pg_tolower(), pgtypes_alloc(), PGTYPES_DATE_MONTH_MAXLENGTH, pgtypes_date_months, pgtypes_strdup(), tm, and pg_tm::tm_year.

Referenced by main(), and rdefmtdate().

{
    /*
     * token[2] = { 4,6 } means that token 2 starts at position 4 and ends at
     * (including) position 6
     */
    int         token[3][2];
    int         token_values[3] = {-1, -1, -1};
    char       *fmt_token_order;
    char       *fmt_ystart,
               *fmt_mstart,
               *fmt_dstart;
    unsigned int i;
    int         reading_digit;
    int         token_count;
    char       *str_copy;
    struct tm   tm;

    tm.tm_year = tm.tm_mon = tm.tm_mday = 0;    /* keep compiler quiet */

    if (!d || !str || !fmt)
    {
        errno = PGTYPES_DATE_ERR_EARGS;
        return -1;
    }

    /* analyze the fmt string */
    fmt_ystart = strstr(fmt, "yy");
    fmt_mstart = strstr(fmt, "mm");
    fmt_dstart = strstr(fmt, "dd");

    if (!fmt_ystart || !fmt_mstart || !fmt_dstart)
    {
        errno = PGTYPES_DATE_ERR_EARGS;
        return -1;
    }

    if (fmt_ystart < fmt_mstart)
    {
        /* y m */
        if (fmt_dstart < fmt_ystart)
        {
            /* d y m */
            fmt_token_order = "dym";
        }
        else if (fmt_dstart > fmt_mstart)
        {
            /* y m d */
            fmt_token_order = "ymd";
        }
        else
        {
            /* y d m */
            fmt_token_order = "ydm";
        }
    }
    else
    {
        /* fmt_ystart > fmt_mstart */
        /* m y */
        if (fmt_dstart < fmt_mstart)
        {
            /* d m y */
            fmt_token_order = "dmy";
        }
        else if (fmt_dstart > fmt_ystart)
        {
            /* m y d */
            fmt_token_order = "myd";
        }
        else
        {
            /* m d y */
            fmt_token_order = "mdy";
        }
    }

    /*
     * handle the special cases where there is no delimiter between the
     * digits. If we see this:
     *
     * only digits, 6 or 8 bytes then it might be ddmmyy and ddmmyyyy (or
     * similar)
     *
     * we reduce it to a string with delimiters and continue processing
     */

    /* check if we have only digits */
    reading_digit = 1;
    for (i = 0; str[i]; i++)
    {
        if (!isdigit((unsigned char) str[i]))
        {
            reading_digit = 0;
            break;
        }
    }
    if (reading_digit)
    {
        int         frag_length[3];
        int         target_pos;

        i = strlen(str);
        if (i != 8 && i != 6)
        {
            errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
            return -1;
        }
        /* okay, this really is the special case */

        /*
         * as long as the string, one additional byte for the terminator and 2
         * for the delimiters between the 3 fiedls
         */
        str_copy = pgtypes_alloc(strlen(str) + 1 + 2);
        if (!str_copy)
            return -1;

        /* determine length of the fragments */
        if (i == 6)
        {
            frag_length[0] = 2;
            frag_length[1] = 2;
            frag_length[2] = 2;
        }
        else
        {
            if (fmt_token_order[0] == 'y')
            {
                frag_length[0] = 4;
                frag_length[1] = 2;
                frag_length[2] = 2;
            }
            else if (fmt_token_order[1] == 'y')
            {
                frag_length[0] = 2;
                frag_length[1] = 4;
                frag_length[2] = 2;
            }
            else
            {
                frag_length[0] = 2;
                frag_length[1] = 2;
                frag_length[2] = 4;
            }
        }
        target_pos = 0;

        /*
         * XXX: Here we could calculate the positions of the tokens and save
         * the for loop down there where we again check with isdigit() for
         * digits.
         */
        for (i = 0; i < 3; i++)
        {
            int         start_pos = 0;

            if (i >= 1)
                start_pos += frag_length[0];
            if (i == 2)
                start_pos += frag_length[1];

            strncpy(str_copy + target_pos, str + start_pos,
                    frag_length[i]);
            target_pos += frag_length[i];
            if (i != 2)
            {
                str_copy[target_pos] = ' ';
                target_pos++;
            }
        }
        str_copy[target_pos] = '\0';
    }
    else
    {
        str_copy = pgtypes_strdup(str);
        if (!str_copy)
            return -1;

        /* convert the whole string to lower case */
        for (i = 0; str_copy[i]; i++)
            str_copy[i] = (char) pg_tolower((unsigned char) str_copy[i]);
    }

    /* look for numerical tokens */
    reading_digit = 0;
    token_count = 0;
    for (i = 0; i < strlen(str_copy); i++)
    {
        if (!isdigit((unsigned char) str_copy[i]) && reading_digit)
        {
            /* the token is finished */
            token[token_count][1] = i - 1;
            reading_digit = 0;
            token_count++;
        }
        else if (isdigit((unsigned char) str_copy[i]) && !reading_digit)
        {
            /* we have found a token */
            token[token_count][0] = i;
            reading_digit = 1;
        }
    }

    /*
     * we're at the end of the input string, but maybe we are still reading a
     * number...
     */
    if (reading_digit)
    {
        token[token_count][1] = i - 1;
        token_count++;
    }


    if (token_count < 2)
    {
        /*
         * not all tokens found, no way to find 2 missing tokens with string
         * matches
         */
        free(str_copy);
        errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
        return -1;
    }

    if (token_count != 3)
    {
        /*
         * not all tokens found but we may find another one with string
         * matches by testing for the months names and months abbreviations
         */
        char       *month_lower_tmp = pgtypes_alloc(PGTYPES_DATE_MONTH_MAXLENGTH);
        char       *start_pos;
        int         j;
        int         offset;
        int         found = 0;
        char      **list;

        if (!month_lower_tmp)
        {
            /* free variables we alloc'ed before */
            free(str_copy);
            return -1;
        }
        list = pgtypes_date_months;
        for (i = 0; list[i]; i++)
        {
            for (j = 0; j < PGTYPES_DATE_MONTH_MAXLENGTH; j++)
            {
                month_lower_tmp[j] = (char) pg_tolower((unsigned char) list[i][j]);
                if (!month_lower_tmp[j])
                {
                    /* properly terminated */
                    break;
                }
            }
            if ((start_pos = strstr(str_copy, month_lower_tmp)))
            {
                offset = start_pos - str_copy;

                /*
                 * sort the new token into the numeric tokens, shift them if
                 * necessary
                 */
                if (offset < token[0][0])
                {
                    token[2][0] = token[1][0];
                    token[2][1] = token[1][1];
                    token[1][0] = token[0][0];
                    token[1][1] = token[0][1];
                    token_count = 0;
                }
                else if (offset < token[1][0])
                {
                    token[2][0] = token[1][0];
                    token[2][1] = token[1][1];
                    token_count = 1;
                }
                else
                    token_count = 2;
                token[token_count][0] = offset;
                token[token_count][1] = offset + strlen(month_lower_tmp) - 1;

                /*
                 * the value is the index of the month in the array of months
                 * + 1 (January is month 0)
                 */
                token_values[token_count] = i + 1;
                found = 1;
                break;
            }

            /*
             * evil[tm] hack: if we read the pgtypes_date_months and haven't
             * found a match, reset list to point to pgtypes_date_months_short
             * and reset the counter variable i
             */
            if (list == pgtypes_date_months)
            {
                if (list[i + 1] == NULL)
                {
                    list = months;
                    i = -1;
                }
            }
        }
        if (!found)
        {
            free(month_lower_tmp);
            free(str_copy);
            errno = PGTYPES_DATE_ERR_ENOTDMY;
            return -1;
        }

        /*
         * here we found a month. token[token_count] and
         * token_values[token_count] reflect the month's details.
         *
         * only the month can be specified with a literal. Here we can do a
         * quick check if the month is at the right position according to the
         * format string because we can check if the token that we expect to
         * be the month is at the position of the only token that already has
         * a value. If we wouldn't check here we could say "December 4 1990"
         * with a fmt string of "dd mm yy" for 12 April 1990.
         */
        if (fmt_token_order[token_count] != 'm')
        {
            /* deal with the error later on */
            token_values[token_count] = -1;
        }
        free(month_lower_tmp);
    }

    /* terminate the tokens with ASCII-0 and get their values */
    for (i = 0; i < 3; i++)
    {
        *(str_copy + token[i][1] + 1) = '\0';
        /* A month already has a value set, check for token_value == -1 */
        if (token_values[i] == -1)
        {
            errno = 0;
            token_values[i] = strtol(str_copy + token[i][0], (char **) NULL, 10);
            /* strtol sets errno in case of an error */
            if (errno)
                token_values[i] = -1;
        }
        if (fmt_token_order[i] == 'd')
            tm.tm_mday = token_values[i];
        else if (fmt_token_order[i] == 'm')
            tm.tm_mon = token_values[i];
        else if (fmt_token_order[i] == 'y')
            tm.tm_year = token_values[i];
    }
    free(str_copy);

    if (tm.tm_mday < 1 || tm.tm_mday > 31)
    {
        errno = PGTYPES_DATE_BAD_DAY;
        return -1;
    }

    if (tm.tm_mon < 1 || tm.tm_mon > MONTHS_PER_YEAR)
    {
        errno = PGTYPES_DATE_BAD_MONTH;
        return -1;
    }

    if (tm.tm_mday == 31 && (tm.tm_mon == 4 || tm.tm_mon == 6 || tm.tm_mon == 9 || tm.tm_mon == 11))
    {
        errno = PGTYPES_DATE_BAD_DAY;
        return -1;
    }

    if (tm.tm_mon == 2 && tm.tm_mday > 29)
    {
        errno = PGTYPES_DATE_BAD_DAY;
        return -1;
    }

    *d = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - date2j(2000, 1, 1);

    return 0;
}

int PGTYPESdate_fmt_asc ( date  dDate,
const char *  fmtstring,
char *  outbuf 
)

Definition at line 176 of file datetime.c.

References date2j(), format, free, i, j2date(), months, NULL, pgtypes_alloc(), PGTYPES_DATE_NUM_MAX_DIGITS, pgtypes_date_weekdays_short, PGTYPES_FMTDATE_DAY_DIGITS_LZ, PGTYPES_FMTDATE_DOW_LITERAL_SHORT, PGTYPES_FMTDATE_MONTH_DIGITS_LZ, PGTYPES_FMTDATE_MONTH_LITERAL_SHORT, PGTYPES_FMTDATE_YEAR_DIGITS_LONG, PGTYPES_FMTDATE_YEAR_DIGITS_SHORT, PGTYPES_TYPE_STRING_CONSTANT, PGTYPES_TYPE_STRING_MALLOCED, PGTYPES_TYPE_UINT, PGTYPES_TYPE_UINT_2_LZ, PGTYPES_TYPE_UINT_4_LZ, PGTYPESdate_dayofweek(), snprintf(), un_fmt_comb::str_val, tm, and un_fmt_comb::uint_val.

Referenced by main(), and rfmtdate().

{
    static struct
    {
        char       *format;
        int         component;
    }           mapping[] =
    {
        /*
         * format items have to be sorted according to their length, since the
         * first pattern that matches gets replaced by its value
         */
        {
            "ddd", PGTYPES_FMTDATE_DOW_LITERAL_SHORT
        },
        {
            "dd", PGTYPES_FMTDATE_DAY_DIGITS_LZ
        },
        {
            "mmm", PGTYPES_FMTDATE_MONTH_LITERAL_SHORT
        },
        {
            "mm", PGTYPES_FMTDATE_MONTH_DIGITS_LZ
        },
        {
            "yyyy", PGTYPES_FMTDATE_YEAR_DIGITS_LONG
        },
        {
            "yy", PGTYPES_FMTDATE_YEAR_DIGITS_SHORT
        },
        {
            NULL, 0
        }
    };

    union un_fmt_comb replace_val;
    int         replace_type;

    int         i;
    int         dow;
    char       *start_pattern;
    struct tm   tm;

    /* copy the string over */
    strcpy(outbuf, fmtstring);

    /* get the date */
    j2date(dDate + date2j(2000, 1, 1), &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
    dow = PGTYPESdate_dayofweek(dDate);

    for (i = 0; mapping[i].format != NULL; i++)
    {
        while ((start_pattern = strstr(outbuf, mapping[i].format)) != NULL)
        {
            switch (mapping[i].component)
            {
                case PGTYPES_FMTDATE_DOW_LITERAL_SHORT:
                    replace_val.str_val = pgtypes_date_weekdays_short[dow];
                    replace_type = PGTYPES_TYPE_STRING_CONSTANT;
                    break;
                case PGTYPES_FMTDATE_DAY_DIGITS_LZ:
                    replace_val.uint_val = tm.tm_mday;
                    replace_type = PGTYPES_TYPE_UINT_2_LZ;
                    break;
                case PGTYPES_FMTDATE_MONTH_LITERAL_SHORT:
                    replace_val.str_val = months[tm.tm_mon - 1];
                    replace_type = PGTYPES_TYPE_STRING_CONSTANT;
                    break;
                case PGTYPES_FMTDATE_MONTH_DIGITS_LZ:
                    replace_val.uint_val = tm.tm_mon;
                    replace_type = PGTYPES_TYPE_UINT_2_LZ;
                    break;
                case PGTYPES_FMTDATE_YEAR_DIGITS_LONG:
                    replace_val.uint_val = tm.tm_year;
                    replace_type = PGTYPES_TYPE_UINT_4_LZ;
                    break;
                case PGTYPES_FMTDATE_YEAR_DIGITS_SHORT:
                    replace_val.uint_val = tm.tm_year % 100;
                    replace_type = PGTYPES_TYPE_UINT_2_LZ;
                    break;
                default:

                    /*
                     * should not happen, set something anyway
                     */
                    replace_val.str_val = " ";
                    replace_type = PGTYPES_TYPE_STRING_CONSTANT;
            }
            switch (replace_type)
            {
                case PGTYPES_TYPE_STRING_MALLOCED:
                case PGTYPES_TYPE_STRING_CONSTANT:
                    strncpy(start_pattern, replace_val.str_val,
                            strlen(replace_val.str_val));
                    if (replace_type == PGTYPES_TYPE_STRING_MALLOCED)
                        free(replace_val.str_val);
                    break;
                case PGTYPES_TYPE_UINT:
                    {
                        char       *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);

                        if (!t)
                            return -1;
                        snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
                                 "%u", replace_val.uint_val);
                        strncpy(start_pattern, t, strlen(t));
                        free(t);
                    }
                    break;
                case PGTYPES_TYPE_UINT_2_LZ:
                    {
                        char       *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);

                        if (!t)
                            return -1;
                        snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
                                 "%02u", replace_val.uint_val);
                        strncpy(start_pattern, t, strlen(t));
                        free(t);
                    }
                    break;
                case PGTYPES_TYPE_UINT_4_LZ:
                    {
                        char       *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);

                        if (!t)
                            return -1;
                        snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
                                 "%04u", replace_val.uint_val);
                        strncpy(start_pattern, t, strlen(t));
                        free(t);
                    }
                    break;
                default:

                    /*
                     * doesn't happen (we set replace_type to
                     * PGTYPES_TYPE_STRING_CONSTANT in case of an error above)
                     */
                    break;
            }
        }
    }
    return 0;
}

void PGTYPESdate_free ( date d  ) 

Definition at line 26 of file datetime.c.

References free.

Referenced by main().

{
    free(d);
}

date PGTYPESdate_from_asc ( char *  str,
char **  endptr 
)

Definition at line 53 of file datetime.c.

References date2j(), DecodeDateTime(), DTK_DATE, DTK_EPOCH, GetEpochTime(), MAXDATELEN, ParseDateTime(), and tm.

Referenced by ecpg_get_data(), and main().

{

    date        dDate;
    fsec_t      fsec;
    struct tm   tt,
               *tm = &tt;
    int         dtype;
    int         nf;
    char       *field[MAXDATEFIELDS];
    int         ftype[MAXDATEFIELDS];
    char        lowstr[MAXDATELEN + 1];
    char       *realptr;
    char      **ptr = (endptr != NULL) ? endptr : &realptr;

    bool        EuroDates = FALSE;

    errno = 0;
    if (strlen(str) >= sizeof(lowstr))
    {
        errno = PGTYPES_DATE_BAD_DATE;
        return INT_MIN;
    }

    if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
        DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, EuroDates) != 0)
    {
        errno = PGTYPES_DATE_BAD_DATE;
        return INT_MIN;
    }

    switch (dtype)
    {
        case DTK_DATE:
            break;

        case DTK_EPOCH:
            if (GetEpochTime(tm) < 0)
            {
                errno = PGTYPES_DATE_BAD_DATE;
                return INT_MIN;
            }
            break;

        default:
            errno = PGTYPES_DATE_BAD_DATE;
            return INT_MIN;
    }

    dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));

    return dDate;
}

date PGTYPESdate_from_timestamp ( timestamp  dt  ) 

Definition at line 32 of file datetime.c.

References SECS_PER_DAY, and TIMESTAMP_NOT_FINITE.

Referenced by main(), and PGTYPEStimestamp_fmt_asc().

{
    date        dDate;

    dDate = 0;                  /* suppress compiler warning */

    if (!TIMESTAMP_NOT_FINITE(dt))
    {
#ifdef HAVE_INT64_TIMESTAMP
        /* Microseconds to days */
        dDate = (dt / USECS_PER_DAY);
#else
        /* Seconds to days */
        dDate = (dt / (double) SECS_PER_DAY);
#endif
    }

    return dDate;
}

void PGTYPESdate_julmdy ( date  jd,
int *  mdy 
)

Definition at line 122 of file datetime.c.

References date2j(), and j2date().

Referenced by main(), and rjulmdy().

{
    int         y,
                m,
                d;

    j2date((int) (jd + date2j(2000, 1, 1)), &y, &m, &d);
    mdy[0] = m;
    mdy[1] = d;
    mdy[2] = y;
}

void PGTYPESdate_mdyjul ( int *  mdy,
date jdate 
)

Definition at line 135 of file datetime.c.

References date2j().

Referenced by main(), and rmdyjul().

{
    /* month is mdy[0] */
    /* day   is mdy[1] */
    /* year  is mdy[2] */

    *jdate = (date) (date2j(mdy[2], mdy[0], mdy[1]) - date2j(2000, 1, 1));
}

date* PGTYPESdate_new ( void   ) 

Definition at line 16 of file datetime.c.

References pgtypes_alloc().

Referenced by main().

{
    date       *result;

    result = (date *) pgtypes_alloc(sizeof(date));
    /* result can be NULL if we run out of memory */
    return result;
}

char* PGTYPESdate_to_asc ( date  dDate  ) 

Definition at line 108 of file datetime.c.

References buf, date2j(), DateStyle, EncodeDateOnly(), j2date(), MAXDATELEN, pgtypes_strdup(), and tm.

Referenced by ecpg_store_input(), main(), and rdatestr().

{
    struct tm   tt,
               *tm = &tt;
    char        buf[MAXDATELEN + 1];
    int         DateStyle = 1;
    bool        EuroDates = FALSE;

    j2date(dDate + date2j(2000, 1, 1), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
    EncodeDateOnly(tm, DateStyle, buf, EuroDates);
    return pgtypes_strdup(buf);
}

void PGTYPESdate_today ( date d  ) 

Definition at line 155 of file datetime.c.

References date2j(), GetCurrentDateTime(), and tm.

Referenced by rtoday().

{
    struct tm   ts;

    GetCurrentDateTime(&ts);
    if (errno == 0)
        *d = date2j(ts.tm_year, ts.tm_mon, ts.tm_mday) - date2j(2000, 1, 1);
    return;
}