@@ -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