Skip to content

Commit 941f7cb

Browse files
authored
[libc++][TZDB] Fixes mapping of nonexisting time. (#127330)
All non-existing local times in a contiguous range should map to the same time point. This fixes a bug, were the times inside the range were mapped to the wrong time. Fixes: #113654
1 parent 7465647 commit 941f7cb

File tree

2 files changed

+21
-4
lines changed

2 files changed

+21
-4
lines changed

libcxx/include/__chrono/time_zone.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,14 @@ class _LIBCPP_AVAILABILITY_TZDB time_zone {
103103
to_sys(const local_time<_Duration>& __time, choose __z) const {
104104
local_info __info = get_info(__time);
105105
switch (__info.result) {
106-
case local_info::unique:
107-
case local_info::nonexistent: // first and second are the same
106+
case local_info::unique: // first and second are the same
108107
return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
109108

109+
case local_info::nonexistent:
110+
// first and second are the same
111+
// All non-existing values are converted to the same time.
112+
return sys_time<common_type_t<_Duration, seconds>>{__info.first.end};
113+
110114
case local_info::ambiguous:
111115
switch (__z) {
112116
case choose::earliest:

libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/to_sys_choose.pass.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static void test_nonexistent() {
8888
// Pick an historic date where it's well known what the time zone rules were.
8989
// This makes it unlikely updates to the database change these rules.
9090
std::chrono::local_time<std::chrono::seconds> time{
91-
(std::chrono::sys_days{std::chrono::March / 30 / 1986} + 2h + 30min).time_since_epoch()};
91+
(std::chrono::sys_days{std::chrono::March / 30 / 1986} + 2h).time_since_epoch()};
9292

9393
std::chrono::sys_seconds expected{time.time_since_epoch() - 1h};
9494

@@ -100,6 +100,13 @@ static void test_nonexistent() {
100100
assert(tz->to_sys(time + 0us, std::chrono::choose::latest) == expected);
101101
assert(tz->to_sys(time + 0ms, std::chrono::choose::earliest) == expected);
102102
assert(tz->to_sys(time + 0s, std::chrono::choose::latest) == expected);
103+
104+
// The entire nonexisting hour should map to the same time.
105+
// For nonexistant the value of std::chrono::choose has no effect.
106+
assert(tz->to_sys(time + 1s, std::chrono::choose::earliest) == expected);
107+
assert(tz->to_sys(time + 1min, std::chrono::choose::latest) == expected);
108+
assert(tz->to_sys(time + 30min, std::chrono::choose::earliest) == expected);
109+
assert(tz->to_sys(time + 59min + 59s, std::chrono::choose::latest) == expected);
103110
}
104111

105112
// Tests ambiguous conversions.
@@ -120,7 +127,7 @@ static void test_ambiguous() {
120127
// Pick an historic date where it's well known what the time zone rules were.
121128
// This makes it unlikely updates to the database change these rules.
122129
std::chrono::local_time<std::chrono::seconds> time{
123-
(std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h + 30min).time_since_epoch()};
130+
(std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h).time_since_epoch()};
124131

125132
std::chrono::sys_seconds earlier{time.time_since_epoch() - 2h};
126133
std::chrono::sys_seconds later{time.time_since_epoch() - 1h};
@@ -133,6 +140,12 @@ static void test_ambiguous() {
133140
assert(tz->to_sys(time + 0us, std::chrono::choose::latest) == later);
134141
assert(tz->to_sys(time + 0ms, std::chrono::choose::earliest) == earlier);
135142
assert(tz->to_sys(time + 0s, std::chrono::choose::latest) == later);
143+
144+
// Test times in the ambigious hour
145+
assert(tz->to_sys(time + 1s, std::chrono::choose::earliest) == earlier + 1s);
146+
assert(tz->to_sys(time + 1min, std::chrono::choose::latest) == later + 1min);
147+
assert(tz->to_sys(time + 30min, std::chrono::choose::earliest) == earlier + 30min);
148+
assert(tz->to_sys(time + 59min + 59s, std::chrono::choose::latest) == later + 59min + 59s);
136149
}
137150

138151
// This test does the basic validations of this function. The library function

0 commit comments

Comments
 (0)