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(&copy) - 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


------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT 
organizations don't have a clear picture of how application performance 
affects their revenue. With AppDynamics, you get 100% visibility into your 
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
libdbi-devel mailing list
libdbi-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libdbi-devel

Reply via email to