================ @@ -903,6 +904,180 @@ time_zone::__get_info(sys_seconds __time) const { std::__throw_runtime_error("tzdb: corrupt db"); } +enum class __position { + __beginning, + __middle, + __end, +}; + +// Determines the position of "__time" inside "__info". +// +// The code picks an arbitrary value to determine the "middle" +// - Every time that is more than the threshold from a boundary, or +// - Every value that is at the boundary sys_seconds::min() or +// sys_seconds::max(). +// +// If not in the middle, it returns __beginning or __end. +[[nodiscard]] static __position __get_position(sys_seconds __time, const sys_info __info) { + _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( + __time >= __info.begin && __time < __info.end, "A value outside the range's position can't be determined."); + + using _Tp = sys_seconds::rep; + // Africa/Freetown has a 4 day "zone" + // Africa/Freetown Fri Sep 1 00:59:59 1939 UT = Thu Aug 31 23:59:59 1939 -01 isdst=0 gmtoff=-3600 + // Africa/Freetown Fri Sep 1 01:00:00 1939 UT = Fri Sep 1 00:20:00 1939 -0040 isdst=1 gmtoff=-2400 + // Africa/Freetown Tue Sep 5 00:39:59 1939 UT = Mon Sep 4 23:59:59 1939 -0040 isdst=1 gmtoff=-2400 + // Africa/Freetown Tue Sep 5 00:40:00 1939 UT = Mon Sep 4 23:40:00 1939 -01 isdst=0 gmtoff=-3600 + // + // Originally used a one week threshold, but due to this switched to 1 day. + // This seems to work in practice. + // + // TODO TZDB Evaluate the proper threshold. + constexpr _Tp __threshold = 24 * 3600; + + _Tp __upper = std::__add_sat(__info.begin.time_since_epoch().count(), __threshold); + if (__time >= __info.begin && __time.time_since_epoch().count() < __upper) + return __info.begin != sys_seconds::min() ? __position::__beginning : __position::__middle; + + _Tp __lower = std::__sub_sat(__info.end.time_since_epoch().count(), __threshold); + if (__time < __info.end && __time.time_since_epoch().count() >= __lower) + return __info.end != sys_seconds::max() ? __position::__end : __position::__middle; + + return __position::__middle; +} + +[[nodiscard]] static local_info +__get_info(local_seconds __local_time, const sys_info& __first, const sys_info& __second) { + std::chrono::local_seconds __end_first{__first.end.time_since_epoch() + __first.offset}; + std::chrono::local_seconds __begin_second{__second.begin.time_since_epoch() + __second.offset}; + + if (__local_time < __end_first) { + if (__local_time >= __begin_second) + // |--------| + // |------| + // ^ + return {local_info::ambiguous, __first, __second}; + + // |--------| + // |------| + // ^ + return {local_info::unique, __first, sys_info{}}; + } + + if (__local_time < __begin_second) + // |--------| + // |------| + // ^ + return {local_info::nonexistent, __first, __second}; + + // |--------| + // |------| + // ^ + return {local_info::unique, __second, sys_info{}}; +} + +[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI local_info +time_zone::__get_info(local_seconds __local_time) const { + seconds __local_seconds = __local_time.time_since_epoch(); + + /* An example of a typical year with a DST switch displayed in local time. + * + * At the first of April the time goes forward one hour. This means the + * time marked with ~~ is not a valid local time. This is represented by the + * nonexistent value in local_info.result. + * + * At the first of November the time goes backward one hour. This means the + * time marked with ^^ happens twice. This is represented by the ambiguous + * value in local_info.result. + * + * 2020.11.01 2021.04.01 2021.11.01 + * offset +05 offset +05 offset +05 + * save 0s save 1h save 0s + * |-------------W----------| + * |----------W--------------| ---------------- ldionne wrote:
```suggestion * |-------------//---------| * |----------//-------------| ``` That might make it clearer that this is meant to represent "a jump on the axis". https://github.com/llvm/llvm-project/pull/89537 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits