1bdd1243dSDimitry Andric // -*- C++ -*- 2bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 3bdd1243dSDimitry Andric // 4bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 6bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7bdd1243dSDimitry Andric // 8bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 9bdd1243dSDimitry Andric 10bdd1243dSDimitry Andric #ifndef _LIBCPP___CHRONO_CONVERT_TO_TM_H 11bdd1243dSDimitry Andric #define _LIBCPP___CHRONO_CONVERT_TO_TM_H 12bdd1243dSDimitry Andric 1306c3fb27SDimitry Andric #include <__chrono/calendar.h> 1406c3fb27SDimitry Andric #include <__chrono/concepts.h> 15bdd1243dSDimitry Andric #include <__chrono/day.h> 16bdd1243dSDimitry Andric #include <__chrono/duration.h> 1706c3fb27SDimitry Andric #include <__chrono/file_clock.h> 18bdd1243dSDimitry Andric #include <__chrono/hh_mm_ss.h> 19*0fca6ea1SDimitry Andric #include <__chrono/local_info.h> 20bdd1243dSDimitry Andric #include <__chrono/month.h> 21bdd1243dSDimitry Andric #include <__chrono/month_weekday.h> 22bdd1243dSDimitry Andric #include <__chrono/monthday.h> 23bdd1243dSDimitry Andric #include <__chrono/statically_widen.h> 24*0fca6ea1SDimitry Andric #include <__chrono/sys_info.h> 25bdd1243dSDimitry Andric #include <__chrono/system_clock.h> 26bdd1243dSDimitry Andric #include <__chrono/time_point.h> 27bdd1243dSDimitry Andric #include <__chrono/weekday.h> 28bdd1243dSDimitry Andric #include <__chrono/year.h> 29bdd1243dSDimitry Andric #include <__chrono/year_month.h> 30bdd1243dSDimitry Andric #include <__chrono/year_month_day.h> 31bdd1243dSDimitry Andric #include <__chrono/year_month_weekday.h> 32*0fca6ea1SDimitry Andric #include <__chrono/zoned_time.h> 33bdd1243dSDimitry Andric #include <__concepts/same_as.h> 34bdd1243dSDimitry Andric #include <__config> 3506c3fb27SDimitry Andric #include <__format/format_error.h> 36bdd1243dSDimitry Andric #include <__memory/addressof.h> 3706c3fb27SDimitry Andric #include <__type_traits/is_convertible.h> 38*0fca6ea1SDimitry Andric #include <__type_traits/is_specialization.h> 39bdd1243dSDimitry Andric #include <cstdint> 40bdd1243dSDimitry Andric #include <ctime> 4106c3fb27SDimitry Andric #include <limits> 42bdd1243dSDimitry Andric 43bdd1243dSDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 44bdd1243dSDimitry Andric # pragma GCC system_header 45bdd1243dSDimitry Andric #endif 46bdd1243dSDimitry Andric 4706c3fb27SDimitry Andric _LIBCPP_PUSH_MACROS 4806c3fb27SDimitry Andric #include <__undef_macros> 4906c3fb27SDimitry Andric 50bdd1243dSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD 51bdd1243dSDimitry Andric 5206c3fb27SDimitry Andric #if _LIBCPP_STD_VER >= 20 53bdd1243dSDimitry Andric 54bdd1243dSDimitry Andric // Conerts a chrono date and weekday to a given _Tm type. 55bdd1243dSDimitry Andric // 56bdd1243dSDimitry Andric // This is an implementation detail for the function 57bdd1243dSDimitry Andric // template <class _Tm, class _ChronoT> 58bdd1243dSDimitry Andric // _Tm __convert_to_tm(const _ChronoT& __value) 59bdd1243dSDimitry Andric // 60bdd1243dSDimitry Andric // This manually converts the two values to the proper type. It is possible to 61bdd1243dSDimitry Andric // convert from sys_days to time_t and then to _Tm. But this leads to the Y2K 62bdd1243dSDimitry Andric // bug when time_t is a 32-bit signed integer. Chrono considers years beyond 63bdd1243dSDimitry Andric // the year 2038 valid, so instead do the transformation manually. 64bdd1243dSDimitry Andric template <class _Tm, class _Date> 65bdd1243dSDimitry Andric requires(same_as<_Date, chrono::year_month_day> || same_as<_Date, chrono::year_month_day_last>) 66bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _Date& __date, chrono::weekday __weekday) { 67bdd1243dSDimitry Andric _Tm __result = {}; 68bdd1243dSDimitry Andric # ifdef __GLIBC__ 69bdd1243dSDimitry Andric __result.tm_zone = "UTC"; 70bdd1243dSDimitry Andric # endif 71bdd1243dSDimitry Andric __result.tm_year = static_cast<int>(__date.year()) - 1900; 72bdd1243dSDimitry Andric __result.tm_mon = static_cast<unsigned>(__date.month()) - 1; 73bdd1243dSDimitry Andric __result.tm_mday = static_cast<unsigned>(__date.day()); 74bdd1243dSDimitry Andric __result.tm_wday = static_cast<unsigned>(__weekday.c_encoding()); 75bdd1243dSDimitry Andric __result.tm_yday = 76bdd1243dSDimitry Andric (static_cast<chrono::sys_days>(__date) - 77bdd1243dSDimitry Andric static_cast<chrono::sys_days>(chrono::year_month_day{__date.year(), chrono::January, chrono::day{1}})) 78bdd1243dSDimitry Andric .count(); 79bdd1243dSDimitry Andric 80bdd1243dSDimitry Andric return __result; 81bdd1243dSDimitry Andric } 82bdd1243dSDimitry Andric 8306c3fb27SDimitry Andric template <class _Tm, class _Duration> 8406c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const chrono::sys_time<_Duration> __tp) { 8506c3fb27SDimitry Andric chrono::sys_days __days = chrono::floor<chrono::days>(__tp); 8606c3fb27SDimitry Andric chrono::year_month_day __ymd{__days}; 8706c3fb27SDimitry Andric 8806c3fb27SDimitry Andric _Tm __result = std::__convert_to_tm<_Tm>(chrono::year_month_day{__ymd}, chrono::weekday{__days}); 8906c3fb27SDimitry Andric 9006c3fb27SDimitry Andric uint64_t __sec = 9106c3fb27SDimitry Andric chrono::duration_cast<chrono::seconds>(__tp - chrono::time_point_cast<chrono::seconds>(__days)).count(); 9206c3fb27SDimitry Andric __sec %= 24 * 3600; 9306c3fb27SDimitry Andric __result.tm_hour = __sec / 3600; 9406c3fb27SDimitry Andric __sec %= 3600; 9506c3fb27SDimitry Andric __result.tm_min = __sec / 60; 9606c3fb27SDimitry Andric __result.tm_sec = __sec % 60; 9706c3fb27SDimitry Andric 9806c3fb27SDimitry Andric return __result; 9906c3fb27SDimitry Andric } 10006c3fb27SDimitry Andric 101bdd1243dSDimitry Andric // Convert a chrono (calendar) time point, or dururation to the given _Tm type, 102bdd1243dSDimitry Andric // which must have the same properties as std::tm. 103bdd1243dSDimitry Andric template <class _Tm, class _ChronoT> 104bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) { 105bdd1243dSDimitry Andric _Tm __result = {}; 106bdd1243dSDimitry Andric # ifdef __GLIBC__ 107bdd1243dSDimitry Andric __result.tm_zone = "UTC"; 108bdd1243dSDimitry Andric # endif 109bdd1243dSDimitry Andric 11006c3fb27SDimitry Andric if constexpr (__is_time_point<_ChronoT>) { 11106c3fb27SDimitry Andric if constexpr (same_as<typename _ChronoT::clock, chrono::system_clock>) 11206c3fb27SDimitry Andric return std::__convert_to_tm<_Tm>(__value); 11306c3fb27SDimitry Andric else if constexpr (same_as<typename _ChronoT::clock, chrono::file_clock>) 11406c3fb27SDimitry Andric return std::__convert_to_tm<_Tm>(_ChronoT::clock::to_sys(__value)); 11506c3fb27SDimitry Andric else if constexpr (same_as<typename _ChronoT::clock, chrono::local_t>) 11606c3fb27SDimitry Andric return std::__convert_to_tm<_Tm>(chrono::sys_time<typename _ChronoT::duration>{__value.time_since_epoch()}); 11706c3fb27SDimitry Andric else 11806c3fb27SDimitry Andric static_assert(sizeof(_ChronoT) == 0, "TODO: Add the missing clock specialization"); 11906c3fb27SDimitry Andric } else if constexpr (chrono::__is_duration<_ChronoT>::value) { 120bdd1243dSDimitry Andric // [time.format]/6 121bdd1243dSDimitry Andric // ... However, if a flag refers to a "time of day" (e.g. %H, %I, %p, 122bdd1243dSDimitry Andric // etc.), then a specialization of duration is interpreted as the time of 123bdd1243dSDimitry Andric // day elapsed since midnight. 12406c3fb27SDimitry Andric 12506c3fb27SDimitry Andric // Not all values can be converted to hours, it may run into ratio 12606c3fb27SDimitry Andric // conversion errors. In that case the conversion to seconds works. 12706c3fb27SDimitry Andric if constexpr (is_convertible_v<_ChronoT, chrono::hours>) { 12806c3fb27SDimitry Andric auto __hour = chrono::floor<chrono::hours>(__value); 12906c3fb27SDimitry Andric auto __sec = chrono::duration_cast<chrono::seconds>(__value - __hour); 13006c3fb27SDimitry Andric __result.tm_hour = __hour.count() % 24; 13106c3fb27SDimitry Andric __result.tm_min = __sec.count() / 60; 13206c3fb27SDimitry Andric __result.tm_sec = __sec.count() % 60; 13306c3fb27SDimitry Andric } else { 134bdd1243dSDimitry Andric uint64_t __sec = chrono::duration_cast<chrono::seconds>(__value).count(); 135bdd1243dSDimitry Andric __sec %= 24 * 3600; 136bdd1243dSDimitry Andric __result.tm_hour = __sec / 3600; 137bdd1243dSDimitry Andric __sec %= 3600; 138bdd1243dSDimitry Andric __result.tm_min = __sec / 60; 139bdd1243dSDimitry Andric __result.tm_sec = __sec % 60; 14006c3fb27SDimitry Andric } 141bdd1243dSDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::day>) 142bdd1243dSDimitry Andric __result.tm_mday = static_cast<unsigned>(__value); 143bdd1243dSDimitry Andric else if constexpr (same_as<_ChronoT, chrono::month>) 144bdd1243dSDimitry Andric __result.tm_mon = static_cast<unsigned>(__value) - 1; 145bdd1243dSDimitry Andric else if constexpr (same_as<_ChronoT, chrono::year>) 146bdd1243dSDimitry Andric __result.tm_year = static_cast<int>(__value) - 1900; 147bdd1243dSDimitry Andric else if constexpr (same_as<_ChronoT, chrono::weekday>) 148bdd1243dSDimitry Andric __result.tm_wday = __value.c_encoding(); 149bdd1243dSDimitry Andric else if constexpr (same_as<_ChronoT, chrono::weekday_indexed> || same_as<_ChronoT, chrono::weekday_last>) 150bdd1243dSDimitry Andric __result.tm_wday = __value.weekday().c_encoding(); 151bdd1243dSDimitry Andric else if constexpr (same_as<_ChronoT, chrono::month_day>) { 152bdd1243dSDimitry Andric __result.tm_mday = static_cast<unsigned>(__value.day()); 153bdd1243dSDimitry Andric __result.tm_mon = static_cast<unsigned>(__value.month()) - 1; 154bdd1243dSDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::month_day_last>) { 155bdd1243dSDimitry Andric __result.tm_mon = static_cast<unsigned>(__value.month()) - 1; 156bdd1243dSDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::month_weekday> || same_as<_ChronoT, chrono::month_weekday_last>) { 157bdd1243dSDimitry Andric __result.tm_wday = __value.weekday_indexed().weekday().c_encoding(); 158bdd1243dSDimitry Andric __result.tm_mon = static_cast<unsigned>(__value.month()) - 1; 159bdd1243dSDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::year_month>) { 160bdd1243dSDimitry Andric __result.tm_year = static_cast<int>(__value.year()) - 1900; 161bdd1243dSDimitry Andric __result.tm_mon = static_cast<unsigned>(__value.month()) - 1; 162bdd1243dSDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::year_month_day> || same_as<_ChronoT, chrono::year_month_day_last>) { 163bdd1243dSDimitry Andric return std::__convert_to_tm<_Tm>( 164bdd1243dSDimitry Andric chrono::year_month_day{__value}, chrono::weekday{static_cast<chrono::sys_days>(__value)}); 165bdd1243dSDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::year_month_weekday> || 166bdd1243dSDimitry Andric same_as<_ChronoT, chrono::year_month_weekday_last>) { 167bdd1243dSDimitry Andric return std::__convert_to_tm<_Tm>(chrono::year_month_day{static_cast<chrono::sys_days>(__value)}, __value.weekday()); 16806c3fb27SDimitry Andric } else if constexpr (__is_hh_mm_ss<_ChronoT>) { 16906c3fb27SDimitry Andric __result.tm_sec = __value.seconds().count(); 17006c3fb27SDimitry Andric __result.tm_min = __value.minutes().count(); 17106c3fb27SDimitry Andric // In libc++ hours is stored as a long. The type in std::tm is an int. So 17206c3fb27SDimitry Andric // the overflow can only occur when hour uses more bits than an int 17306c3fb27SDimitry Andric // provides. 17406c3fb27SDimitry Andric if constexpr (sizeof(std::chrono::hours::rep) > sizeof(__result.tm_hour)) 17506c3fb27SDimitry Andric if (__value.hours().count() > std::numeric_limits<decltype(__result.tm_hour)>::max()) 17606c3fb27SDimitry Andric std::__throw_format_error("Formatting hh_mm_ss, encountered an hour overflow"); 17706c3fb27SDimitry Andric __result.tm_hour = __value.hours().count(); 178*0fca6ea1SDimitry Andric # if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) 179*0fca6ea1SDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::sys_info>) { 180*0fca6ea1SDimitry Andric // Has no time information. 181*0fca6ea1SDimitry Andric } else if constexpr (same_as<_ChronoT, chrono::local_info>) { 182*0fca6ea1SDimitry Andric // Has no time information. 183*0fca6ea1SDimitry Andric # if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ 184*0fca6ea1SDimitry Andric !defined(_LIBCPP_HAS_NO_LOCALIZATION) 185*0fca6ea1SDimitry Andric } else if constexpr (__is_specialization_v<_ChronoT, chrono::zoned_time>) { 186*0fca6ea1SDimitry Andric return std::__convert_to_tm<_Tm>( 187*0fca6ea1SDimitry Andric chrono::sys_time<typename _ChronoT::duration>{__value.get_local_time().time_since_epoch()}); 188*0fca6ea1SDimitry Andric # endif 189*0fca6ea1SDimitry Andric # endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) 190bdd1243dSDimitry Andric } else 191bdd1243dSDimitry Andric static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization"); 192bdd1243dSDimitry Andric 193bdd1243dSDimitry Andric return __result; 194bdd1243dSDimitry Andric } 195bdd1243dSDimitry Andric 19606c3fb27SDimitry Andric #endif // if _LIBCPP_STD_VER >= 20 197bdd1243dSDimitry Andric 198bdd1243dSDimitry Andric _LIBCPP_END_NAMESPACE_STD 199bdd1243dSDimitry Andric 20006c3fb27SDimitry Andric _LIBCPP_POP_MACROS 20106c3fb27SDimitry Andric 202bdd1243dSDimitry Andric #endif // _LIBCPP___CHRONO_CONVERT_TO_TM_H 203