#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();
/// Returns the offset of local time to UTC, in seconds.
/// local time = UTC + utcOffset() + dst().
static int dst();
/// Returns the daylight saving time offset in seconds if
/// daylight saving time is in use.
/// local time = UTC + utcOffset() + dst().
static bool isDst(const Timestamp& timestamp);
/// Returns true if daylight saving time is in effect
/// for the given time. Depending on the operating system
/// platform this might only work reliably for certain
/// date ranges, as the C library's localtime() function
/// is used.
static int tzd();
/// Returns the time zone differential for the current timezone.
/// The timezone differential is computed as utcOffset() + dst()
/// and is expressed in seconds.
static std::string name();
/// Returns the timezone name currently in effect.
static std::string standardName();
/// Returns the timezone name if not daylight saving time is in effect.
static std::string dstName();
/// 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;
#endif
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
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)))))
@ -318,7 +318,7 @@ std::time_t LocalDateTime::dstOffset(int& dstOffset) const
local = std::mktime(&broken);
#endif
dstOffset = (broken.tm_isdst == 1) ? 3600 : 0;
dstOffset = (broken.tm_isdst == 1) ? Timezone::dst(_dateTime.timestamp()) : 0;
return local;
}

View File

@ -28,34 +28,24 @@ public:
{
tzset();
}
int timeZone()
{
Poco::FastMutex::ScopedLock lock(_mutex);
#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;
static long gmtOffset(const struct std::tm& t)
{
#if defined(__CYGWIN__)
return t.__TM_GMTOFF;
#else
tzset();
return -timezone;
return t.tm_gmtoff;
#endif
}
const char* name(bool dst)
{
Poco::FastMutex::ScopedLock lock(_mutex);
tzset();
tzset();
return tzname[dst ? 1 : 0];
}
private:
Poco::FastMutex _mutex;
};
@ -64,19 +54,39 @@ private:
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()
{
std::time_t now = std::time(NULL);
struct std::tm t;
if (!localtime_r(&now, &t))
return dst(Poco::Timestamp());
}
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");
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;
}
std::string Timezone::name()
{
return std::string(tzInfo.name(dst() != 0));
}
std::string Timezone::standardName()
{
return std::string(tzInfo.name(false));
}
std::string Timezone::dstName()
{
return std::string(tzInfo.name(true));

View File

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

View File

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