gnc-timezone: Enable POSIX-style timezone strings on Unix systems.

E.g. CEST01CEDT,M4.1.0,M10.1.0.

Also reorder priority of finding TZ information: Passed-in timezone string
is tried first; if that's empty or fails, try the TZ variable, then
/etc/localtime, and finally give up and use UTC. Exceptions never leave
GncTimeZoneProvider and *some* timezone is always returned.

Provide a new test for posix timezones and invert the exception test.
This commit is contained in:
John Ralls 2016-09-27 15:17:39 +02:00
parent a7ca20572d
commit fd37051865
3 changed files with 48 additions and 12 deletions

View File

@ -377,15 +377,6 @@ namespace IANAParser
}
}
if (!ifs.is_open())
{
if (!tzname.empty())
std::cerr << "Failed to open time zone " << tzname <<
"; No such file. Falling back on system default timezone.\n";
ifs.open("/etc/localtime",
std::ios::in|std::ios::binary|std::ios::ate);
}
if (! ifs.is_open())
throw std::invalid_argument("The timezone string failed to resolve to a valid filename");
std::streampos filesize = ifs.tellg();
@ -609,7 +600,8 @@ zone_from_rule(int year, DSTRule::DSTRule rule)
return std::make_pair(year, tz);
}
TimeZoneProvider::TimeZoneProvider(const std::string& tzname) : zone_vector {}
void
TimeZoneProvider::parse_file(const std::string& tzname)
{
IANAParser::IANAParser parser(tzname);
auto last_info = std::find_if(parser.tzinfo.begin(), parser.tzinfo.end(),
@ -675,6 +667,34 @@ TimeZoneProvider::TimeZoneProvider(const std::string& tzname) : zone_vector {}
zone_vector.push_back(zone_from_rule(max_year, last_rule));
}
TimeZoneProvider::TimeZoneProvider(const std::string& tzname) : zone_vector {}
{
try
{
parse_file(tzname);
}
catch(const std::invalid_argument& err)
{
try
{
TZ_Ptr zone(new PTZ(tzname));
zone_vector.push_back(std::make_pair(max_year, zone));
}
catch(const std::exception& err)
{
std::cerr << "Unable to use either provided tzname or TZ environment variable. Resorting to /etc/localtime.\n";
try
{
parse_file("/etc/localtime");
}
catch(const std::invalid_argument& env)
{
std::cerr << "/etc/localtime invalid, resorting to GMT.";
TZ_Ptr zone(new PTZ("UTC0"));
zone_vector.push_back(std::make_pair(max_year, zone));
}
}
}
}
#endif

View File

@ -58,6 +58,7 @@ public:
static const unsigned int min_year; //1400
static const unsigned int max_year; //9999
private:
void parse_file(const std::string& tzname);
TZ_Vector zone_vector;
#if PLATFORM(WINDOWS)
void load_windows_dynamic_tz(HKEY, time_zone_names);

View File

@ -57,8 +57,23 @@ TEST(gnc_timezone_constructors, test_pacific_time_constructor)
EXPECT_TRUE(tz->base_utc_offset().hours() == -8);
}
#if !PLATFORM(WINDOWS)
TEST(gnc_timezone_constructors, test_posix_timezone)
{
std::string timezone("FST08FDT07,M4.1.0,M10.31.0");
TimeZoneProvider tzp(timezone);
TZ_Ptr tz = tzp.get(2006);
EXPECT_TRUE(tz->std_zone_abbrev() == "FST");
EXPECT_TRUE(tz->dst_zone_abbrev() == "FDT");
EXPECT_TRUE(tz->base_utc_offset().hours() == 8L);
EXPECT_TRUE(tz->dst_offset().hours() == 7L);
}
#endif
TEST(gnc_timezone_constructors, test_bogus_time_constructor)
{
EXPECT_THROW (TimeZoneProvider tzp ("New York Standard Time"),
std::invalid_argument);
TimeZoneProvider tzp ("New York Standard Time");
TimeZoneProvider machine ("");
EXPECT_TRUE(machine.get(2006)->std_zone_abbrev() ==
tzp.get(2006)->std_zone_abbrev());
}