Skip to content

Commit 6939905

Browse files
committed
Address review comments.
1 parent 5ba9e6e commit 6939905

File tree

2 files changed

+42
-69
lines changed

2 files changed

+42
-69
lines changed

libcxx/include/__chrono/time_zone.h

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// Enable the contents of the header only when libc++ was built with experimental features enabled.
1717
#if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
1818

19+
# include <__chrono/calendar.h>
1920
# include <__chrono/duration.h>
2021
# include <__chrono/local_info.h>
2122
# include <__chrono/sys_info.h>

libcxx/src/time_zone.cpp

+41-69
Original file line numberDiff line numberDiff line change
@@ -904,48 +904,18 @@ time_zone::__get_info(sys_seconds __time) const {
904904
std::__throw_runtime_error("tzdb: corrupt db");
905905
}
906906

907-
enum class __position {
908-
__beginning,
909-
__middle,
910-
__end,
911-
};
912-
913-
// Determines the position of "__time" inside "__info".
914-
//
915-
// The code picks an arbitrary value to determine the "middle"
916-
// - Every time that is more than the threshold from a boundary, or
917-
// - Every value that is at the boundary sys_seconds::min() or
918-
// sys_seconds::max().
919-
//
920-
// If not in the middle, it returns __beginning or __end.
921-
[[nodiscard]] static __position __get_position(sys_seconds __time, const sys_info __info) {
922-
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
923-
__time >= __info.begin && __time < __info.end, "A value outside the range's position can't be determined.");
924-
925-
using _Tp = sys_seconds::rep;
926-
// Africa/Freetown has a 4 day "zone"
927-
// Africa/Freetown Fri Sep 1 00:59:59 1939 UT = Thu Aug 31 23:59:59 1939 -01 isdst=0 gmtoff=-3600
928-
// Africa/Freetown Fri Sep 1 01:00:00 1939 UT = Fri Sep 1 00:20:00 1939 -0040 isdst=1 gmtoff=-2400
929-
// Africa/Freetown Tue Sep 5 00:39:59 1939 UT = Mon Sep 4 23:59:59 1939 -0040 isdst=1 gmtoff=-2400
930-
// Africa/Freetown Tue Sep 5 00:40:00 1939 UT = Mon Sep 4 23:40:00 1939 -01 isdst=0 gmtoff=-3600
931-
//
932-
// Originally used a one week threshold, but due to this switched to 1 day.
933-
// This seems to work in practice.
934-
//
935-
// TODO TZDB Evaluate the proper threshold.
936-
constexpr _Tp __threshold = 24 * 3600;
937-
938-
_Tp __upper = std::__add_sat(__info.begin.time_since_epoch().count(), __threshold);
939-
if (__time >= __info.begin && __time.time_since_epoch().count() < __upper)
940-
return __info.begin != sys_seconds::min() ? __position::__beginning : __position::__middle;
941-
942-
_Tp __lower = std::__sub_sat(__info.end.time_since_epoch().count(), __threshold);
943-
if (__time < __info.end && __time.time_since_epoch().count() >= __lower)
944-
return __info.end != sys_seconds::max() ? __position::__end : __position::__middle;
907+
// Is the "__local_time" present in "__first" and "__second". If so the
908+
// local_info has an ambiguous result.
909+
[[nodiscard]] static bool
910+
__is_ambiguous(local_seconds __local_time, const sys_info& __first, const sys_info& __second) {
911+
std::chrono::local_seconds __end_first{__first.end.time_since_epoch() + __first.offset};
912+
std::chrono::local_seconds __begin_second{__second.begin.time_since_epoch() + __second.offset};
945913

946-
return __position::__middle;
914+
return __local_time < __end_first && __local_time >= __begin_second;
947915
}
948916

