From: Jan E. <je...@in...> - 2014-01-05 14:01:48
|
Dates before the epoch are not (always) representable in time_t. In particular, the Microsoft C runtime's mktime function rejects any dates before 1970, which is different from glibc which tries to produce negative time_t values. The sensible thing to do is to expose the broken-down date, for example in a struct tm. --- configure.ac | 4 ++-- doc/driver-guide.sgml | 14 +++++++---- include/dbi/dbd.h | 2 +- include/dbi/dbi-dev.h | 2 +- include/dbi/dbi.h.in | 6 +++++ src/dbd_helper.c | 37 +++++++++++++++-------------- src/dbi_result.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++----- src/libdbi.map | 6 +++-- 8 files changed, 102 insertions(+), 35 deletions(-) diff --git a/configure.ac b/configure.ac index 7daf410..3417385 100644 --- a/configure.ac +++ b/configure.ac @@ -6,9 +6,9 @@ AC_CONFIG_SRCDIR([src/dbi_main.c]) AC_USE_SYSTEM_EXTENSIONS dnl set up libtool library versioning -LIB_CURRENT=2 +LIB_CURRENT=3 LIB_REVISION=0 -LIB_AGE=1 +LIB_AGE=0 dnl set up libdbi versioning from the above interface version numbers dnl the math contains an ugly hack to massage the minor version number diff --git a/doc/driver-guide.sgml b/doc/driver-guide.sgml index acdf617..dc4f943 100644 --- a/doc/driver-guide.sgml +++ b/doc/driver-guide.sgml @@ -994,28 +994,32 @@ published by the Free Software Foundation. </VarListEntry> </VariableList> </section> - <section id="internal-dbd-parse-datetime" xreflabel="_dbd_parse_datetime"> - <title>_dbd_parse_datetime</title> + <section id="internal-dbd-parse-datetimex" xreflabel="_dbd_parse_datetimex"> + <title>_dbd_parse_datetimex</title> <funcsynopsis> <funcprototype> - <funcdef>time_t <function moreinfo="none">_dbd_parse_datetime</function></funcdef> + <funcdef>int <function moreinfo="none">_dbd_parse_datetimex</function></funcdef> <paramdef>const char *<parameter moreinfo="none">raw</parameter></paramdef> <paramdef>unsigned int <parameter moreinfo="none">attribs</parameter></paramdef> + <paramdef>dbi_datetimex *<parameter moreinfo="none">dtx</parameter></paramdef> </funcprototype> </funcsynopsis> - <para>Parses the input time, date, or datetime string and converts the value into a time_t value.</para> + <para>Parses the input time, date, or datetime string into a + dbi_datetimex, the latter of which groups a struct tm and a timezone + offset.</para> <VariableList> <VarListEntry> <Term>Arguments</Term> <ListItem> <para><literal moreinfo="none">raw</literal>: A zero-terminated string containing a time, date, or datetime value. Accepted formats are YYYY-MM-DD for date values, HH:MM:SS for time values, and YYYY-MM-DD HH:MM:SS for datetime values. The separators must be present, but can be any character.</para> <Para><Literal>attribs</Literal>: The field attributes of raw.</Para> + <Para><Literal>dtx</Literal>: a dbi_datetimex structure to fill in.</Para> </ListItem> </VarListEntry> <varlistentry> <term><emphasis>Returns</emphasis></term> <listitem> - <para>The numeric equivalent of the input based on UTC. In case of an error, this function returns the start of the Unix epoch.</para> + <para>Always zero.</para> </listitem> </varlistentry> </VariableList> diff --git a/include/dbi/dbd.h b/include/dbi/dbd.h index 33c3efc..cd621df 100644 --- a/include/dbi/dbd.h +++ b/include/dbi/dbd.h @@ -75,7 +75,7 @@ dbi_result_t *_dbd_result_create_from_stringarray(dbi_conn_t *conn, unsigned lon void _dbd_register_driver_cap(dbi_driver_t *driver, const char *capname, int value); void _dbd_register_conn_cap(dbi_conn_t *conn, const char *capname, int value); int _dbd_result_add_to_conn(dbi_result_t *result); -time_t _dbd_parse_datetime(const char *raw, unsigned int attribs); +int _dbd_parse_datetimex(const char *raw, unsigned int attr, dbi_datetimex *dtx); size_t _dbd_escape_chars(char *dest, const char *orig, size_t orig_size, const char *toescape); size_t _dbd_encode_binary(const unsigned char *in, size_t n, unsigned char *out); size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out); diff --git a/include/dbi/dbi-dev.h b/include/dbi/dbi-dev.h index 02b2283..0e21005 100644 --- a/include/dbi/dbi-dev.h +++ b/include/dbi/dbi-dev.h @@ -48,7 +48,7 @@ typedef union dbi_data_u { float d_float; double d_double; char *d_string; - time_t d_datetime; + dbi_datetimex d_datetimex; } dbi_data_t; typedef struct dbi_row_s { diff --git a/include/dbi/dbi.h.in b/include/dbi/dbi.h.in index b5b08e8..92a8e22 100644 --- a/include/dbi/dbi.h.in +++ b/include/dbi/dbi.h.in @@ -100,6 +100,10 @@ typedef struct { dbi_time time; } dbi_datetime; +typedef struct { + struct tm tm; + int utc_offset; +} dbi_datetimex; /* function callback definitions */ typedef void (*dbi_conn_error_handler_func)(dbi_conn, void *); @@ -303,6 +307,7 @@ char *dbi_result_get_string_copy(dbi_result Result, const char *fieldname); unsigned char *dbi_result_get_binary_copy(dbi_result Result, const char *fieldname); time_t dbi_result_get_datetime(dbi_result Result, const char *fieldname); +const dbi_datetimex *dbi_result_get_datetimex(dbi_result Result, const char *fieldname); int dbi_result_bind_char(dbi_result Result, const char *fieldname, char *bindto); int dbi_result_bind_uchar(dbi_result Result, const char *fieldname, unsigned char *bindto); @@ -349,6 +354,7 @@ char *dbi_result_get_string_copy_idx(dbi_result Result, unsigned int fieldidx); unsigned char *dbi_result_get_binary_copy_idx(dbi_result Result, unsigned int fieldidx); time_t dbi_result_get_datetime_idx(dbi_result Result, unsigned int fieldidx); +const dbi_datetimex *dbi_result_get_datetimex_idx(dbi_result Result, unsigned int fieldidx); /* get_as* functions */ long long dbi_result_get_as_longlong(dbi_result Result, const char *fieldname); diff --git a/src/dbd_helper.c b/src/dbd_helper.c index 833dc0f..3a20448 100644 --- a/src/dbd_helper.c +++ b/src/dbd_helper.c @@ -31,6 +31,7 @@ #include <stdlib.h> #include <stdarg.h> #include <string.h> +#include <time.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -299,8 +300,10 @@ static _capability_t *_find_or_create_conn_cap(dbi_conn_t *conn, const char *cap return cap; } -time_t _dbd_parse_datetime(const char *raw, unsigned int attribs) { - struct tm unixtime; +int _dbd_parse_datetimex(const char *raw, unsigned int attribs, + dbi_datetimex *dtx) +{ + struct tm *unixtime = &dtx->tm; char *unparsed; char *cur; @@ -311,11 +314,11 @@ time_t _dbd_parse_datetime(const char *raw, unsigned int attribs) { int check_time = 1; - unixtime.tm_sec = unixtime.tm_min = unixtime.tm_hour = 0; - unixtime.tm_mday = 1; /* days are 1 through 31 */ - unixtime.tm_mon = 0; - unixtime.tm_year = 70; /* can't start before Unix epoch */ - unixtime.tm_isdst = -1; + unixtime->tm_sec = unixtime->tm_min = unixtime->tm_hour = 0; + unixtime->tm_mday = 1; /* days are 1 through 31 */ + unixtime->tm_mon = 0; + unixtime->tm_year = 70; /* can't start before Unix epoch */ + unixtime->tm_isdst = -1; if (raw && (unparsed = strdup(raw)) != NULL) { cur = unparsed; @@ -336,9 +339,9 @@ time_t _dbd_parse_datetime(const char *raw, unsigned int attribs) { cur[4] = '\0'; cur[7] = '\0'; cur[10] = '\0'; - unixtime.tm_year = atoi(cur)-1900; - unixtime.tm_mon = atoi(cur+5)-1; /* months are 0 through 11 */ - unixtime.tm_mday = atoi(cur+8); + unixtime->tm_year = atoi(cur)-1900; + unixtime->tm_mon = atoi(cur+5)-1; /* months are 0 through 11 */ + unixtime->tm_mday = atoi(cur+8); if (attribs & DBI_DATETIME_TIME) { cur += 11; if (*cur == ' ') { @@ -350,9 +353,9 @@ time_t _dbd_parse_datetime(const char *raw, unsigned int attribs) { if (check_time && strlen(cur) > 7 && attribs & DBI_DATETIME_TIME) { cur[2] = '\0'; cur[5] = '\0'; - unixtime.tm_hour = atoi(cur); - unixtime.tm_min = atoi(cur+3); - unixtime.tm_sec = atoi(cur+6); + unixtime->tm_hour = atoi(cur); + unixtime->tm_min = atoi(cur+3); + unixtime->tm_sec = atoi(cur+6); /* check for a timezone suffix */ cur += 8; @@ -394,18 +397,16 @@ time_t _dbd_parse_datetime(const char *raw, unsigned int attribs) { _gm_offset += _tz_hours * 60 * 60; _gm_offset += _tz_mins * 60; - if ( _tz_dir ) { + if (!_tz_dir) _gm_offset *= -1; - } } } } free(unparsed); } - - /* output is UTC, not local time */ - return (time_t)(_gm_offset + timegm(&unixtime)); + dtx->utc_offset = _gm_offset; + return 0; } /* Calculate the required buffer size (in bytes) for directory * diff --git a/src/dbi_result.c b/src/dbi_result.c index 35e1b61..751e490 100644 --- a/src/dbi_result.c +++ b/src/dbi_result.c @@ -1334,6 +1334,14 @@ unsigned char *dbi_result_get_binary_copy_idx(dbi_result Result, unsigned int fi return newblob; } +static time_t _dbi_make_datetime(const dbi_datetimex *dtx) +{ + struct tm copy = dtx->tm; + + /* timegm reserves the right to modify it by not taking a const tm */ + return timegm(©) - dtx->utc_offset; +} + time_t dbi_result_get_datetime(dbi_result Result, const char *fieldname) { time_t my_ERROR = 0; unsigned int fieldidx; @@ -1367,7 +1375,51 @@ time_t dbi_result_get_datetime_idx(dbi_result Result, unsigned int fieldidx) { return my_ERROR; } - return (time_t)(RESULT->rows[RESULT->currowidx]->field_values[fieldidx].d_datetime); + return _dbi_make_datetime(&RESULT->rows[RESULT->currowidx]->field_values[fieldidx].d_datetimex); +} + +static const dbi_datetimex _dbi_datetimex_ERROR = { + .tm = { + .tm_sec = -1, .tm_min = -1, .tm_hour = -1, + .tm_mday = -1, .tm_mon = -1, .tm_year = -1, + .tm_wday = -1, .tm_yday = -1, .tm_isdst = -1, + }, +}; + +const dbi_datetimex * +dbi_result_get_datetimex(dbi_result Result, const char *fieldname) +{ + unsigned int fieldidx; + dbi_error_flag errflag = DBI_ERROR_NONE; + + _reset_conn_error(RESULT->conn); + fieldidx = _find_field(RESULT, fieldname, &errflag); + if (errflag != DBI_ERROR_NONE) { + dbi_conn_t *conn = RESULT->conn; + _error_handler(conn, DBI_ERROR_BADNAME); + return &_dbi_datetimex_ERROR; + } + return dbi_result_get_datetimex_idx(Result, fieldidx + 1); +} + +const dbi_datetimex * +dbi_result_get_datetimex_idx(dbi_result Result, unsigned int fieldidx) +{ + --fieldidx; + _reset_conn_error(RESULT->conn); + if (fieldidx >= RESULT->numfields) { + _error_handler(RESULT->conn, DBI_ERROR_BADIDX); + return &_dbi_datetimex_ERROR; + } + if (RESULT->field_types[fieldidx] != DBI_TYPE_DATETIME) { + _verbose_handler(RESULT->conn, + "%s: field `%s` is not datetime type\n", + __func__, + dbi_result_get_field_name(Result, fieldidx + 1)); + _error_handler(RESULT->conn, DBI_ERROR_BADTYPE); + return &_dbi_datetimex_ERROR; + } + return &RESULT->rows[RESULT->currowidx]->field_values[fieldidx].d_datetimex; } /* RESULT: get_as* functions */ @@ -1430,7 +1482,7 @@ long long dbi_result_get_as_longlong_idx(dbi_result Result, unsigned int fieldid case DBI_TYPE_BINARY: return 0; /* do not raise an error */ case DBI_TYPE_DATETIME: - return (long long)(RESULT->rows[RESULT->currowidx]->field_values[fieldidx].d_datetime); + return _dbi_make_datetime(&RESULT->rows[RESULT->currowidx]->field_values[fieldidx].d_datetimex); default: _error_handler(RESULT->conn, DBI_ERROR_BADTYPE); return my_ERROR; @@ -1457,7 +1509,6 @@ char *dbi_result_get_as_string_copy_idx(dbi_result Result, unsigned int fieldidx char *my_ERROR = "ERROR"; char *newstring = NULL; char *oldstring = NULL; - struct tm utctime; fieldidx--; @@ -1544,10 +1595,13 @@ char *dbi_result_get_as_string_copy_idx(dbi_result Result, unsigned int fieldidx break; case DBI_TYPE_BINARY: break; /* return empty string, do not raise an error */ - case DBI_TYPE_DATETIME: - gmtime_r(&(RESULT->rows[RESULT->currowidx]->field_values[fieldidx].d_datetime), &utctime); - snprintf(newstring, 32, "%04d-%02d-%02d %02d:%02d:%02d", utctime.tm_year+1900, utctime.tm_mon+1, utctime.tm_mday, utctime.tm_hour, utctime.tm_min, utctime.tm_sec); + case DBI_TYPE_DATETIME: { + const struct tm *tm = &RESULT->rows[RESULT->currowidx]->field_values[fieldidx].d_datetimex.tm; + snprintf(newstring, 32, "%04d-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); break; + } default: _error_handler(RESULT->conn, DBI_ERROR_BADTYPE); } diff --git a/src/libdbi.map b/src/libdbi.map index 6d86cdd..96d4118 100644 --- a/src/libdbi.map +++ b/src/libdbi.map @@ -1,4 +1,4 @@ -ABI_2 { +ABI_3 { global: dbi_conn_cap_get; dbi_conn_clear_option; @@ -104,6 +104,8 @@ global: dbi_result_get_currow; dbi_result_get_datetime; dbi_result_get_datetime_idx; + dbi_result_get_datetimex; + dbi_result_get_datetimex_idx; dbi_result_get_double; dbi_result_get_double_idx; dbi_result_get_field_attrib; @@ -163,7 +165,7 @@ global: _dbd_encode_binary; _dbd_escape_chars; _dbd_internal_error_handler; - _dbd_parse_datetime; + _dbd_parse_datetimex; _dbd_register_conn_cap; _dbd_register_driver_cap; _dbd_result_add_field; -- 1.8.4 |