Fix error

This commit is contained in:
Alexey Milovidov 2021-03-07 01:35:23 +03:00
parent 8d5d1b76fb
commit 0dbadc8d8e
2 changed files with 29 additions and 18 deletions

View File

@ -69,10 +69,18 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
cctz::time_zone::civil_lookup lookup = cctz_time_zone.lookup(date);
/// Ambiguity is possible if time was changed backwards at the midnight
/// (or after midnight time has been changed to the previous day, for example two hours backwards at 01:00).
/// Then midnight appears twice. Usually time change happens exactly at 00:00.
/// Then we should use the second midnight as the start of the day.
start_of_day = std::chrono::system_clock::to_time_t(lookup.post);
/// or after midnight time has been changed back to midnight, for example one hour backwards at 01:00
/// or after midnight time has been changed to the previous day, for example two hours backwards at 01:00
/// Then midnight appears twice. Usually time change happens exactly at 00:00 or 01:00.
/// If transition did not involve previous day, we should use the first midnight as the start of the day,
/// otherwise it's better to use the second midnight.
std::chrono::time_point start_of_day_time_point = lookup.trans < lookup.post
? lookup.post /* Second midnight appears after transition, so there was a piece of previous day after transition */
: lookup.pre;
start_of_day = std::chrono::system_clock::to_time_t(start_of_day_time_point);
Values & values = lut[i];
values.year = date.year();
@ -97,17 +105,13 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
values.time_at_offset_change_value = 0;
values.amount_of_offset_change_value = 0;
/// TODO: This partially ignores fractional offsets,
/// which may cause incorrect toRelativeHourNum() results for some timezones, namelly Europe/Minsk
/// when pre-May 2 1924 it had an offset of UTC+1:50, and after it was UTC+2h.
/// https://www.timeanddate.com/time/zone/belarus/minsk?syear=1900
if (start_of_day > 0 && start_of_day % 3600)
if (offset_is_whole_number_of_hours_everytime && start_of_day > 0 && start_of_day % 3600)
offset_is_whole_number_of_hours_everytime = false;
/// If UTC offset was changed this day.
/// Change in time zone without transition is possible, e.g. Moscow 1991 Sun, 31 Mar, 02:00 MSK to EEST
cctz::time_zone::civil_transition transition{};
if (cctz_time_zone.next_transition(lookup.post, &transition)
if (cctz_time_zone.next_transition(start_of_day_time_point - std::chrono::seconds(1), &transition)
&& transition.from.year() == date.year()
&& transition.from.month() == date.month()
&& transition.from.day() == date.day()
@ -116,8 +120,8 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
values.time_at_offset_change_value = (transition.from - cctz::civil_second(date)) / Values::OffsetChangeFactor;
values.amount_of_offset_change_value = (transition.to - transition.from) / Values::OffsetChangeFactor;
// std::cerr << time_zone << ", " << date << ": change from " << transition.from << " to " << transition.to << "\n";
// std::cerr << time_zone << ", " << date << ": change at " << values.time_at_offset_change() << " with " << values.amount_of_offset_change() << "\n";
std::cerr << time_zone << ", " << date << ": change from " << transition.from << " to " << transition.to << "\n";
std::cerr << time_zone << ", " << date << ": change at " << values.time_at_offset_change() << " with " << values.amount_of_offset_change() << "\n";
/// We don't support too large changes.
if (values.amount_of_offset_change_value > 24 * 4)

View File

@ -472,18 +472,25 @@ public:
}
/// NOTE: Assuming timezone offset is a multiple of 15 minutes.
inline time_t toStartOfMinute(time_t t) const { return (t + DATE_LUT_ADD) / 60 * 60 - DATE_LUT_ADD; }
inline time_t toStartOfFiveMinute(time_t t) const { return (t + DATE_LUT_ADD) / 300 * 300 - DATE_LUT_ADD; }
inline time_t toStartOfFifteenMinutes(time_t t) const { return (t + DATE_LUT_ADD) / 900 * 900 - DATE_LUT_ADD; }
inline time_t toStartOfMinute(time_t t) const { return roundDown(t, 60); }
inline time_t toStartOfFiveMinute(time_t t) const { return roundDown(t, 300); }
inline time_t toStartOfFifteenMinutes(time_t t) const { return roundDown(t, 900); }
/// NOTE: This most likely wrong for Nepal - it has offset 05:45. Australia/Eucla is also unfortunate.
inline time_t toStartOfTenMinutes(time_t t) const { return (t + DATE_LUT_ADD) / 600 * 600 - DATE_LUT_ADD; }
inline time_t toStartOfTenMinutes(time_t t) const
{
if (offset_is_whole_number_of_hours_everytime)
return roundDown(t, 600);
/// More complex logic is for Nepal - it has offset 05:45. Australia/Eucla is also unfortunate.
Int64 date = find(t).date;
return date + (t - date) / 600 * 600;
}
/// NOTE: Assuming timezone transitions are multiple of hours. Lord Howe Island in Australia is a notable exception.
inline time_t toStartOfHour(time_t t) const
{
if (offset_is_whole_number_of_hours_everytime)
return (t + DATE_LUT_ADD) / 3600 * 3600 - DATE_LUT_ADD;
return roundDown(t, 3600);
Int64 date = find(t).date;
return date + (t - date) / 3600 * 3600;