diff --git a/stl/inc/chrono b/stl/inc/chrono index 9ffe4b06af6..ece4fc6268e 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5537,6 +5537,8 @@ namespace chrono { _Hours = _Val.hours().count(); _Minutes = _Val.minutes().count(); _Seconds = static_cast(_Val.seconds().count()); + } else if constexpr (_Is_any_of_v<_Ty, sys_info, local_info>) { + return {}; // none of the valid conversion specifiers need tm fields } else if constexpr (_Is_specialization_v<_Ty, time_point>) { const auto _Dp = _CHRONO floor(_Val); const year_month_day _Ymd{_Dp}; @@ -5655,6 +5657,47 @@ namespace chrono { return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%T}"), _Val); } +#pragma warning(push) +#pragma warning(disable : 4365) // 'argument': conversion from 'char' to 'const wchar_t', signed/unsigned mismatch + template + _NODISCARD decltype(auto) _Widen_string(const string& _Str) { + if constexpr (is_same_v<_CharT, char>) { + return _Str; + } else { + return wstring{_Str.begin(), _Str.end()}; // TRANSITION, should probably use ctype::widen + } + } +#pragma warning(pop) + + template + basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const sys_info& _Val) { + return _Os << _STD format(_Os.getloc(), + _STATICALLY_WIDEN(_CharT, "begin: {}, end: {}, offset: {}, save: {}, abbrev: {}"), // + _Val.begin, _Val.end, _Val.offset, _Val.save, _Widen_string<_CharT>(_Val.abbrev)); + } + + template + basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const local_info& _Val) { + switch (_Val.result) { + case local_info::unique: + return _Os << _STD format(_Os.getloc(), // + _STATICALLY_WIDEN(_CharT, "result: unique, first: ({})"), // + _Val.first); + case local_info::nonexistent: + return _Os << _STD format(_Os.getloc(), + _STATICALLY_WIDEN(_CharT, "result: nonexistent, first: ({}), second: ({})"), // + _Val.first, _Val.second); + case local_info::ambiguous: + return _Os << _STD format(_Os.getloc(), + _STATICALLY_WIDEN(_CharT, "result: ambiguous, first: ({}), second: ({})"), // + _Val.first, _Val.second); + default: + return _Os << _STD format(_Os.getloc(), // + _STATICALLY_WIDEN(_CharT, "result: {}, first: ({}), second: ({})"), // + _Val.result, _Val.first, _Val.second); + } + } + template // clang-format off requires (!treat_as_floating_point_v && _Duration{1} < days{1}) @@ -5815,6 +5858,8 @@ namespace chrono { } else if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) { return _Type == 'H' || _Type == 'I' || _Type == 'M' || _Type == 'S' || _Type == 'r' || _Type == 'R' || _Type == 'T' || _Type == 'p'; + } else if constexpr (_Is_any_of_v<_Ty, sys_info, local_info>) { + return _Type == 'z' || _Type == 'Z'; } else if constexpr (_Is_specialization_v<_Ty, time_point>) { if constexpr (!is_same_v) { if (_Type == 'z' || _Type == 'Z') { @@ -5889,6 +5934,12 @@ namespace chrono { template bool _Custom_write( basic_ostream<_CharT>& _Os, const _Chrono_spec<_CharT>& _Spec, const tm& _Time, const _Ty& _Val) { + if constexpr (is_same_v<_Ty, local_info>) { + if (_Val.result != local_info::unique) { + _THROW(format_error("Cannot print non-unique local_info")); + } + } + const auto _Year = _Time.tm_year + 1900; const auto _Month = _Time.tm_mon + 1; const bool _Has_modifier = _Spec._Modifier != '\0'; @@ -5996,15 +6047,34 @@ namespace chrono { _Write_seconds(_Os, _Val); return true; case 'Z': - _Os << _Time_zone_abbreviation; + if constexpr (is_same_v<_Ty, sys_info>) { + _Os << _Widen_string<_CharT>(_Val.abbrev); + } else if constexpr (is_same_v<_Ty, local_info>) { + _Os << _Widen_string<_CharT>(_Val.first.abbrev); + } else { + _Os << _Time_zone_abbreviation; + } return true; case 'z': - _Os << _STATICALLY_WIDEN(_CharT, "+00"); - if (_Has_modifier) { - _Os << _CharT{':'}; + { + hh_mm_ss _Offset; + + if constexpr (is_same_v<_Ty, sys_info>) { + _Offset = hh_mm_ss{_Val.offset}; + } else if constexpr (is_same_v<_Ty, local_info>) { + _Offset = hh_mm_ss{_Val.first.offset}; + } else { + _Offset = hh_mm_ss{}; + } + + const auto _Sign = _Offset.is_negative() ? _CharT{'-'} : _CharT{'+'}; + const auto _Separator = + _Has_modifier ? _STATICALLY_WIDEN(_CharT, ":") : _STATICALLY_WIDEN(_CharT, ""); + + _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{}{:02}{}{:02}"), _Sign, _Offset.hours().count(), + _Separator, _Offset.minutes().count()); + return true; } - _Os << _STATICALLY_WIDEN(_CharT, "00"); - return true; default: return false; } @@ -6099,6 +6169,14 @@ template struct formatter<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>, _CharT> : _Fill_tm_formatter<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>, _CharT> {}; +template +struct formatter<_CHRONO sys_info, _CharT> // + : _Fill_tm_formatter<_CHRONO sys_info, _CharT> {}; + +template +struct formatter<_CHRONO local_info, _CharT> // + : _Fill_tm_formatter<_CHRONO local_info, _CharT> {}; + template struct formatter<_CHRONO sys_time<_Duration>, _CharT> { auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 5a920d16407..74ea3106ef3 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -140,7 +140,6 @@ // P0325R4 to_array() // P0339R6 polymorphic_allocator<> // P0355R7 Calendars And Time Zones -// (partially implemented) // P0356R5 bind_front() // P0357R3 Supporting Incomplete Types In reference_wrapper // P0408R7 Efficient Access To basic_stringbuf's Buffer @@ -1172,12 +1171,6 @@ #define __cpp_lib_variant 201606L #endif // _HAS_CXX17 -#if _HAS_CXX17 -#define __cpp_lib_chrono 201611L // P0505R0 constexpr For (Again) -#else // _HAS_CXX17 -#define __cpp_lib_chrono 201510L // P0092R1 floor(), ceil(), round(), abs() -#endif // _HAS_CXX17 - // C++20 #define __cpp_lib_atomic_value_initialization 201911L @@ -1303,6 +1296,14 @@ #define __cpp_lib_array_constexpr 201803L #endif // _HAS_CXX17 +#if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#define __cpp_lib_chrono 201907L // P1466R3 Miscellaneous Minor Fixes For +#elif _HAS_CXX17 +#define __cpp_lib_chrono 201611L // P0505R0 constexpr For (Again) +#else // _HAS_CXX17 +#define __cpp_lib_chrono 201510L // P0092R1 floor(), ceil(), round(), abs() +#endif // _HAS_CXX17 + #if _HAS_CXX20 #define __cpp_lib_shared_ptr_arrays 201707L // P0674R1 make_shared() For Arrays #else // _HAS_CXX20 diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 67bc2348870..8cd4a768c69 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -640,6 +640,9 @@ std/language.support/support.limits/support.limits.general/algorithm.version.pas std/language.support/support.limits/support.limits.general/functional.version.pass.cpp FAIL std/language.support/support.limits/support.limits.general/iterator.version.pass.cpp FAIL +# Test expects __cpp_lib_chrono to have the old value 201611L for P0505R0; we define the C++20 value 201907L for P1466R3. +std/language.support/support.limits/support.limits.general/chrono.version.pass.cpp FAIL + # We unconditionally define __cpp_lib_addressof_constexpr; test error says it # "should not be defined when TEST_HAS_BUILTIN(__builtin_addressof) || TEST_GCC_VER >= 700 is not defined!" std/language.support/support.limits/support.limits.general/memory.version.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index fedbf725d24..2eeaaaee2a8 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -640,6 +640,9 @@ language.support\support.limits\support.limits.general\algorithm.version.pass.cp language.support\support.limits\support.limits.general\functional.version.pass.cpp language.support\support.limits\support.limits.general\iterator.version.pass.cpp +# Test expects __cpp_lib_chrono to have the old value 201611L for P0505R0; we define the C++20 value 201907L for P1466R3. +language.support\support.limits\support.limits.general\chrono.version.pass.cpp + # We unconditionally define __cpp_lib_addressof_constexpr; test error says it # "should not be defined when TEST_HAS_BUILTIN(__builtin_addressof) || TEST_GCC_VER >= 700 is not defined!" language.support\support.limits\support.limits.general\memory.version.pass.cpp diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index 311cb96ab79..333c9b71c9e 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -13,6 +13,8 @@ #include #include +#include + using namespace std; using namespace chrono; @@ -212,9 +214,9 @@ void empty_braces_helper(const Arg& val, const CharT* const expected) { template constexpr void print(Str str) { if constexpr (is_same_v) { - cout << "res: " << str << "\n"; + cout << str << "\n"; } else { - wcout << "res: " << str << "\n"; + wcout << str << "\n"; } } @@ -611,6 +613,107 @@ void test_exception_classes() { } } +template +void test_information_classes() { + const tzdb& database = get_tzdb(); + + const time_zone* const sydney_tz = database.locate_zone(Sydney::Tz_name); + assert(sydney_tz != nullptr); + + const time_zone* const la_tz = database.locate_zone(LA::Tz_name); + assert(la_tz != nullptr); + + const sys_info sys1 = sydney_tz->get_info(Sydney::Std_1.begin() + days{1}); + const sys_info sys2 = sydney_tz->get_info(Sydney::Day_2.begin() + days{1}); + const sys_info sys3 = la_tz->get_info(LA::Std_1.begin() + days{1}); + const sys_info sys4 = la_tz->get_info(LA::Day_2.begin() + days{1}); + + const local_info loc1 = sydney_tz->get_info(Sydney::Std_1.local_begin() + days{1}); + const local_info loc2 = sydney_tz->get_info(Sydney::Day_2.local_begin() + days{1}); + const local_info loc3 = la_tz->get_info(LA::Std_1.local_begin() + days{1}); + const local_info loc4 = la_tz->get_info(LA::Day_2.local_begin() + days{1}); + + const local_info ambiguous1 = sydney_tz->get_info(Sydney::Std_1.local_begin()); + const local_info ambiguous2 = la_tz->get_info(LA::Std_1.local_begin()); + + const local_info nonexistent1 = sydney_tz->get_info(Sydney::Std_1.local_end()); + const local_info nonexistent2 = la_tz->get_info(LA::Std_1.local_end()); + + empty_braces_helper(sys1, STR("begin: 2020-04-04 16:00:00, end: 2020-10-03 16:00:00, " + "offset: 36000s, save: 0min, abbrev: GMT+10")); + empty_braces_helper(sys2, STR("begin: 2020-10-03 16:00:00, end: 2021-04-03 16:00:00, " + "offset: 39600s, save: 60min, abbrev: GMT+11")); + empty_braces_helper(sys3, STR("begin: 2020-11-01 09:00:00, end: 2021-03-14 10:00:00, " + "offset: -28800s, save: 0min, abbrev: PST")); + empty_braces_helper(sys4, STR("begin: 2021-03-14 10:00:00, end: 2021-11-07 09:00:00, " + "offset: -25200s, save: 60min, abbrev: PDT")); + empty_braces_helper(loc1, STR("result: unique, " + "first: (begin: 2020-04-04 16:00:00, end: 2020-10-03 16:00:00, " + "offset: 36000s, save: 0min, abbrev: GMT+10)")); + empty_braces_helper(loc2, STR("result: unique, " + "first: (begin: 2020-10-03 16:00:00, end: 2021-04-03 16:00:00, " + "offset: 39600s, save: 60min, abbrev: GMT+11)")); + empty_braces_helper(loc3, STR("result: unique, " + "first: (begin: 2020-11-01 09:00:00, end: 2021-03-14 10:00:00, " + "offset: -28800s, save: 0min, abbrev: PST)")); + empty_braces_helper(loc4, STR("result: unique, " + "first: (begin: 2021-03-14 10:00:00, end: 2021-11-07 09:00:00, " + "offset: -25200s, save: 60min, abbrev: PDT)")); + empty_braces_helper(ambiguous1, STR("result: ambiguous, " + "first: (begin: 2019-10-05 16:00:00, end: 2020-04-04 16:00:00, " + "offset: 39600s, save: 60min, abbrev: GMT+11), " + "second: (begin: 2020-04-04 16:00:00, end: 2020-10-03 16:00:00, " + "offset: 36000s, save: 0min, abbrev: GMT+10)")); + empty_braces_helper(ambiguous2, STR("result: ambiguous, " + "first: (begin: 2020-03-08 10:00:00, end: 2020-11-01 09:00:00, " + "offset: -25200s, save: 60min, abbrev: PDT), " + "second: (begin: 2020-11-01 09:00:00, end: 2021-03-14 10:00:00, " + "offset: -28800s, save: 0min, abbrev: PST)")); + empty_braces_helper(nonexistent1, STR("result: nonexistent, " + "first: (begin: 2020-04-04 16:00:00, end: 2020-10-03 16:00:00, " + "offset: 36000s, save: 0min, abbrev: GMT+10), " + "second: (begin: 2020-10-03 16:00:00, end: 2021-04-03 16:00:00, " + "offset: 39600s, save: 60min, abbrev: GMT+11)")); + empty_braces_helper(nonexistent2, STR("result: nonexistent, " + "first: (begin: 2020-11-01 09:00:00, end: 2021-03-14 10:00:00, " + "offset: -28800s, save: 0min, abbrev: PST), " + "second: (begin: 2021-03-14 10:00:00, end: 2021-11-07 09:00:00, " + "offset: -25200s, save: 60min, abbrev: PDT)")); + + assert(format(STR("{:%z %Ez %Oz %Z}"), sys1) == STR("+1000 +10:00 +10:00 GMT+10")); + assert(format(STR("{:%z %Ez %Oz %Z}"), sys2) == STR("+1100 +11:00 +11:00 GMT+11")); + assert(format(STR("{:%z %Ez %Oz %Z}"), sys3) == STR("-0800 -08:00 -08:00 PST")); + assert(format(STR("{:%z %Ez %Oz %Z}"), sys4) == STR("-0700 -07:00 -07:00 PDT")); + + assert(format(STR("{:%z %Ez %Oz %Z}"), loc1) == STR("+1000 +10:00 +10:00 GMT+10")); + assert(format(STR("{:%z %Ez %Oz %Z}"), loc2) == STR("+1100 +11:00 +11:00 GMT+11")); + assert(format(STR("{:%z %Ez %Oz %Z}"), loc3) == STR("-0800 -08:00 -08:00 PST")); + assert(format(STR("{:%z %Ez %Oz %Z}"), loc4) == STR("-0700 -07:00 -07:00 PDT")); + + throw_helper(STR("{:%z}"), ambiguous1); + throw_helper(STR("{:%z}"), ambiguous2); + throw_helper(STR("{:%z}"), nonexistent1); + throw_helper(STR("{:%z}"), nonexistent2); + + throw_helper(STR("{:%Z}"), ambiguous1); + throw_helper(STR("{:%Z}"), ambiguous2); + throw_helper(STR("{:%Z}"), nonexistent1); + throw_helper(STR("{:%Z}"), nonexistent2); + + // Additionally test zero and half-hour offsets. + const time_zone* const utc_tz = database.locate_zone("Etc/UTC"sv); + assert(utc_tz != nullptr); + + const time_zone* const kolkata_tz = database.locate_zone("Asia/Kolkata"sv); + assert(kolkata_tz != nullptr); + + const sys_info sys5 = utc_tz->get_info(sys_days{2021y / January / 1}); + const sys_info sys6 = kolkata_tz->get_info(sys_days{2021y / January / 1}); + + assert(format(STR("{:%z %Ez %Oz}"), sys5) == STR("+0000 +00:00 +00:00")); + assert(format(STR("{:%z %Ez %Oz}"), sys6) == STR("+0530 +05:30 +05:30")); +} + int main() { test_parse_conversion_spec(); test_parse_conversion_spec(); @@ -673,4 +776,7 @@ int main() { test_hh_mm_ss_formatter(); test_exception_classes(); + + test_information_classes(); + test_information_classes(); } diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 73bf5dfcf86..3bf79bc6065 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -325,6 +325,12 @@ STATIC_ASSERT(__cpp_lib_char8_t == 201907L); #ifndef __cpp_lib_chrono #error __cpp_lib_chrono is not defined +#elif _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#if __cpp_lib_chrono != 201907L +#error __cpp_lib_chrono is not 201907L +#else +STATIC_ASSERT(__cpp_lib_chrono == 201907L); +#endif #elif _HAS_CXX17 #if __cpp_lib_chrono != 201611L #error __cpp_lib_chrono is not 201611L