#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

@ -32,32 +32,44 @@ public:
static int utcOffset(); static int utcOffset();
/// Returns the offset of local time to UTC, in seconds. /// Returns the offset of local time to UTC, in seconds.
/// local time = UTC + utcOffset() + dst(). /// local time = UTC + utcOffset() + dst().
static int dst(); static int dst();
/// Returns the daylight saving time offset in seconds if /// Returns the daylight saving time offset in seconds if
/// daylight saving time is in use. /// daylight saving time is in use.
/// local time = UTC + utcOffset() + dst(). /// local time = UTC + utcOffset() + dst().
static bool isDst(const Timestamp& timestamp); static bool isDst(const Timestamp& timestamp);
/// Returns true if daylight saving time is in effect /// Returns true if daylight saving time is in effect
/// for the given time. Depending on the operating system /// for the given time. Depending on the operating system
/// platform this might only work reliably for certain /// platform this might only work reliably for certain
/// date ranges, as the C library's localtime() function /// date ranges, as the C library's localtime() function
/// is used. /// is used.
static int tzd(); static int tzd();
/// Returns the time zone differential for the current timezone. /// Returns the time zone differential for the current timezone.
/// The timezone differential is computed as utcOffset() + dst() /// The timezone differential is computed as utcOffset() + dst()
/// and is expressed in seconds. /// and is expressed in seconds.
static std::string name(); static std::string name();
/// Returns the timezone name currently in effect. /// Returns the timezone name currently in effect.
static std::string standardName(); static std::string standardName();
/// Returns the timezone name if not daylight saving time is in effect. /// Returns the timezone name if not daylight saving time is in effect.
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

