mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
A better way to handle MySQL's 0000-00-00 invalid date indicator.
This commit is contained in:
parent
23f25d74d9
commit
af1bc45021
@ -30,6 +30,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
@ -1904,27 +1905,43 @@ load_timespec( const GncSqlBackend* be, GncSqlRow* row,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* MySQL's TIMESTAMP type is not compliant with the SQL
|
||||||
|
* standard in that it is valid only from 1970-01-01 00:00:01
|
||||||
|
* to 2038-01-15 23:59:59. Times outside that range are set to
|
||||||
|
* "0000-00-00 00:00:00"; depending on the libdbi version we
|
||||||
|
* may get either the string value or the time64 representing
|
||||||
|
* it. In either case leave the timespec at 0.
|
||||||
|
*/
|
||||||
if ( G_VALUE_HOLDS_INT64( val ) )
|
if ( G_VALUE_HOLDS_INT64( val ) )
|
||||||
{
|
{
|
||||||
timespecFromTime64 (&ts, (time64)(g_value_get_int64 (val)));
|
const time64 MINTIME = INT64_C(-62135596800);
|
||||||
isOK = TRUE;
|
const time64 MAXTIME = INT64_C(253402300799);
|
||||||
|
const time64 t = (time64)(g_value_get_int64 (val));
|
||||||
|
if (t >= MINTIME && t <= MAXTIME)
|
||||||
|
{
|
||||||
|
timespecFromTime64 (&ts, t);
|
||||||
|
}
|
||||||
|
isOK = TRUE;
|
||||||
}
|
}
|
||||||
else if (G_VALUE_HOLDS_STRING (val))
|
else if (G_VALUE_HOLDS_STRING (val))
|
||||||
{
|
{
|
||||||
const gchar* s = g_value_get_string( val );
|
const gchar* s = g_value_get_string( val );
|
||||||
if ( s != NULL )
|
if ( s != NULL )
|
||||||
{
|
{
|
||||||
gchar* buf;
|
if (! g_str_has_prefix (s, "0000-00-00"))
|
||||||
buf = g_strdup_printf( "%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c",
|
{
|
||||||
s[0], s[1], s[2], s[3],
|
gchar* buf;
|
||||||
s[4], s[5],
|
buf = g_strdup_printf( "%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c",
|
||||||
s[6], s[7],
|
s[0], s[1], s[2], s[3],
|
||||||
s[8], s[9],
|
s[4], s[5],
|
||||||
s[10], s[11],
|
s[6], s[7],
|
||||||
s[12], s[13] );
|
s[8], s[9],
|
||||||
ts = gnc_iso8601_to_timespec_gmt( buf );
|
s[10], s[11],
|
||||||
g_free( buf );
|
s[12], s[13] );
|
||||||
isOK = TRUE;
|
ts = gnc_iso8601_to_timespec_gmt( buf );
|
||||||
|
g_free( buf );
|
||||||
|
}
|
||||||
|
isOK = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -73,17 +73,6 @@
|
|||||||
# define GNC_T_FMT "%r"
|
# define GNC_T_FMT "%r"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* t < MINTIME is probably from a bad conversion from t 0 to
|
|
||||||
* 0000-00-00, so restore it to the Unix Epoch. t anywhere near
|
|
||||||
* MAXTIME is obviously an error, but we don't want to crash with a
|
|
||||||
* bad date-time so just clamp it to MAXTIME.
|
|
||||||
*/
|
|
||||||
static inline time64
|
|
||||||
clamp_time(time64 t)
|
|
||||||
{
|
|
||||||
return t < MINTIME ? 0 : t > MAXTIME ? MAXTIME : t;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *gnc_default_strftime_date_format =
|
const char *gnc_default_strftime_date_format =
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
/* The default date format for use with strftime in Win32. */
|
/* The default date format for use with strftime in Win32. */
|
||||||
@ -152,7 +141,7 @@ GDateTime*
|
|||||||
gnc_g_date_time_new_from_unix_local (time64 time)
|
gnc_g_date_time_new_from_unix_local (time64 time)
|
||||||
{
|
{
|
||||||
GTimeZone *tz = gnc_g_time_zone_new_local ();
|
GTimeZone *tz = gnc_g_time_zone_new_local ();
|
||||||
GDateTime *gdt = g_date_time_new_from_unix_utc (clamp_time (time));
|
GDateTime *gdt = g_date_time_new_from_unix_utc (time);
|
||||||
if (gdt)
|
if (gdt)
|
||||||
gdt = gnc_g_date_time_adjust_for_dst (gdt, tz);
|
gdt = gnc_g_date_time_adjust_for_dst (gdt, tz);
|
||||||
return gdt;
|
return gdt;
|
||||||
@ -260,7 +249,7 @@ struct tm*
|
|||||||
gnc_localtime_r (const time64 *secs, struct tm* time)
|
gnc_localtime_r (const time64 *secs, struct tm* time)
|
||||||
{
|
{
|
||||||
guint index = 0;
|
guint index = 0;
|
||||||
GDateTime *gdt = gnc_g_date_time_new_from_unix_local (clamp_time (*secs));
|
GDateTime *gdt = gnc_g_date_time_new_from_unix_local (*secs);
|
||||||
g_return_val_if_fail (gdt != NULL, NULL);
|
g_return_val_if_fail (gdt != NULL, NULL);
|
||||||
|
|
||||||
gnc_g_date_time_fill_struct_tm (gdt, time);
|
gnc_g_date_time_fill_struct_tm (gdt, time);
|
||||||
@ -282,7 +271,7 @@ struct tm*
|
|||||||
gnc_gmtime (const time64 *secs)
|
gnc_gmtime (const time64 *secs)
|
||||||
{
|
{
|
||||||
struct tm *time;
|
struct tm *time;
|
||||||
GDateTime *gdt = g_date_time_new_from_unix_utc (clamp_time (*secs));
|
GDateTime *gdt = g_date_time_new_from_unix_utc (*secs);
|
||||||
g_return_val_if_fail (gdt != NULL, NULL);
|
g_return_val_if_fail (gdt != NULL, NULL);
|
||||||
time = g_slice_alloc0 (sizeof (struct tm));
|
time = g_slice_alloc0 (sizeof (struct tm));
|
||||||
gnc_g_date_time_fill_struct_tm (gdt, time);
|
gnc_g_date_time_fill_struct_tm (gdt, time);
|
||||||
@ -400,10 +389,10 @@ gnc_timegm (struct tm* time)
|
|||||||
gchar*
|
gchar*
|
||||||
gnc_ctime (const time64 *secs)
|
gnc_ctime (const time64 *secs)
|
||||||
{
|
{
|
||||||
GDateTime *gdt = gnc_g_date_time_new_from_unix_local (clamp_time (*secs));
|
GDateTime *gdt = gnc_g_date_time_new_from_unix_local (*secs);
|
||||||
gchar *string = g_date_time_format (gdt, "%a %b %e %H:%M:%S %Y");
|
gchar *string = g_date_time_format (gdt, "%a %b %e %H:%M:%S %Y");
|
||||||
g_date_time_unref (gdt);
|
g_date_time_unref (gdt);
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
time64
|
time64
|
||||||
@ -898,7 +887,7 @@ size_t
|
|||||||
qof_print_date_buff (char * buff, size_t len, time64 t)
|
qof_print_date_buff (char * buff, size_t len, time64 t)
|
||||||
{
|
{
|
||||||
struct tm theTime;
|
struct tm theTime;
|
||||||
time64 bt = clamp_time (t);
|
time64 bt = t;
|
||||||
size_t actual;
|
size_t actual;
|
||||||
if (!buff) return 0 ;
|
if (!buff) return 0 ;
|
||||||
if (!gnc_localtime_r(&bt, &theTime))
|
if (!gnc_localtime_r(&bt, &theTime))
|
||||||
@ -1646,7 +1635,7 @@ gnc_timezone (const struct tm *tm)
|
|||||||
void
|
void
|
||||||
timespecFromTime64 ( Timespec *ts, time64 t )
|
timespecFromTime64 ( Timespec *ts, time64 t )
|
||||||
{
|
{
|
||||||
ts->tv_sec = clamp_time (t);
|
ts->tv_sec = t;
|
||||||
ts->tv_nsec = 0;
|
ts->tv_nsec = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdint.h>
|
|
||||||
/**
|
/**
|
||||||
* Many systems, including Microsoft Windows and BSD-derived Unixes
|
* Many systems, including Microsoft Windows and BSD-derived Unixes
|
||||||
* like Darwin, are retaining the int-32 typedef for time_t. Since
|
* like Darwin, are retaining the int-32 typedef for time_t. Since
|
||||||
@ -100,8 +100,7 @@ extern const char *gnc_default_strftime_date_format;
|
|||||||
|
|
||||||
/** The maximum length of a string created by the date printers */
|
/** The maximum length of a string created by the date printers */
|
||||||
#define MAX_DATE_LENGTH 34
|
#define MAX_DATE_LENGTH 34
|
||||||
#define MAXTIME INT64_C(253402300799)
|
|
||||||
#define MINTIME INT64_C(-62135596800)
|
|
||||||
/** Constants *******************************************************/
|
/** Constants *******************************************************/
|
||||||
/** \brief UTC date format string.
|
/** \brief UTC date format string.
|
||||||
|
|
||||||
|
@ -58,17 +58,6 @@ typedef struct
|
|||||||
static _GncDateTime gncdt;
|
static _GncDateTime gncdt;
|
||||||
extern void _gnc_date_time_init (_GncDateTime *);
|
extern void _gnc_date_time_init (_GncDateTime *);
|
||||||
|
|
||||||
/* t < MINTIME is probably from a bad conversion from t 0 to
|
|
||||||
* 0000-00-00, so restore it to the Unix Epoch. t anywhere near
|
|
||||||
* MAXTIME is obviously an error, but we don't want to crash with a
|
|
||||||
* bad date-time so just clamp it to MAXTIME.
|
|
||||||
*/
|
|
||||||
static inline time64
|
|
||||||
clamp_time(time64 t)
|
|
||||||
{
|
|
||||||
return t < MINTIME ? 0 : t > MAXTIME ? MAXTIME : t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* gnc_localtime just creates a tm on the heap and calls
|
/* gnc_localtime just creates a tm on the heap and calls
|
||||||
* gnc_localtime_r with it, so this suffices to test both.
|
* gnc_localtime_r with it, so this suffices to test both.
|
||||||
*/
|
*/
|
||||||
@ -81,6 +70,18 @@ test_gnc_localtime (void)
|
|||||||
// difference between g_date_time and tm->tm_wday)
|
// difference between g_date_time and tm->tm_wday)
|
||||||
};
|
};
|
||||||
guint ind;
|
guint ind;
|
||||||
|
#if defined(__clang__) && __clang_major__ < 6
|
||||||
|
#define _func "struct tm *gnc_localtime_r(const time64 *, struct tm *)"
|
||||||
|
#else
|
||||||
|
#define _func "gnc_localtime_r"
|
||||||
|
#endif
|
||||||
|
gchar *msg = _func ": assertion " _Q "gdt != NULL' failed";
|
||||||
|
#undef _func
|
||||||
|
gint loglevel = G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL;
|
||||||
|
gchar *logdomain = "qof";
|
||||||
|
TestErrorStruct check = {loglevel, logdomain, msg, 0};
|
||||||
|
GLogFunc hdlr = g_log_set_default_handler ((GLogFunc)test_null_handler, &check);
|
||||||
|
g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_checked_handler, &check);
|
||||||
|
|
||||||
for (ind = 0; ind < G_N_ELEMENTS (secs); ind++)
|
for (ind = 0; ind < G_N_ELEMENTS (secs); ind++)
|
||||||
{
|
{
|
||||||
@ -111,6 +112,8 @@ test_gnc_localtime (void)
|
|||||||
g_date_time_unref (gdt);
|
g_date_time_unref (gdt);
|
||||||
gnc_tm_free (time);
|
gnc_tm_free (time);
|
||||||
}
|
}
|
||||||
|
g_assert_cmpint (check.hits, ==, 1);
|
||||||
|
g_log_set_default_handler (hdlr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -126,23 +129,35 @@ test_gnc_gmtime (void)
|
|||||||
{ 48, 51, 23, 18, 11, 69, 4, 352, 0, 0, NULL },
|
{ 48, 51, 23, 18, 11, 69, 4, 352, 0, 0, NULL },
|
||||||
{ 41, 12, 0, 6, 0, 70, 2, 6, 0, 0, NULL },
|
{ 41, 12, 0, 6, 0, 70, 2, 6, 0, 0, NULL },
|
||||||
{ 32, 30, 2, 3, 11, 92, 4, 338, 0, 0, NULL },
|
{ 32, 30, 2, 3, 11, 92, 4, 338, 0, 0, NULL },
|
||||||
{ 59, 59, 23, 31, 11, 8099, 5, 365, 0, 0, NULL },
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL },
|
||||||
{ 6, 47, 16, 7, 3, 107, 6, 97, 0, 0, NULL },
|
{ 6, 47, 16, 7, 3, 107, 6, 97, 0, 0, NULL },
|
||||||
#else
|
#else
|
||||||
{ 6, 41, 2, 24, 9, -1301, 4, 297, 0 },
|
{ 6, 41, 2, 24, 9, -1301, 4, 297, 0 },
|
||||||
{ 48, 51, 23, 18, 11, 69, 4, 352, 0 },
|
{ 48, 51, 23, 18, 11, 69, 4, 352, 0 },
|
||||||
{ 41, 12, 0, 6, 0, 70, 2, 6, 0 },
|
{ 41, 12, 0, 6, 0, 70, 2, 6, 0 },
|
||||||
{ 32, 30, 2, 3, 11, 92, 4, 338, 0 },
|
{ 32, 30, 2, 3, 11, 92, 4, 338, 0 },
|
||||||
{ 59, 50, 23, 31, 11, 8099, 5, 365, 0 },
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||||
{ 6, 47, 16, 7, 3, 107, 6, 97, 0 },
|
{ 6, 47, 16, 7, 3, 107, 6, 97, 0 },
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
guint ind;
|
guint ind;
|
||||||
|
#if defined(__clang__) && __clang_major__ < 6
|
||||||
|
#define _func "struct tm *gnc_gmtime(const time64 *)"
|
||||||
|
#else
|
||||||
|
#define _func "gnc_gmtime"
|
||||||
|
#endif
|
||||||
|
gchar *msg = _func ": assertion " _Q "gdt != NULL' failed";
|
||||||
|
#undef _func
|
||||||
|
gint loglevel = G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL;
|
||||||
|
gchar *logdomain = "qof";
|
||||||
|
TestErrorStruct check = {loglevel, logdomain, msg, 0};
|
||||||
|
GLogFunc hdlr = g_log_set_default_handler ((GLogFunc)test_null_handler, &check);
|
||||||
|
g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_checked_handler, &check);
|
||||||
|
|
||||||
for (ind = 0; ind < G_N_ELEMENTS (secs); ind++)
|
for (ind = 0; ind < G_N_ELEMENTS (secs); ind++)
|
||||||
{
|
{
|
||||||
struct tm* time = gnc_gmtime (&secs[ind]);
|
struct tm* time = gnc_gmtime (&secs[ind]);
|
||||||
GDateTime *gdt = g_date_time_new_from_unix_utc (clamp_time (secs[ind]));
|
GDateTime *gdt = g_date_time_new_from_unix_utc (secs[ind]);
|
||||||
if (gdt == NULL)
|
if (gdt == NULL)
|
||||||
{
|
{
|
||||||
g_assert (time == NULL);
|
g_assert (time == NULL);
|
||||||
@ -163,6 +178,8 @@ test_gnc_gmtime (void)
|
|||||||
g_date_time_unref (gdt);
|
g_date_time_unref (gdt);
|
||||||
gnc_tm_free (time);
|
gnc_tm_free (time);
|
||||||
}
|
}
|
||||||
|
g_assert_cmpint (check.hits, ==, 1);
|
||||||
|
g_log_set_default_handler (hdlr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2020,23 +2037,14 @@ gnc_timezone (const struct tm *tm)// C: 5 in 2 Local: 2:0:0
|
|||||||
test_gnc_timezone (void)
|
test_gnc_timezone (void)
|
||||||
{
|
{
|
||||||
}*/
|
}*/
|
||||||
/* timespecFromTime64
|
/* timespecFromtime64
|
||||||
void
|
void
|
||||||
timespecFromTime64( Timespec *ts, time64 t )// C: 22 in 11 Local: 0:0:0
|
timespecFromtime64( Timespec *ts, time64 t )// C: 22 in 11 Local: 0:0:0
|
||||||
*/
|
*/
|
||||||
static void
|
/* static void
|
||||||
test_timespecFromTime64 (void)
|
test_timespecFromtime64 (void)
|
||||||
{
|
{
|
||||||
Timespec ts = {-9999, 0};
|
}*/
|
||||||
timespecFromTime64 (&ts, MINTIME - 1);
|
|
||||||
g_assert_cmpint (0, ==, ts.tv_sec);
|
|
||||||
timespecFromTime64 (&ts, MINTIME + 1);
|
|
||||||
g_assert_cmpint (MINTIME + 1, ==, ts.tv_sec);
|
|
||||||
timespecFromTime64 (&ts, MAXTIME + 1);
|
|
||||||
g_assert_cmpint (MAXTIME, ==, ts.tv_sec);
|
|
||||||
timespecFromTime64 (&ts, MAXTIME - 1);
|
|
||||||
g_assert_cmpint (MAXTIME - 1, ==, ts.tv_sec);
|
|
||||||
}
|
|
||||||
/* timespec_now
|
/* timespec_now
|
||||||
Timespec
|
Timespec
|
||||||
timespec_now()// C: 2 in 2 Local: 0:0:0
|
timespec_now()// C: 2 in 2 Local: 0:0:0
|
||||||
@ -2466,7 +2474,7 @@ test_suite_gnc_date (void)
|
|||||||
GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec end", test_gnc_dmy2timespec_end);
|
GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec end", test_gnc_dmy2timespec_end);
|
||||||
GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec Neutral", test_gnc_dmy2timespec_neutral);
|
GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec Neutral", test_gnc_dmy2timespec_neutral);
|
||||||
// GNC_TEST_ADD_FUNC (suitename, "gnc timezone", test_gnc_timezone);
|
// GNC_TEST_ADD_FUNC (suitename, "gnc timezone", test_gnc_timezone);
|
||||||
GNC_TEST_ADD_FUNC (suitename, "timespecFromTime64", test_timespecFromTime64);
|
// GNC_TEST_ADD_FUNC (suitename, "timespecFromTime t", test_timespecFromtime64);
|
||||||
// GNC_TEST_ADD_FUNC (suitename, "timespec now", test_timespec_now);
|
// GNC_TEST_ADD_FUNC (suitename, "timespec now", test_timespec_now);
|
||||||
// GNC_TEST_ADD_FUNC (suitename, "timespecToTime t", test_timespecTotime64);
|
// GNC_TEST_ADD_FUNC (suitename, "timespecToTime t", test_timespecTotime64);
|
||||||
GNC_TEST_ADD_FUNC (suitename, "timespec to gdate", test_timespec_to_gdate);
|
GNC_TEST_ADD_FUNC (suitename, "timespec to gdate", test_timespec_to_gdate);
|
||||||
|
Loading…
Reference in New Issue
Block a user