#3509: fix dst and utcOffset handling for Dublin time zone

This commit is contained in:
Günter Obiltschnig 2022-03-27 21:25:51 +02:00
parent 1b5de92f09
commit 6aa29ade17
5 changed files with 110 additions and 55 deletions

View File

@ -58,6 +58,18 @@ public:
static std::string dstName(); static std::string dstName();
/// Returns the timezone name if daylight saving time is in effect. /// Returns the timezone name if daylight saving time is in effect.
#if !defined(POCO_OS_FAMILY_WINDOWS)
static int utcOffset(const Poco::Timestamp& timestamp);
/// Returns the offset of local time to UTC
/// for the given time, in seconds.
/// local time = UTC + utcOffset() + dst().
static int dst(const Poco::Timestamp& timestamp);
/// Returns the daylight saving time offset in seconds if
/// daylight saving time is in use for the given time.
/// local time = UTC + utcOffset() + dst().
#endif
}; };

View File

@ -277,7 +277,7 @@ void LocalDateTime::determineTzd(bool adjust)
if (err) broken = nullptr; if (err) broken = nullptr;
#endif #endif
if (!broken) throw Poco::SystemException("cannot get local time"); if (!broken) throw Poco::SystemException("cannot get local time");
_tzd = (Timezone::utcOffset() + ((broken->tm_isdst == 1) ? 3600 : 0)); _tzd = (Timezone::utcOffset() + Timezone::dst(_dateTime.timestamp()));
#else #else
std::tm broken; std::tm broken;
#if defined(POCO_VXWORKS) && (defined(_VXWORKS_COMPATIBILITY_MODE) || (defined(_WRS_VXWORKS_MAJOR) && ((_WRS_VXWORKS_MAJOR < 6) || ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR < 9))))) #if defined(POCO_VXWORKS) && (defined(_VXWORKS_COMPATIBILITY_MODE) || (defined(_WRS_VXWORKS_MAJOR) && ((_WRS_VXWORKS_MAJOR < 6) || ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR < 9)))))
@ -318,7 +318,7 @@ std::time_t LocalDateTime::dstOffset(int& dstOffset) const
local = std::mktime(&broken); local = std::mktime(&broken);
#endif #endif
dstOffset = (broken.tm_isdst == 1) ? 3600 : 0; dstOffset = (broken.tm_isdst == 1) ? Timezone::dst(_dateTime.timestamp()) : 0;
return local; return local;
} }

View File

@ -29,22 +29,12 @@ public:
tzset(); tzset();
} }
int timeZone() static long gmtOffset(const struct std::tm& t)
{ {
Poco::FastMutex::ScopedLock lock(_mutex); #if defined(__CYGWIN__)
return t.__TM_GMTOFF;
#if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) || POCO_OS == POCO_OS_ANDROID // no timezone global var
std::time_t now = std::time(NULL);
struct std::tm t;
gmtime_r(&now, &t);
std::time_t utc = std::mktime(&t);
return now - utc;
#elif defined(__CYGWIN__)
tzset();
return -_timezone;
#else #else
tzset(); return t.tm_gmtoff;
return -timezone;
#endif #endif
} }
@ -64,19 +54,39 @@ private:
static TZInfo tzInfo; static TZInfo tzInfo;
int Timezone::utcOffset(const Poco::Timestamp& timestamp)
{
std::time_t time = timestamp.epochTime();
struct std::tm local;
if (!localtime_r(&time, &local))
throw Poco::SystemException("cannot get UTC offset");
struct std::tm utc;
gmtime_r(&time, &utc);
std::time_t utctime = std::mktime(&utc);
return time - utctime;
}
int Timezone::utcOffset() int Timezone::utcOffset()
{ {
return tzInfo.timeZone(); return utcOffset(Poco::Timestamp());
} }
int Timezone::dst() int Timezone::dst()
{ {
std::time_t now = std::time(NULL); return dst(Poco::Timestamp());
struct std::tm t; }
if (!localtime_r(&now, &t))
int Timezone::dst(const Poco::Timestamp& timestamp)
{
std::time_t time = timestamp.epochTime();
struct std::tm local;
if (!localtime_r(&time, &local))
throw Poco::SystemException("cannot get local time DST offset"); throw Poco::SystemException("cannot get local time DST offset");
return t.tm_isdst == 1 ? 3600 : 0; long dst = TZInfo::gmtOffset(local) - utcOffset(timestamp);
return local.tm_isdst == 1 ? dst : 0;
} }

View File

@ -454,6 +454,37 @@ void LocalDateTimeTest::testTimezone()
} }
void LocalDateTimeTest::testTimezone2()
{
#if defined(POCO_OS_FAMILY_UNIX)
std::vector<std::string> timezones = {
"/usr/share/zoneinfo/America/Los_Angeles",
"/usr/share/zoneinfo/Europe/London",
"/usr/share/zoneinfo/Europe/Dublin", // special winter time of Ireland
"/usr/share/zoneinfo/Europe/Prague",
};
std::cout << "\n";
for (const std::string& tz : timezones)
{
setenv("TZ", tz.c_str(), 1); // POSIX-specific
std::vector<LocalDateTime> times = {
LocalDateTime(2022, 06, 29), // summer period
LocalDateTime(2022, 01, 29), // winter period
};
for (const LocalDateTime& ldt : times) {
std::time_t t = ldt.timestamp().epochTime();
std::tm then;
then = *std::localtime(&t);
assertTrue (then.tm_gmtoff == ldt.tzd());
}
unsetenv("TZ");
}
#endif
}
void LocalDateTimeTest::setUp() void LocalDateTimeTest::setUp()
{ {
} }
@ -479,6 +510,7 @@ CppUnit::Test* LocalDateTimeTest::suite()
CppUnit_addTest(pSuite, LocalDateTimeTest, testArithmetics2); CppUnit_addTest(pSuite, LocalDateTimeTest, testArithmetics2);
CppUnit_addTest(pSuite, LocalDateTimeTest, testSwap); CppUnit_addTest(pSuite, LocalDateTimeTest, testSwap);
CppUnit_addTest(pSuite, LocalDateTimeTest, testTimezone); CppUnit_addTest(pSuite, LocalDateTimeTest, testTimezone);
CppUnit_addTest(pSuite, LocalDateTimeTest, testTimezone2);
return pSuite; return pSuite;
} }

View File

@ -35,6 +35,7 @@ public:
void testArithmetics2(); void testArithmetics2();
void testSwap(); void testSwap();
void testTimezone(); void testTimezone();
void testTimezone2();
void setUp(); void setUp();
void tearDown(); void tearDown();