mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Fix DST calculation error.
The symptom was that in 2017 the PDT->PST transition was set a week late. The cause was that the timezone lookup function went the wrong way, finding the *next* timezone rule instead of the desired one because timezone rules are stored for the year that they start rather than when they end. Fix reverses the search to find the correct timezone rule. Commit includes new tests to detect the problem.
This commit is contained in:
parent
e66dd12aa9
commit
66e81040cb
@ -692,18 +692,18 @@ TimeZoneProvider::TimeZoneProvider(const std::string& tzname) : zone_vector {}
|
||||
{
|
||||
if(construct(tzname))
|
||||
return;
|
||||
std::cerr << tzname << " invalid, trying TZ environment variable.\n";
|
||||
DEBUG("%s invalid, trying TZ environment variable.\n", tzname.c_str());
|
||||
const char* tz_env = getenv("TZ");
|
||||
if(tz_env && construct(tz_env))
|
||||
return;
|
||||
std::cerr << "No valid $TZ, resorting to /etc/localtime.\n";
|
||||
DEBUG("No valid $TZ, resorting to /etc/localtime.\n");
|
||||
try
|
||||
{
|
||||
parse_file("/etc/localtime");
|
||||
}
|
||||
catch(const std::invalid_argument& env)
|
||||
{
|
||||
std::cerr << "/etc/localtime invalid, resorting to GMT.";
|
||||
DEBUG("/etc/localtime invalid, resorting to GMT.");
|
||||
TZ_Ptr zone(new PTZ("UTC0"));
|
||||
zone_vector.push_back(std::make_pair(max_year, zone));
|
||||
}
|
||||
@ -714,15 +714,12 @@ TimeZoneProvider::TimeZoneProvider(const std::string& tzname) : zone_vector {}
|
||||
TZ_Ptr
|
||||
TimeZoneProvider::get(int year) const noexcept
|
||||
{
|
||||
auto iter = find_if(zone_vector.begin(), zone_vector.end(),
|
||||
[=](TZ_Entry e) { return e.first >= year; });
|
||||
if (iter == zone_vector.end())
|
||||
auto iter = find_if(zone_vector.rbegin(), zone_vector.rend(),
|
||||
[=](TZ_Entry e) { return e.first <= year; });
|
||||
if (iter == zone_vector.rend())
|
||||
{
|
||||
/* This shouldn't happen, but if it does: */
|
||||
PERR("TimeZoneProvider::get was unable to get a timezone for year %d",
|
||||
year);
|
||||
if (!zone_vector.empty())
|
||||
return zone_vector.back().second;
|
||||
return zone_vector.front().second;
|
||||
return TZ_Ptr(new PTZ("UTC0"));
|
||||
}
|
||||
return iter->second;
|
||||
|
@ -95,3 +95,14 @@ TEST(gnc_datetime_functions, test_date)
|
||||
EXPECT_EQ(ymd.month, 11);
|
||||
EXPECT_EQ(ymd.day, 13);
|
||||
}
|
||||
|
||||
TEST(gnc_datetime_functions, test_timezone_offset)
|
||||
{
|
||||
|
||||
GncDateTime gncdt1(1488797940); //6 Mar 2017
|
||||
EXPECT_EQ(-28800, gncdt1.offset());
|
||||
GncDateTime gncdt2(1489661940); //16 Mar 2017 10:59 Z
|
||||
EXPECT_EQ(-25200, gncdt2.offset());
|
||||
GncDateTime gncdt3(1490525940); //26 Mar 2017
|
||||
EXPECT_EQ(-25200, gncdt3.offset());
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ TEST(gnc_timezone_constructors, test_pacific_time_constructor)
|
||||
std::string timezone("America/Los_Angeles");
|
||||
#endif
|
||||
TimeZoneProvider tzp (timezone);
|
||||
EXPECT_NO_THROW (tzp.get(2006));
|
||||
TZ_Ptr tz = tzp.get (2006);
|
||||
EXPECT_NO_THROW (tzp.get(2012));
|
||||
TZ_Ptr tz = tzp.get (2012);
|
||||
|
||||
EXPECT_FALSE(tz->std_zone_abbrev().empty());
|
||||
#if PLATFORM(WINDOWS)
|
||||
@ -54,7 +54,9 @@ TEST(gnc_timezone_constructors, test_pacific_time_constructor)
|
||||
EXPECT_TRUE(tz->std_zone_abbrev() == "PST");
|
||||
EXPECT_TRUE(tz->dst_zone_abbrev() == "PDT");
|
||||
#endif
|
||||
EXPECT_TRUE(tz->base_utc_offset().hours() == -8);
|
||||
EXPECT_EQ(-8, tz->base_utc_offset().hours());
|
||||
|
||||
EXPECT_EQ(12, tz->dst_local_start_time (2017).date().day());
|
||||
}
|
||||
|
||||
#if !PLATFORM(WINDOWS)
|
||||
|
@ -104,7 +104,7 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TimeMap test[8];
|
||||
TimeMap test[9];
|
||||
} FixtureB;
|
||||
|
||||
static void
|
||||
@ -118,6 +118,7 @@ setup_begin(FixtureB *f, gconstpointer pData)
|
||||
f->test[5] = (TimeMap){2017, 02, 29, INT64_C(1488326400)}; /*invalid day*/
|
||||
f->test[6] = (TimeMap){2017, 02, 33, INT64_C(1488672000)}; /*invalid day*/
|
||||
f->test[7] = (TimeMap){2017, 13, 29, INT64_C(1517184000)}; /*invalid month*/
|
||||
f->test[8] = (TimeMap){2017, 03, 16, INT64_C(1489622400)};
|
||||
}
|
||||
|
||||
static void
|
||||
@ -131,6 +132,7 @@ setup_neutral(FixtureB *f, gconstpointer pData)
|
||||
f->test[5] = (TimeMap){2017, 02, 29, INT64_MAX};
|
||||
f->test[6] = (TimeMap){2017, 02, 33, INT64_MAX};
|
||||
f->test[7] = (TimeMap){2017, 13, 29, INT64_MAX};
|
||||
f->test[8] = (TimeMap){2017, 03, 16, INT64_C(1489661940)};
|
||||
}
|
||||
|
||||
static void
|
||||
@ -144,6 +146,7 @@ setup_end(FixtureB *f, gconstpointer pData)
|
||||
f->test[5] = (TimeMap){2017, 02, 29, INT64_C(1488412799)};
|
||||
f->test[6] = (TimeMap){2017, 02, 33, INT64_C(1488758399)};
|
||||
f->test[7] = (TimeMap){2017, 13, 29, INT64_C(1517270399)};
|
||||
f->test[8] = (TimeMap){2017, 03, 16, INT64_C(1489708799)};
|
||||
}
|
||||
|
||||
void test_suite_gnc_date ( void );
|
||||
@ -1965,7 +1968,7 @@ Timespec gdate_to_timespec (GDate d)// C: 7 in 6 Local: 0:0:0
|
||||
static void
|
||||
test_gdate_to_timespec (FixtureB *f, gconstpointer pData)
|
||||
{
|
||||
|
||||
|
||||
gchar *msg = "g_date_set_dmy: assertion 'g_date_valid_dmy (day, m, y)' failed";
|
||||
gint loglevel = G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL;
|
||||
gchar *logdomain = G_LOG_DOMAIN;
|
||||
@ -1974,12 +1977,18 @@ test_gdate_to_timespec (FixtureB *f, gconstpointer pData)
|
||||
g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_checked_handler, &check);
|
||||
for (int i = 0; i < sizeof(f->test)/sizeof(TimeMap); ++i)
|
||||
{
|
||||
GDate gd;
|
||||
GDate gd, gd2;
|
||||
Timespec r_t;
|
||||
g_date_clear(&gd, 1);
|
||||
g_date_clear(&gd2, 1);
|
||||
g_date_set_dmy(&gd, f->test[i].day, f->test[i].mon, f->test[i].yr);
|
||||
r_t = gdate_to_timespec(gd);
|
||||
g_assert_cmpint (r_t.tv_sec, ==, f->test[i].secs);
|
||||
if (f->test[i].secs < INT64_MAX)
|
||||
{
|
||||
gd2 = timespec_to_gdate(r_t);
|
||||
g_assert (g_date_compare (&gd2, &gd) == 0);
|
||||
}
|
||||
}
|
||||
g_log_set_default_handler (hdlr, 0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user