Fix offset handling in GncDateTime struct tm ctor, gnc_mktime, & gnc_timegm.

Tests now pass in all TZa from Honolulu to New Zealand.
This commit is contained in:
John Ralls 2017-12-12 08:42:32 -08:00
parent de4d1e9859
commit 6db0820771
5 changed files with 56 additions and 29 deletions

View File

@ -208,7 +208,8 @@ gnc_mktime (struct tm* time)
{
normalize_struct_tm (time);
GncDateTime gncdt(*time);
return static_cast<time64>(gncdt) - gncdt.offset();
*time = static_cast<struct tm>(gncdt);
return static_cast<time64>(gncdt);
}
catch(std::invalid_argument)
{
@ -222,7 +223,14 @@ gnc_timegm (struct tm* time)
try
{
normalize_struct_tm(time);
return static_cast<time64>(GncDateTime(*time));
GncDateTime gncdt(*time);
*time = static_cast<struct tm>(gncdt);
time->tm_sec -= gncdt.offset();
normalize_struct_tm(time);
#ifdef HAVE_STRUcT_TM_GMTOFF
time->tm_gmtoff = 0;
#endif
return static_cast<time64>(gncdt) - gncdt.offset();
}
catch(std::invalid_argument)
{

View File

@ -594,7 +594,6 @@ void gnc_tm_set_day_start (struct tm *tm)
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1;
}
/** The gnc_tm_set_day_middle() inline routine will set the appropriate
@ -609,7 +608,6 @@ void gnc_tm_set_day_middle (struct tm *tm)
tm->tm_hour = 12;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1;
}
/** The gnc_tm_set_day_end() inline routine will set the appropriate
@ -624,7 +622,6 @@ void gnc_tm_set_day_end (struct tm *tm)
tm->tm_hour = 23;
tm->tm_min = 59;
tm->tm_sec = 59;
tm->tm_isdst = -1;
}
/** The gnc_time64_get_day_start() routine will take the given time in

View File

@ -29,6 +29,7 @@ extern "C"
}
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/regex.hpp>
#include <libintl.h>
#include <map>
@ -163,12 +164,23 @@ LDT_from_struct_tm(const struct tm tm)
auto tdur = boost::posix_time::time_duration(tm.tm_hour, tm.tm_min,
tm.tm_sec, 0);
auto tz = tzp.get(tdate.year());
return LDT(PTime(tdate, tdur), tz);
LDT ldt(tdate, tdur, tz, LDTBase::EXCEPTION_ON_ERROR);
if (tm.tm_isdst == -1 && ldt.is_dst())
ldt += tz->dst_offset();
return ldt;
}
catch(boost::gregorian::bad_year)
{
throw(std::invalid_argument("Time value is outside the supported year range."));
}
catch(boost::local_time::time_label_invalid)
{
throw(std::invalid_argument("Struct tm does not resolve to a valid time."));
}
catch(boost::local_time::ambiguous_result)
{
throw(std::invalid_argument("Struct tm can resolve to more than one time."));
}
}
class GncDateTimeImpl

View File

@ -282,13 +282,15 @@ TEST(gnc_datetime_constructors, test_struct_tm_constructor)
const time64 time = 2394187200; //2045-11-13 12:00:00 Z
GncDateTime atime(tm);
EXPECT_EQ(static_cast<time64>(atime), time);
EXPECT_EQ(static_cast<time64>(atime) + atime.offset(), time);
const struct tm tm1 = static_cast<struct tm>(atime);
EXPECT_EQ(tm1.tm_year, tm.tm_year);
EXPECT_EQ(tm1.tm_mon, tm.tm_mon);
// We have to contort these a bit to handle offsets > 12, e.g. New Zealand during DST.
// EXPECT_EQ(tm1.tm_mday - (11 + atime.offset() / 3600) / 24 , tm.tm_mday);
// EXPECT_EQ((24 + tm1.tm_hour - atime.offset() / 3600) % 24, tm.tm_hour);
EXPECT_EQ(tm1.tm_mday, tm.tm_mday);
// We have to contort this a bit to handle offsets > 12, e.g. New Zealand during DST.
EXPECT_EQ((24 + tm1.tm_hour - atime.offset() / 3600) % 24, tm.tm_hour);
EXPECT_EQ(tm1.tm_hour, tm.tm_hour);
EXPECT_EQ(tm1.tm_min, tm.tm_min);
}
@ -324,8 +326,10 @@ TEST(gnc_datetime_constructors, test_gncdate_neutral_constructor)
TEST(gnc_datetime_functions, test_format)
{
GncDateTime atime(2394187200); //2045-11-13 12:00:00 Z
//Date only to finesse timezone issues. It will still fail in +12 DST.
EXPECT_EQ(atime.format("%d-%m-%Y"), "13-11-2045");
if ((atime.offset() / 3600) > 12)
EXPECT_EQ(atime.format("%d-%m-%Y"), "14-11-2045");
else
EXPECT_EQ(atime.format("%d-%m-%Y"), "13-11-2045");
}
TEST(gnc_datetime_functions, test_format_zulu)
@ -343,9 +347,10 @@ TEST(gnc_datetime_functions, test_date)
auto ymd = gncd.year_month_day();
EXPECT_EQ(ymd.year, 2045);
EXPECT_EQ(ymd.month, 11);
EXPECT_EQ(ymd.day, 13);
EXPECT_EQ(ymd.day - (12 + atime.offset() / 3600) / 24, 13);
}
/* This test works only in the America/LosAngeles time zone and
* there's no way at present to make it more flexible.
TEST(gnc_datetime_functions, test_timezone_offset)
{
@ -356,3 +361,4 @@ TEST(gnc_datetime_functions, test_timezone_offset)
GncDateTime gncdt3(1490525940); //26 Mar 2017
EXPECT_EQ(-25200, gncdt3.offset());
}
*/

View File

@ -268,10 +268,10 @@ test_gnc_mktime (void)
#endif
};
guint ind;
int offset = timegm(&time[4]) - mktime(&time[4]);
for (ind = 0; ind < G_N_ELEMENTS (time); ind++)
{
int offset = timegm(&time[ind]) - mktime(&time[ind]);
time64 secs = gnc_mktime (&time[ind]);
#if !PLATFORM(WINDOWS)
//The timezone database uses local time for some
@ -319,7 +319,8 @@ test_gnc_mktime_normalization (void)
#endif
};
guint ind;
int offset = timegm(&normal_time) - mktime(&normal_time);
time_t calc_timegm = timegm(&normal_time);
time_t calc_time = mktime(&normal_time);
for (ind = 0; ind < G_N_ELEMENTS (time); ind++)
{
time64 secs = gnc_mktime (&time[ind]);
@ -330,7 +331,7 @@ test_gnc_mktime_normalization (void)
g_assert_cmpint (time[ind].tm_mday, ==, normal_time.tm_mday);
g_assert_cmpint (time[ind].tm_mon, ==, normal_time.tm_mon);
g_assert_cmpint (time[ind].tm_year, ==, normal_time.tm_year);
g_assert_cmpint (secs, ==, ans - offset);
g_assert_cmpint (secs, ==, ans - (calc_timegm - calc_time));
}
}
@ -705,9 +706,11 @@ test_timespecCanonicalDayTime (void)
const int sec_per_day = 24 * 3600;
const int sec_per_mo = 30 * sec_per_day;
const time64 sec_per_yr = 365 * sec_per_day;
const time64 secs = 8 * 3600 + 43 * 60 + 11;
const time64 secs1 = 23 * sec_per_yr + 5 * sec_per_mo + 11 * sec_per_day + 8 * 3600 + 43 * 60 + 11;
const time64 secs2 = 21 * sec_per_yr + 11 * sec_per_mo + 19 * sec_per_day + 21 * 3600 + 9 * 60 + 48;
const time64 secs = 8 * 3600 + 43 * 60 + 11; /* 1970-01-01 08:43:11 Z */
const time64 secs1 = 23 * sec_per_yr + 5 * sec_per_mo +
11 * sec_per_day + 8 * 3600 + 43 * 60 + 11; /* 1993-05-11 08:43:60 Z */
const time64 secs2 = 21 * sec_per_yr + 11 * sec_per_mo +
19 * sec_per_day + 21 * 3600 + 9 * 60 + 48; /* 1991-11-19 21:09:48 Z */
Timespec t0 = { secs, 0 };
Timespec ta = { secs1, 0 };
@ -998,9 +1001,9 @@ test_qof_print_date_buff (void)
gchar buff[MAX_DATE_LENGTH], ans[MAX_DATE_LENGTH];
gchar *locale = g_strdup (setlocale (LC_TIME, NULL));
time64 time1 = 154440000; //1974-11-23 12:00:00
time64 time2 = -281188800; //1961-02-02 12:00:00
time64 time3 = 2381227200LL; //2045-06-16 12:00:00
time64 time1 = 154436399; //1974-11-23 10:59:59
time64 time2 = -281192401; //1961-02-02 10:59:59
time64 time3 = 2381223599LL; //2045-06-16 10:59:59
struct tm tm1 = {0, 0, 12, 23, 10, 74};
struct tm tm2 = {0, 0, 12, 2, 1, 61};
struct tm tm3 = {0, 0, 12, 16, 5, 145};
@ -1280,9 +1283,9 @@ test_qof_print_date (void)
{
gchar *locale = g_strdup (setlocale (LC_TIME, NULL));
char ans[MAX_DATE_LENGTH];
time64 time1 = 154440000; //1974-11-23 12:00:00
time64 time2 = -281188800; //1961-02-02 12:00:00
time64 time3 = 2381227200LL; //2045-06-16 12:00:00
time64 time1 = 154436399; //1974-11-23 10:59:59
time64 time2 = -281192401; //1961-02-02 10:59:59
time64 time3 = 2381223599LL; //2045-06-16 10:59:59
struct tm tm1 = {0, 0, 12, 23, 10, 74};
struct tm tm2 = {0, 0, 12, 2, 1, 61};
struct tm tm3 = {0, 0, 12, 16, 5, 145};
@ -1812,7 +1815,7 @@ static void
test_gnc_dmy2timespec (FixtureB *f, gconstpointer pData)
{
gchar *msg1 = "[qof_dmy2timespec()] Date computation error from Y-M-D 1257-7-2: Time value is outside the supported year range.";
gint loglevel = G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL;
gint loglevel = G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL;
gchar *logdomain = "qof.engine";
TestErrorStruct check = {loglevel, logdomain, msg1, 0};
GLogFunc hdlr = g_log_set_default_handler ((GLogFunc)test_null_handler, &check);
@ -1828,13 +1831,14 @@ test_gnc_dmy2timespec (FixtureB *f, gconstpointer pData)
#endif
Timespec r_t = gnc_dmy2timespec (f->test[i].day, f->test[i].mon,
f->test[i].yr);
int offset = gnc_mktime(&tm) - gnc_timegm(&tm);
struct tm time1 = tm, time2 = tm;
int offset = gnc_mktime(&time1) - gnc_timegm(&time2);
if (f->test[i].secs == INT64_MAX)
/* We use INT64_MAX as invalid timespec.secs.
* As we can't *add* to the max, we can ignore the tz offset in this case. */
g_assert_cmpint (r_t.tv_sec, ==, INT64_MAX);
else
g_assert_cmpint (r_t.tv_sec, ==, f->test[i].secs + offset);
g_assert_cmpint (r_t.tv_sec, ==, f->test[i].secs - offset);
}
g_log_set_default_handler (hdlr, 0);
}
@ -1868,7 +1872,7 @@ test_gnc_dmy2timespec_end (FixtureB *f, gconstpointer pData)
* As we can't *add* to the max, we can ignore the tz offset in this case. */
g_assert_cmpint (r_t.tv_sec, ==, INT64_MAX);
else
g_assert_cmpint (r_t.tv_sec, ==, f->test[i].secs + offset);
g_assert_cmpint (r_t.tv_sec, ==, f->test[i].secs - offset);
}
g_log_set_default_handler (hdlr, 0);
}