fix: use datetime info while getting time offset

to convert offset_datetime to system_clock::time_point.
This commit is contained in:
ToruNiina
2019-12-08 22:44:12 +11:00
parent b246f5ac5c
commit 331de4ea5d

View File

@@ -488,7 +488,8 @@ struct offset_datetime
: date(dt.date), time(dt.time), offset(o) : date(dt.date), time(dt.time), offset(o)
{} {}
explicit offset_datetime(const local_datetime& ld) explicit offset_datetime(const local_datetime& ld)
: date(ld.date), time(ld.time), offset(get_local_offset()) : date(ld.date), time(ld.time), offset(get_local_offset(nullptr))
// use the current local timezone offset
{} {}
explicit offset_datetime(const std::chrono::system_clock::time_point& tp) explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset(0, 0) // use gmtime : offset(0, 0) // use gmtime
@@ -518,30 +519,33 @@ struct offset_datetime
using internal_duration = using internal_duration =
typename std::chrono::system_clock::time_point::duration; typename std::chrono::system_clock::time_point::duration;
// std::mktime returns date as **local** time zone. // first, convert it to local date-time information in the same way as
// local_datetime does. later we will use time_t to adjust time offset.
std::tm t; std::tm t;
t.tm_sec = 0; t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = 0; t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = 0; t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day); t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month); t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900; t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored
t.tm_isdst = 0; // do not consider DST; explicitly turn it off. t.tm_isdst = -1;
// all the offset info, including DST, should be in the offset part. const std::time_t tp_loc = std::mktime(std::addressof(t));
std::chrono::system_clock::time_point tp = auto tp = std::chrono::system_clock::from_time_t(tp_loc);
std::chrono::system_clock::from_time_t(std::mktime(&t)) + tp += std::chrono::duration_cast<internal_duration>(
std::chrono::duration_cast<internal_duration>( std::chrono::milliseconds(this->time.millisecond) +
std::chrono::nanoseconds(this->time)); std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
// get date-time in UTC.
// Since mktime uses local time zone, it should be corrected. // Since mktime uses local time zone, it should be corrected.
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
// to add `+09:00` to `03:00:00Z`. // to add `+09:00` to `03:00:00Z`.
const auto ofs = get_local_offset(); // Here, it uses the time_t converted from date-time info to handle
// daylight saving time.
const auto ofs = get_local_offset(std::addressof(tp_loc));
tp += std::chrono::hours (ofs.hour); tp += std::chrono::hours (ofs.hour);
tp += std::chrono::minutes(ofs.minute); tp += std::chrono::minutes(ofs.minute);
@@ -568,11 +572,10 @@ struct offset_datetime
private: private:
static time_offset get_local_offset() static time_offset get_local_offset(const std::time_t* tp)
{ {
// get current timezone // get local timezone with the same date-time information as mktime
const auto tmp1 = std::time(nullptr); const auto t = detail::localtime_s(tp);
const auto t = detail::localtime_s(&tmp1);
std::array<char, 6> buf; std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0