@ -28,34 +28,24 @@ public:
{ {
tzset(); tzset();
} }
int timeZone()
{
Poco::FastMutex::ScopedLock lock(_mutex);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) || POCO_OS == POCO_OS_ANDROID // no timezone global var static long gmtOffset(const struct std::tm& t)
std::time_t now = std::time(NULL); {
struct std::tm t; #if defined(__CYGWIN__)
gmtime_r(&now, &t); return t.__TM_GMTOFF;
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
} }
const char* name(bool dst) const char* name(bool dst)
{ {
Poco::FastMutex::ScopedLock lock(_mutex); Poco::FastMutex::ScopedLock lock(_mutex);
tzset(); tzset();
return tzname[dst ? 1 : 0]; return tzname[dst ? 1 : 0];
} }
private: private:
Poco::FastMutex _mutex; Poco::FastMutex _mutex;
}; };
@ -64,19 +54,39 @@ private:
static TZInfo tzInfo; static TZInfo tzInfo;
int Timezone::utcOffset() int Timezone::utcOffset(const Poco::Timestamp& timestamp)
{ {
return tzInfo.timeZone(); 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()
{
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;
} }
@ -88,19 +98,19 @@ bool Timezone::isDst(const Timestamp& timestamp)
return tms->tm_isdst > 0; return tms->tm_isdst > 0;
} }
std::string Timezone::name() std::string Timezone::name()
{ {
return std::string(tzInfo.name(dst() != 0)); return std::string(tzInfo.name(dst() != 0));
} }
std::string Timezone::standardName() std::string Timezone::standardName()
{ {
return std::string(tzInfo.name(false)); return std::string(tzInfo.name(false));
} }
std::string Timezone::dstName() std::string Timezone::dstName()
{ {
return std::string(tzInfo.name(true)); return std::string(tzInfo.name(true));

View File

@ -65,7 +65,7 @@ void LocalDateTimeTest::testGregorian1()
// the one on 1970-1-1 // the one on 1970-1-1
//assertTrue (dt.tzd() == Timezone::tzd()); //assertTrue (dt.tzd() == Timezone::tzd());
assertTrue (dt.julianDay() == 2440587.5); assertTrue (dt.julianDay() == 2440587.5);
dt.assign(2001, 9, 9, 1, 46, 40); dt.assign(2001, 9, 9, 1, 46, 40);
assertTrue (dt.year() == 2001); assertTrue (dt.year() == 2001);
assertTrue (dt.month() == 9); assertTrue (dt.month() == 9);
@ -92,7 +92,7 @@ void LocalDateTimeTest::testGregorian2()
assertTrue (dt.millisecond() == 0); assertTrue (dt.millisecond() == 0);
assertTrue (dt.dayOfWeek() == 4); assertTrue (dt.dayOfWeek() == 4);
assertTrue (dt.tzd() == 2*3600); assertTrue (dt.tzd() == 2*3600);
dt.assign(-7*3600, 2001, 9, 9, 1, 46, 40, 0, 0); dt.assign(-7*3600, 2001, 9, 9, 1, 46, 40, 0, 0);
assertTrue (dt.year() == 2001); assertTrue (dt.year() == 2001);
assertTrue (dt.month() == 9); assertTrue (dt.month() == 9);
@ -134,12 +134,12 @@ void LocalDateTimeTest::testConversions()
assertTrue (dt5.millisecond() == 234); assertTrue (dt5.millisecond() == 234);
assertTrue (dt5.dayOfWeek() == 5); assertTrue (dt5.dayOfWeek() == 5);
assertTrue (dt5.tzd() == -4*3600); assertTrue (dt5.tzd() == -4*3600);
DateTime dt6(2005, 1, 28, 14, 24, 44, 234, 0); DateTime dt6(2005, 1, 28, 14, 24, 44, 234, 0);
LocalDateTime dt7(3600, dt6); LocalDateTime dt7(3600, dt6);
LocalDateTime dt8(3600, dt6, false); LocalDateTime dt8(3600, dt6, false);
LocalDateTime dt9(3600, dt6, true); LocalDateTime dt9(3600, dt6, true);
assertTrue (dt7.hour() == 15); assertTrue (dt7.hour() == 15);
assertTrue (dt8.hour() == 14); assertTrue (dt8.hour() == 14);
assertTrue (dt9.hour() == 15); assertTrue (dt9.hour() == 15);
@ -169,9 +169,9 @@ void LocalDateTimeTest::testCalcs()
assertTrue (dt1.week(DateTime::MONDAY) == 1); assertTrue (dt1.week(DateTime::MONDAY) == 1);
dt1.assign(2007, 12, 31); dt1.assign(2007, 12, 31);
assertTrue (dt1.week(DateTime::MONDAY) == 53); assertTrue (dt1.week(DateTime::MONDAY) == 53);
// Jan 1 is Mon // Jan 1 is Mon
dt1.assign(2001, 1, 1); dt1.assign(2001, 1, 1);
assertTrue (dt1.week() == 1); assertTrue (dt1.week() == 1);
dt1.assign(2001, 1, 7); dt1.assign(2001, 1, 7);
assertTrue (dt1.week() == 1); assertTrue (dt1.week() == 1);
@ -205,7 +205,7 @@ void LocalDateTimeTest::testCalcs()
assertTrue (dt1.week() == 3); assertTrue (dt1.week() == 3);
dt1.assign(2003, 1, 20); dt1.assign(2003, 1, 20);
assertTrue (dt1.week() == 4); assertTrue (dt1.week() == 4);
// Jan 1 is Thu // Jan 1 is Thu
dt1.assign(2004, 1, 1); dt1.assign(2004, 1, 1);
assertTrue (dt1.week() == 1); assertTrue (dt1.week() == 1);
@ -241,7 +241,7 @@ void LocalDateTimeTest::testCalcs()
assertTrue (dt1.week() == 2); assertTrue (dt1.week() == 2);
dt1.assign(2000, 1, 17); dt1.assign(2000, 1, 17);
assertTrue (dt1.week() == 3); assertTrue (dt1.week() == 3);
// Jan 1 is Sun // Jan 1 is Sun
dt1.assign(1995, 1, 1); dt1.assign(1995, 1, 1);
assertTrue (dt1.week() == 0); assertTrue (dt1.week() == 0);
@ -262,7 +262,7 @@ void LocalDateTimeTest::testAMPM()
assertTrue (dt1.isAM()); assertTrue (dt1.isAM());
assertTrue (!dt1.isPM()); assertTrue (!dt1.isPM());
assertTrue (dt1.hourAMPM() == 12); assertTrue (dt1.hourAMPM() == 12);
dt1.assign(2005, 1, 1, 12, 15, 30); dt1.assign(2005, 1, 1, 12, 15, 30);
assertTrue (!dt1.isAM()); assertTrue (!dt1.isAM());
assertTrue (dt1.isPM()); assertTrue (dt1.isPM());
@ -280,14 +280,14 @@ void LocalDateTimeTest::testRelational1()
LocalDateTime dt1(2005, 1, 1, 0, 15, 30); LocalDateTime dt1(2005, 1, 1, 0, 15, 30);
LocalDateTime dt2(2005, 1, 2, 0, 15, 30); LocalDateTime dt2(2005, 1, 2, 0, 15, 30);
LocalDateTime dt3(dt1); LocalDateTime dt3(dt1);
assertTrue (dt1 < dt2); assertTrue (dt1 < dt2);
assertTrue (dt1 <= dt2); assertTrue (dt1 <= dt2);
assertTrue (dt2 > dt1); assertTrue (dt2 > dt1);
assertTrue (dt2 >= dt1); assertTrue (dt2 >= dt1);
assertTrue (dt1 != dt2); assertTrue (dt1 != dt2);
assertTrue (!(dt1 == dt2)); assertTrue (!(dt1 == dt2));
assertTrue (dt1 == dt3); assertTrue (dt1 == dt3);
assertTrue (!(dt1 != dt3)); assertTrue (!(dt1 != dt3));
assertTrue (dt1 >= dt3); assertTrue (dt1 >= dt3);
@ -309,7 +309,7 @@ void LocalDateTimeTest::testRelational2()
assertTrue (dt2 >= dt1); assertTrue (dt2 >= dt1);
assertTrue (dt1 != dt2); assertTrue (dt1 != dt2);
assertTrue (!(dt1 == dt2)); assertTrue (!(dt1 == dt2));
assertTrue (dt1 == dt3); assertTrue (dt1 == dt3);
assertTrue (!(dt1 != dt3)); assertTrue (!(dt1 != dt3));
assertTrue (dt1 >= dt3); assertTrue (dt1 >= dt3);
@ -323,13 +323,13 @@ void LocalDateTimeTest::testArithmetics1()
{ {
LocalDateTime dt1(2005, 1, 1, 0, 15, 30); LocalDateTime dt1(2005, 1, 1, 0, 15, 30);
LocalDateTime dt2(2005, 1, 2, 0, 15, 30); LocalDateTime dt2(2005, 1, 2, 0, 15, 30);
Timespan s = dt2 - dt1; Timespan s = dt2 - dt1;
assertTrue (s.days() == 1); assertTrue (s.days() == 1);
LocalDateTime dt3 = dt1 + s; LocalDateTime dt3 = dt1 + s;
assertTrue (dt3 == dt2); assertTrue (dt3 == dt2);
dt3 -= s; dt3 -= s;
assertTrue (dt3 == dt1); assertTrue (dt3 == dt1);
dt1 += s; dt1 += s;
@ -341,13 +341,13 @@ void LocalDateTimeTest::testArithmetics2()
{ {
LocalDateTime dt1(2*3600, 2005, 1, 1, 15, 30, 0, 0, 0); LocalDateTime dt1(2*3600, 2005, 1, 1, 15, 30, 0, 0, 0);
LocalDateTime dt2(5*3600, 2005, 1, 2, 18, 30, 0, 0, 0); LocalDateTime dt2(5*3600, 2005, 1, 2, 18, 30, 0, 0, 0);
Timespan s = dt2 - dt1; Timespan s = dt2 - dt1;
assertTrue (s.days() == 1); assertTrue (s.days() == 1);
LocalDateTime dt3 = dt1 + s; LocalDateTime dt3 = dt1 + s;
assertTrue (dt3 == dt2); assertTrue (dt3 == dt2);
dt3 -= s; dt3 -= s;
assertTrue (dt3 == dt1); assertTrue (dt3 == dt1);
dt1 += s; dt1 += s;
@ -359,7 +359,7 @@ void LocalDateTimeTest::testSwap()
{ {
LocalDateTime dt1(2005, 1, 1, 0, 15, 30); LocalDateTime dt1(2005, 1, 1, 0, 15, 30);
LocalDateTime dt2(2005, 1, 2, 0, 15, 30); LocalDateTime dt2(2005, 1, 2, 0, 15, 30);
assertTrue (dt1 < dt2); assertTrue (dt1 < dt2);
dt1.swap(dt2); dt1.swap(dt2);
assertTrue (dt2 < dt1); assertTrue (dt2 < dt1);
@ -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();