fix: consider timezone correctly

explicitly set tm.tm_isdst = 0 and use UTC offset
This commit is contained in:
ToruNiina
2019-12-06 20:57:51 +09:00
parent 5a8d368927
commit 62c01f9826

View File

@@ -470,37 +470,64 @@ struct offset_datetime
: date(ld.date), time(ld.time), offset(get_local_offset()) : date(ld.date), time(ld.time), offset(get_local_offset())
{} {}
explicit offset_datetime(const std::chrono::system_clock::time_point& tp) explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset_datetime(local_datetime(tp)) : offset(0, 0) // use gmtime
{} {
const auto timet = std::chrono::system_clock::to_time_t(tp);
const auto tm = detail::gmtime_s(&timet);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::time_t& t) explicit offset_datetime(const std::time_t& t)
: offset_datetime(local_datetime(t)) : offset(0, 0) // use gmtime
{} {
const auto tm = detail::gmtime_s(&t);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::tm& t) explicit offset_datetime(const std::tm& t)
: offset_datetime(local_datetime(t)) : offset(0, 0) // assume gmtime
{} {
this->date = local_date(t);
this->time = local_time(t);
}
operator std::chrono::system_clock::time_point() const operator std::chrono::system_clock::time_point() const
{ {
// get date-time // get date-time
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.
std::tm t;
t.tm_sec = 0;
t.tm_min = 0;
t.tm_hour = 0;
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 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.
// all the offset info, including DST, should be in the offset part.
std::chrono::system_clock::time_point tp = std::chrono::system_clock::time_point tp =
std::chrono::system_clock::time_point(this->date) + std::chrono::system_clock::from_time_t(std::mktime(&t)) +
std::chrono::duration_cast<internal_duration>( std::chrono::duration_cast<internal_duration>(
std::chrono::nanoseconds(this->time)); std::chrono::nanoseconds(this->time));
// get date-time in UTC. let's say we are in +09:00 (JPN). // get date-time in UTC.
// writing 12:00:00 in +09:00 means 03:00:00Z. to represent // Since mktime uses local time zone, it should be corrected.
// 12:00:00Z, first we need to add +09:00. // `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
// to add `+09:00` to `03:00:00Z`.
const auto ofs = get_local_offset(); const auto ofs = get_local_offset();
tp += std::chrono::hours (ofs.hour); tp += std::chrono::hours (ofs.hour);
tp += std::chrono::minutes(ofs.minute); tp += std::chrono::minutes(ofs.minute);
// here, tp represents 12:00:00 in UTC but we have offset information. // We got `12:00:00Z` by correcting local timezone applied by mktime.
// we need to subtract it. For example, let's say the input is // Then we will apply the offset. Let's say `12:00:00-08:00` is given.
// 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
// conversion. But the actual time we need to return is 20:00:00Z // So we need to subtract the offset.
// because of -08:00.
tp -= std::chrono::minutes(this->offset); tp -= std::chrono::minutes(this->offset);
return tp; return tp;
} }