diff --git a/libgnucash/engine/gnc-datetime.cpp b/libgnucash/engine/gnc-datetime.cpp index 2bec6489ca..795ae5c4bb 100644 --- a/libgnucash/engine/gnc-datetime.cpp +++ b/libgnucash/engine/gnc-datetime.cpp @@ -262,48 +262,31 @@ GncDateTimeImpl::GncDateTimeImpl(const GncDateImpl& date, DayPart part) : } } +using PTZ = boost::local_time::posix_time_zone; + +static TZ_Ptr +tz_from_string(std::string str) +{ + if (str.empty()) return utc_zone; + std::string tzstr = "XXX" + str; + if (tzstr.length() > 6 && tzstr[6] != ':') //6 for XXXsHH, s is + or - + tzstr.insert(6, ":"); + if (tzstr.length() > 9 && tzstr[9] != ':') //9 for XXXsHH:MM + { + tzstr.insert(9, ":"); + } + return TZ_Ptr(new PTZ(tzstr)); +} + GncDateTimeImpl::GncDateTimeImpl(const std::string str) : m_time(unix_epoch, utc_zone) { if (str.empty()) return; - using std::string; - using PTZ = boost::local_time::posix_time_zone; - TZ_Ptr tzptr; auto tzpos = str.find_first_of("+-", str.find(":")); - int offset = 0L; - if (tzpos != str.npos) - { - string tzstr = "XXX" + str.substr(tzpos); - if (tzstr.length() > 6 && tzstr[6] != ':') //6 for XXXsHH, s is + or - - tzstr.insert(6, ":"); - if (tzstr.length() > 9 && tzstr[9] != ':') //9 for XXXsHH:MM - { - tzstr.insert(9, ":"); - /* Bug 767824: A GLib bug in parsing the UTC timezone on - * Windows may have created a bogus timezone of a random - * number of minutes. Since there are no fractional-hour - * timezones around the prime meridian we can safely check for - * this in files by looking for minutes-only offsets and - * making the appropriate correction. - */ - if (tzstr.compare(7,8, "00") && - (tzstr.length() > 10 ? tzstr[10] != '0' : - !tzstr.compare(10, 11, "00"))) - { - offset = atoi(tzstr.substr(10,11).c_str()); - if (offset && tzpos == '-') - offset = -offset; - tzstr.replace(10, 11, "00"); - } - } - tzptr.reset(new PTZ(tzstr)); - if (str[tzpos - 1] == ' ') --tzpos; - } - else - { - tzptr = utc_zone; - } + auto tzptr = tz_from_string(tzpos != str.npos ? str.substr(tzpos) : ""); + if (tzpos != str.npos && str[tzpos - 1] == ' ') --tzpos; + try { using Facet = boost::posix_time::time_input_facet; @@ -328,8 +311,15 @@ GncDateTimeImpl::GncDateTimeImpl(const std::string str) : { throw(std::invalid_argument("The date string was outside of the supported year range.")); } - if (offset) - m_time -= boost::posix_time::minutes(offset); + /* Bug 767824: A GLib bug in parsing the UTC timezone on Windows may have + * created a bogus timezone of a random number of minutes. Since there are + * no fractional-hour timezones around the prime meridian we can safely + * check for this in files by resetting to UTC if there's a + * less-than-an-hour offset. + */ + auto offset = tzptr->base_utc_offset().seconds(); + if (offset != 0 && std::abs(offset) < 3600) + m_time = m_time.local_time_in(utc_zone); } GncDateTimeImpl::operator time64() const @@ -460,7 +450,7 @@ GncDateTime::GncDateTime(const time64 time) : m_impl(new GncDateTimeImpl(time)) {} GncDateTime::GncDateTime(const struct tm tm) : m_impl(new GncDateTimeImpl(tm)) {} -GncDateTime::GncDateTime(const std::string str) : +GncDateTime::GncDateTime(const std::string str, std::string fmt) : m_impl(new GncDateTimeImpl(str)) {} GncDateTime::~GncDateTime() = default; diff --git a/libgnucash/engine/test/gtest-gnc-datetime.cpp b/libgnucash/engine/test/gtest-gnc-datetime.cpp index 350d22aaab..245562b6da 100644 --- a/libgnucash/engine/test/gtest-gnc-datetime.cpp +++ b/libgnucash/engine/test/gtest-gnc-datetime.cpp @@ -261,15 +261,38 @@ TEST(gnc_datetime_constructors, test_time64_constructor) TEST(gnc_datetime_constructors, test_string_constructor) { - std::string timestr("2015-12-05 00:01:00"); - GncDateTime time(timestr); - auto tm = time.utc_tm(); +/* Plain UTC date-time */ + std::string timestr("2015-12-05 11:57:03"); + GncDateTime time1(timestr); + auto tm = time1.utc_tm(); EXPECT_EQ(tm.tm_year, 115); EXPECT_EQ(tm.tm_mon, 11); EXPECT_EQ(tm.tm_mday, 5); - EXPECT_EQ(tm.tm_hour, 0); - EXPECT_EQ(tm.tm_min, 1); - EXPECT_EQ(tm.tm_sec, 0); + EXPECT_EQ(tm.tm_hour,11); + EXPECT_EQ(tm.tm_min, 57); + EXPECT_EQ(tm.tm_sec, 3); + +/* Datetime with an offset */ + timestr = "1993-07-22 15:21:19 +0300"; + GncDateTime time2(timestr); + tm = time2.utc_tm(); + EXPECT_EQ(tm.tm_year, 93); + EXPECT_EQ(tm.tm_mon, 6); + EXPECT_EQ(tm.tm_mday, 22); + EXPECT_EQ(tm.tm_hour, 12); + EXPECT_EQ(tm.tm_min, 21); + EXPECT_EQ(tm.tm_sec, 19); + +/* Bug 767824 date-time */ + timestr = "1993-07-22 15:21:19 +0013"; + GncDateTime time3(timestr); + tm = time3.utc_tm(); + EXPECT_EQ(tm.tm_year, 93); + EXPECT_EQ(tm.tm_mon, 6); + EXPECT_EQ(tm.tm_mday, 22); + EXPECT_EQ(tm.tm_hour, 15); + EXPECT_EQ(tm.tm_min, 8); + EXPECT_EQ(tm.tm_sec, 19); } TEST(gnc_datetime_constructors, test_struct_tm_constructor)