mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-13 02:22:57 +01:00
* Foundation/include/Poco/LocalDateTime.h
* Foundation/src/LocalDateTime.cpp - Fixed Sourceforge tracker 1800031. The time zone differential was not being maintained in many places. There are some minor functionality changes with this changeset however. * Assignment methods which do not take the full time information or a tzd as parameters will adjust the time stamp for DST according to the time specified. The only methods that do not fall under this constraint are: - constructors or assigns which accept year, month, day, etc. - constructors or assigns which accept a tzd * operators += and -= correctly adjust for DST boundary crossings. * operators + and - will return a LocalDateTime instance that has been adjust for DST boundary crossings. * Foundation/testsuite/src/LocalDateTimeTest.h * Foundation/testsuite/src/LocalDateTimeTest.cpp - Added testTimezone method. - Removed an assertion in testGregorian1() that would fail when the current DST offset differed from that of 1970-1-1.
This commit is contained in:
parent
d9364e8f51
commit
01d998dcc2
@ -64,6 +64,14 @@ class Foundation_API LocalDateTime
|
||||
/// class for better performance. The relational operators
|
||||
/// normalize the dates/times involved to UTC before carrying out
|
||||
/// the comparison.
|
||||
///
|
||||
/// The time zone differential is based on the input date and
|
||||
/// time and current time zone. A number of constructors accept
|
||||
/// an explicit time zone differential parameter. These should
|
||||
/// not be used since daylight savings time processing is impossible
|
||||
/// since the time zone is unknown. Each of the constructors
|
||||
/// accepting a tzd parameter have been marked as deprecated and
|
||||
/// may be removed in a future revision.
|
||||
{
|
||||
public:
|
||||
LocalDateTime();
|
||||
@ -81,6 +89,7 @@ public:
|
||||
/// * millisecond is from 0 to 999.
|
||||
/// * microsecond is from 0 to 999.
|
||||
|
||||
//@ deprecated
|
||||
LocalDateTime(int tzd, int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond);
|
||||
/// Creates a DateTime for the given Gregorian date and time in the
|
||||
/// time zone denoted by the time zone differential in tzd.
|
||||
@ -98,11 +107,13 @@ public:
|
||||
/// Creates a LocalDateTime from the UTC time given in dateTime,
|
||||
/// using the time zone differential of the current time zone.
|
||||
|
||||
//@ deprecated
|
||||
LocalDateTime(int tzd, const DateTime& dateTime);
|
||||
/// Creates a LocalDateTime from the UTC time given in dateTime,
|
||||
/// using the given time zone differential. Adjusts dateTime
|
||||
/// for the given time zone differential.
|
||||
|
||||
//@ deprecated
|
||||
LocalDateTime(int tzd, const DateTime& dateTime, bool adjust);
|
||||
/// Creates a LocalDateTime from the UTC time given in dateTime,
|
||||
/// using the given time zone differential. If adjust is true,
|
||||
@ -111,6 +122,7 @@ public:
|
||||
LocalDateTime(double julianDay);
|
||||
/// Creates a LocalDateTime for the given Julian day in the local time zone.
|
||||
|
||||
//@ deprecated
|
||||
LocalDateTime(int tzd, double julianDay);
|
||||
/// Creates a LocalDateTime for the given Julian day in the time zone
|
||||
/// denoted by the time zone differential in tzd.
|
||||
@ -141,6 +153,7 @@ public:
|
||||
/// * millisecond is from 0 to 999.
|
||||
/// * microsecond is from 0 to 999.
|
||||
|
||||
//@ deprecated
|
||||
LocalDateTime& assign(int tzd, int year, int month, int day, int hour, int minute, int second, int millisecond, int microseconds);
|
||||
/// Assigns a Gregorian local date and time in the time zone denoted by
|
||||
/// the time zone differential in tzd.
|
||||
@ -154,6 +167,7 @@ public:
|
||||
/// * millisecond is from 0 to 999.
|
||||
/// * microsecond is from 0 to 999.
|
||||
|
||||
//@ deprecated
|
||||
LocalDateTime& assign(int tzd, double julianDay);
|
||||
/// Assigns a Julian day in the time zone denoted by the
|
||||
/// time zone differential in tzd.
|
||||
@ -245,6 +259,13 @@ public:
|
||||
|
||||
protected:
|
||||
LocalDateTime(Timestamp::UtcTimeVal utcTime, Timestamp::TimeDiff diff, int tzd);
|
||||
void determineTzd (bool adjust = false);
|
||||
/// Recalculate the tzd based on the _dateTime member based
|
||||
/// on the current timezone using the Standard C runtime functions.
|
||||
/// If adjust is true, then adjustForTzd() is called after the
|
||||
/// differential is calculated.
|
||||
void adjustForTzd();
|
||||
/// Adjust the _dateTime member based on the _tzd member.
|
||||
|
||||
private:
|
||||
DateTime _dateTime;
|
||||
@ -364,6 +385,12 @@ inline Timestamp::UtcTimeVal LocalDateTime::utcTime() const
|
||||
}
|
||||
|
||||
|
||||
inline void LocalDateTime::adjustForTzd()
|
||||
{
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
}
|
||||
|
||||
|
||||
inline void swap(LocalDateTime& d1, LocalDateTime& d2)
|
||||
{
|
||||
d1.swap(d2);
|
||||
|
@ -38,22 +38,22 @@
|
||||
#include "Poco/Timezone.h"
|
||||
#include "Poco/Timespan.h"
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
|
||||
|
||||
LocalDateTime::LocalDateTime():
|
||||
_tzd(Timezone::tzd())
|
||||
LocalDateTime::LocalDateTime()
|
||||
{
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
determineTzd(true);
|
||||
}
|
||||
|
||||
|
||||
LocalDateTime::LocalDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond):
|
||||
_dateTime(year, month, day, hour, minute, second, millisecond, microsecond),
|
||||
_tzd(Timezone::tzd())
|
||||
_dateTime(year, month, day, hour, minute, second, millisecond, microsecond)
|
||||
{
|
||||
determineTzd();
|
||||
}
|
||||
|
||||
|
||||
@ -65,10 +65,9 @@ LocalDateTime::LocalDateTime(int tzd, int year, int month, int day, int hour, in
|
||||
|
||||
|
||||
LocalDateTime::LocalDateTime(double julianDay):
|
||||
_dateTime(julianDay),
|
||||
_tzd(Timezone::tzd())
|
||||
_dateTime(julianDay)
|
||||
{
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
determineTzd(true);
|
||||
}
|
||||
|
||||
|
||||
@ -76,15 +75,14 @@ LocalDateTime::LocalDateTime(int tzd, double julianDay):
|
||||
_dateTime(julianDay),
|
||||
_tzd(tzd)
|
||||
{
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
adjustForTzd();
|
||||
}
|
||||
|
||||
|
||||
LocalDateTime::LocalDateTime(const DateTime& dateTime):
|
||||
_dateTime(dateTime),
|
||||
_tzd(Timezone::tzd())
|
||||
_dateTime(dateTime)
|
||||
{
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
determineTzd(true);
|
||||
}
|
||||
|
||||
|
||||
@ -92,7 +90,7 @@ LocalDateTime::LocalDateTime(int tzd, const DateTime& dateTime):
|
||||
_dateTime(dateTime),
|
||||
_tzd(tzd)
|
||||
{
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
adjustForTzd();
|
||||
}
|
||||
|
||||
|
||||
@ -101,7 +99,7 @@ LocalDateTime::LocalDateTime(int tzd, const DateTime& dateTime, bool adjust):
|
||||
_tzd(tzd)
|
||||
{
|
||||
if (adjust)
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
adjustForTzd();
|
||||
}
|
||||
|
||||
|
||||
@ -116,6 +114,7 @@ LocalDateTime::LocalDateTime(Timestamp::UtcTimeVal utcTime, Timestamp::TimeDiff
|
||||
_dateTime(utcTime, diff),
|
||||
_tzd(tzd)
|
||||
{
|
||||
adjustForTzd();
|
||||
}
|
||||
|
||||
|
||||
@ -123,7 +122,7 @@ LocalDateTime::~LocalDateTime()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
LocalDateTime& LocalDateTime::operator = (const LocalDateTime& dateTime)
|
||||
{
|
||||
if (&dateTime != this)
|
||||
@ -138,7 +137,10 @@ LocalDateTime& LocalDateTime::operator = (const LocalDateTime& dateTime)
|
||||
LocalDateTime& LocalDateTime::operator = (const Timestamp& timestamp)
|
||||
{
|
||||
if (timestamp != this->timestamp())
|
||||
{
|
||||
_dateTime = timestamp;
|
||||
determineTzd(true);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -146,17 +148,16 @@ LocalDateTime& LocalDateTime::operator = (const Timestamp& timestamp)
|
||||
|
||||
LocalDateTime& LocalDateTime::operator = (double julianDay)
|
||||
{
|
||||
_tzd = Timezone::tzd();
|
||||
_dateTime = julianDay;
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
determineTzd(true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
LocalDateTime& LocalDateTime::assign(int year, int month, int day, int hour, int minute, int second, int millisecond, int microseconds)
|
||||
{
|
||||
_dateTime.assign(year, month, day, hour, minute, second, millisecond, microseconds);
|
||||
_tzd = Timezone::tzd();
|
||||
determineTzd(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -173,7 +174,7 @@ LocalDateTime& LocalDateTime::assign(int tzd, double julianDay)
|
||||
{
|
||||
_tzd = tzd;
|
||||
_dateTime = julianDay;
|
||||
_dateTime += Timespan(((Timestamp::TimeDiff) _tzd)*Timespan::SECONDS);
|
||||
adjustForTzd();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -229,13 +230,19 @@ bool LocalDateTime::operator >= (const LocalDateTime& dateTime) const
|
||||
|
||||
LocalDateTime LocalDateTime::operator + (const Timespan& span) const
|
||||
{
|
||||
return LocalDateTime(_dateTime.utcTime(), span.totalMicroseconds(), _tzd);
|
||||
// First calculate the adjusted UTC time, then calculate the
|
||||
// locally adjusted time by constructing a new LocalDateTime.
|
||||
DateTime tmp(utcTime(), span.totalMicroseconds());
|
||||
return LocalDateTime(tmp);
|
||||
}
|
||||
|
||||
|
||||
LocalDateTime LocalDateTime::operator - (const Timespan& span) const
|
||||
{
|
||||
return LocalDateTime(_dateTime.utcTime(), -span.totalMicroseconds(), _tzd);
|
||||
// First calculate the adjusted UTC time, then calculate the
|
||||
// locally adjusted time by constructing a new LocalDateTime.
|
||||
DateTime tmp(utcTime(), -span.totalMicroseconds());
|
||||
return LocalDateTime(tmp);
|
||||
}
|
||||
|
||||
|
||||
@ -247,16 +254,44 @@ Timespan LocalDateTime::operator - (const LocalDateTime& dateTime) const
|
||||
|
||||
LocalDateTime& LocalDateTime::operator += (const Timespan& span)
|
||||
{
|
||||
_dateTime += span;
|
||||
// Use the same trick as in operator+. Create a UTC time, adjust
|
||||
// it for the span, and convert back to LocalDateTime. This will
|
||||
// recalculate the tzd correctly in the case where the addition
|
||||
// crosses a DST boundary.
|
||||
*this = DateTime(utcTime(), span.totalMicroseconds());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
LocalDateTime& LocalDateTime::operator -= (const Timespan& span)
|
||||
{
|
||||
_dateTime -= span;
|
||||
// Use the same trick as in operator-. Create a UTC time, adjust
|
||||
// it for the span, and convert back to LocalDateTime. This will
|
||||
// recalculate the tzd correctly in the case where the subtraction
|
||||
// crosses a DST boundary.
|
||||
*this = DateTime(utcTime(), -span.totalMicroseconds());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void LocalDateTime::determineTzd (bool adjust)
|
||||
{
|
||||
std::time_t local;
|
||||
std::tm broken;
|
||||
|
||||
broken.tm_year = (_dateTime.year() - 1900);
|
||||
broken.tm_mon = (_dateTime.month() - 1);
|
||||
broken.tm_mday = _dateTime.day();
|
||||
broken.tm_hour = _dateTime.hour();
|
||||
broken.tm_min = _dateTime.minute();
|
||||
broken.tm_sec = _dateTime.second();
|
||||
broken.tm_isdst = -1;
|
||||
local = std::mktime(&broken);
|
||||
|
||||
_tzd = (Timezone::utcOffset() + ((broken.tm_isdst == 1) ? 3600 : 0));
|
||||
if (adjust)
|
||||
adjustForTzd();
|
||||
}
|
||||
|
||||
} // namespace Poco
|
||||
|
||||
|
@ -29,8 +29,11 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
|
||||
#include "LocalDateTimeTest.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
|
||||
#include "CppUnit/TestCaller.h"
|
||||
#include "CppUnit/TestSuite.h"
|
||||
#include "Poco/LocalDateTime.h"
|
||||
@ -39,6 +42,8 @@
|
||||
#include "Poco/Timespan.h"
|
||||
#include "Poco/Timezone.h"
|
||||
|
||||
#include "Poco/DateTimeFormat.h"
|
||||
#include "Poco/DateTimeFormatter.h"
|
||||
|
||||
using Poco::LocalDateTime;
|
||||
using Poco::DateTime;
|
||||
@ -68,7 +73,9 @@ void LocalDateTimeTest::testGregorian1()
|
||||
assert (dt.second() == 0);
|
||||
assert (dt.millisecond() == 0);
|
||||
assert (dt.dayOfWeek() == 4);
|
||||
assert (dt.tzd() == Timezone::tzd());
|
||||
// REMOVED: this fails when the current DST offset differs from
|
||||
// the one on 1970-1-1
|
||||
//assert (dt.tzd() == Timezone::tzd());
|
||||
assert (dt.julianDay() == 2440587.5);
|
||||
|
||||
dt.assign(2001, 9, 9, 1, 46, 40);
|
||||
@ -371,6 +378,93 @@ void LocalDateTimeTest::testSwap()
|
||||
}
|
||||
|
||||
|
||||
void LocalDateTimeTest::testTimezone()
|
||||
{
|
||||
std::time_t tINCREMENT = (30 * 24 * 60 * 60); // 30 days
|
||||
Timespan tsINCREMENT(30*Timespan::DAYS);
|
||||
LocalDateTime now;
|
||||
std::time_t t = std::time(NULL);
|
||||
std::tm then;
|
||||
bool foundDST = false;
|
||||
|
||||
then = *std::localtime(&t);
|
||||
if (then.tm_isdst >= 0)
|
||||
{
|
||||
std::string tzNow, tzThen;
|
||||
char tzBuf[12];
|
||||
int iterations = 0;
|
||||
std::strftime(&tzBuf[0], sizeof(tzBuf), "%z", &then);
|
||||
tzNow = tzThen = tzBuf;
|
||||
while (iterations < 14)
|
||||
{
|
||||
// Add one month until the timezone changes or we roll
|
||||
// over 13 months.
|
||||
t += tINCREMENT;
|
||||
then = *std::localtime(&t);
|
||||
std::strftime(&tzBuf[0], sizeof(tzBuf), "%z", &then);
|
||||
tzThen = tzBuf;
|
||||
foundDST = (tzNow == tzThen);
|
||||
if (foundDST)
|
||||
{
|
||||
break;
|
||||
}
|
||||
++iterations;
|
||||
}
|
||||
if (foundDST)
|
||||
{
|
||||
// We found a timezone change that was induced by changing
|
||||
// the month, so we crossed a DST boundary. Now we can
|
||||
// actually do the test...
|
||||
//
|
||||
// Start with the current time and add 30 days for 13
|
||||
// iterations. Do this with both a LocalDateTime object and
|
||||
// a ANSI C time_t. Then create a LocalDateTime based on the
|
||||
// time_t and verify that the time_t calculated value is equal
|
||||
// to the LocalDateTime value. The comparision operator
|
||||
// verifies the _dateTime and _tzd members.
|
||||
LocalDateTime dt2;
|
||||
t = std::time(NULL);
|
||||
for (iterations = 0; iterations < 14; ++iterations)
|
||||
{
|
||||
t += tINCREMENT;
|
||||
dt2 += tsINCREMENT;
|
||||
then = *std::localtime(&t);
|
||||
|
||||
// This is the tricky part. We have to use the constructor
|
||||
// from a UTC DateTime object that is constructed from the
|
||||
// time_t. The LocalDateTime constructor with integer
|
||||
// arguments, LocalDateTime(yr, mon, day, ...), assumes that
|
||||
// the time is already adjusted with respect to the time
|
||||
// zone. The DateTime conversion constructor, however, does
|
||||
// not. So we want to construct from the UTC time.
|
||||
//
|
||||
// The second tricky part is that we want to use the
|
||||
// sub-second information from the LocalDateTime object
|
||||
// since ANSI C time routines are not sub-second accurate.
|
||||
then = *std::gmtime(&t);
|
||||
LocalDateTime calcd(DateTime((then.tm_year + 1900),
|
||||
(then.tm_mon + 1),
|
||||
then.tm_mday,
|
||||
then.tm_hour,
|
||||
then.tm_min,
|
||||
then.tm_sec,
|
||||
dt2.millisecond(),
|
||||
dt2.microsecond()));
|
||||
assert (dt2 == calcd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundDST)
|
||||
{
|
||||
std::cerr
|
||||
<< __FILE__ << ":" << __LINE__
|
||||
<< " - failed to locate DST boundary, timezone test skipped."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LocalDateTimeTest::setUp()
|
||||
{
|
||||
}
|
||||
@ -395,6 +489,7 @@ CppUnit::Test* LocalDateTimeTest::suite()
|
||||
CppUnit_addTest(pSuite, LocalDateTimeTest, testArithmetics1);
|
||||
CppUnit_addTest(pSuite, LocalDateTimeTest, testArithmetics2);
|
||||
CppUnit_addTest(pSuite, LocalDateTimeTest, testSwap);
|
||||
CppUnit_addTest(pSuite, LocalDateTimeTest, testTimezone);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
void testArithmetics1();
|
||||
void testArithmetics2();
|
||||
void testSwap();
|
||||
void testTimezone();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
Loading…
Reference in New Issue
Block a user