917+
// Determines the result of the "__local_time". This expects the object
918+
// "__first" to be earlier in time than "__second".
949919
[[nodiscard]] static local_info
950920
__get_info(local_seconds __local_time, const sys_info& __first, const sys_info& __second) {
951921
std::chrono::local_seconds __end_first{__first.end.time_since_epoch() + __first.offset};
@@ -993,16 +963,16 @@ time_zone::__get_info(local_seconds __local_time) const {
993963
* 2020.11.01 2021.04.01 2021.11.01
994964
* offset +05 offset +05 offset +05
995965
* save 0s save 1h save 0s
996-
* |-------------W----------|
997-
* |----------W--------------|
966+
* |------------//----------|
967+
* |---------//--------------|
998968
* |-------------
999969
* ~~ ^^
1000970
*
1001971
* These shifts can happen due to changes in the current time zone for a
1002972
* location. For example, Indian/Kerguelen switched only once. In 1950 from an
1003973
* offset of 0 hours to an offset of +05 hours.
1004974
*
1005-
* During all these shifts the UTC time will have not gaps.
975+
* During all these shifts the UTC time will not have gaps.
1006976
*/
1007977

1008978
// The code needs to determine the system time for the local time. There is no
@@ -1012,8 +982,9 @@ time_zone::__get_info(local_seconds __local_time) const {
1012982
sys_info __info = __get_info(__guess);
1013983

1014984
// At this point the offset can be used to determine an estimate for the local
1015-
// time. Before doing the determine the offset validate whether the local time
1016-
// is the range [chrono::local_seconds::min(), chrono::local_seconds::max()).
985+
// time. Before doing that, determine the offset and validate whether the
986+
// local time is the range [chrono::local_seconds::min(),
987+
// chrono::local_seconds::max()).
1017988
if (__local_seconds < 0s && __info.offset > 0s)
1018989
if (__local_seconds - chrono::local_seconds::min().time_since_epoch() < __info.offset)
1019990
return {-1, __info, {}};
@@ -1022,22 +993,23 @@ time_zone::__get_info(local_seconds __local_time) const {
1022993
if (chrono::local_seconds::max().time_since_epoch() - __local_seconds < -__info.offset)
1023994
return {-2, __info, {}};
1024995

1025-
// Based on the information in the sys_info found the local time can be
996+
// Based on the information found in the sys_info, the local time can be
1026997
// converted to a system time. This resulting time can be in the following
1027-
// locations of the sys_info found:
998+
// locations of the sys_info:
1028999
//
1029-
// |----------W--------------|
1030-
// 1 2 3 4 5
1000+
// |---------//--------------|
1001+
// 1 2.1 2.2 2.3 5
10311002
//
10321003
// 1. The estimate is before the returned sys_info object.
10331004
// The result is either non-existent or unique in the previous sys_info.
1034-
// 2. The estimate is in the beginning of the returned sys_info object.
1035-
// The result is either unique or ambiguous with the previous sys_info.
1036-
// 3. The estimate is in the "middle" of the returned sys_info.
1037-
// The result is unique.
1038-
// 4. The result is at the end of the returned sys_info object.
1039-
// The result is either unique or ambiguous with the next sys_info.
1040-
// 5. The estimate is after the returned sys_info object.
1005+
// 2. The estimate is in the sys_info object
1006+
// - If the sys_info begin is not sys_seconds::min(), then it might be at
1007+
// 2.1 and could be ambiguous with the previous or unique.
1008+
// - If sys_info end is not sys_seconds::max(), then it might be at 2.3
1009+
// and could be ambiguous with the next or unique.
1010+
// - Else it is at 2.2 and always unique. This case happens when a
1011+
// time zone has not transitions. For example, UTC or GMT+1.
1012+
// 3. The estimate is after the returned sys_info object.
10411013
// The result is either non-existent or unique in the next sys_info.
10421014
//
10431015
// There is no specification where the "middle" starts. Similar issues can
@@ -1051,31 +1023,31 @@ time_zone::__get_info(local_seconds __local_time) const {
10511023
//
10521024
// However the local_info object only has 2 sys_info objects, so this option
10531025
// is not tested.
1054-
//
1055-
// The positions 2, 3, or 4 are determined by __get_position. This function
1056-
// also contains the definition of "middle".
10571026

10581027
sys_seconds __sys_time{__local_seconds - __info.offset};
10591028
if (__sys_time < __info.begin)
10601029
// Case 1 before __info
10611030
return chrono::__get_info(__local_time, __get_info(__info.begin - 1s), __info);
10621031

10631032
if (__sys_time >= __info.end)
1064-
// Case 5 after __info
1033+
// Case 3 after __info
10651034
return chrono::__get_info(__local_time, __info, __get_info(__info.end));
10661035

1067-
switch (__get_position(__sys_time, __info)) {
1068-
case __position::__beginning: // Case 2
1069-
return chrono::__get_info(__local_time, __get_info(__info.begin - 1s), __info);
1070-
1071-
case __position::__middle: // Case 3
1072-
return {local_info::unique, __info, {}};
1073-
1074-
case __position::__end: // Case 4
1075-
return chrono::__get_info(__local_time, __info, __get_info(__info.end));
1036+
// Case 2 in __info
1037+
if (__info.begin != sys_seconds::min()) {
1038+
// Case 2.1 Not at the beginning, when not ambiguous the result should test
1039+
// case 2.3.
1040+
sys_info __prev = __get_info(__info.begin - 1s);
1041+
if (__is_ambiguous(__local_time, __prev, __info))
1042+
return {local_info::ambiguous, __prev, __info};
10761043
}
10771044

1078-
std::__libcpp_unreachable();
1045+
if (__info.end == sys_seconds::max())
1046+
// At the end so it's case 2.2
1047+
return {local_info::unique, __info, sys_info{}};
1048+
1049+
// This tests case 2.2 or case 2.3.
1050+
return chrono::__get_info(__local_time, __info, __get_info(__info.end));
10791051
}
10801052

10811053
} // namespace chrono

0 commit comments

Comments
 (0)