|
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
|