From b925e16a008cb311872532f72f81fb24d256d0f4 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 1 May 2021 21:03:19 +0800 Subject: [PATCH 01/20] =?UTF-8?q?Implement=20`file=5Ftime`=20=E2=86=94=20U?= =?UTF-8?q?TC=20conversion=20with=20leap=20second=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows 10 1809 and Windows Server 2019 introduced support for leap seconds (enabled by default, could be disabled system-wide). When leap seconds are enabled, leap seconds after July 2018 are included in the tick count of `FILETIME`. This is different from the old behavior that `FILETIME` doesn't count leap seconds. In the future, the same `FILETIME` value could represent different time points a few seconds apart depending on whether leap second support is available and enabled. This commit implements the core algorithm for conversion between `file_time` and UTC date/time components. It calls Windows `FileTimeToSystemTime` or `SystemTimeToFileTime` to determine the interpretation of `FILETIME` by the underlying system. --- stl/CMakeLists.txt | 2 + stl/inc/xtime0.h | 99 +++++++++++++++++ stl/src/xtime0.cpp | 266 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 stl/inc/xtime0.h create mode 100644 stl/src/xtime0.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 12fa2b9255..db46ff0ff1 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -232,6 +232,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/xstddef ${CMAKE_CURRENT_LIST_DIR}/inc/xstring ${CMAKE_CURRENT_LIST_DIR}/inc/xthreads.h + ${CMAKE_CURRENT_LIST_DIR}/inc/xtime0.h ${CMAKE_CURRENT_LIST_DIR}/inc/xtimec.h ${CMAKE_CURRENT_LIST_DIR}/inc/xtr1common ${CMAKE_CURRENT_LIST_DIR}/inc/xtree @@ -256,6 +257,7 @@ set(IMPLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/syserror_import_lib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/vector_algorithms.cpp ${CMAKE_CURRENT_LIST_DIR}/src/xonce2.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/xtime0.cpp ) # The following files are linked in msvcp140[d][_clr].dll. diff --git a/stl/inc/xtime0.h b/stl/inc/xtime0.h new file mode 100644 index 0000000000..6eb57ce844 --- /dev/null +++ b/stl/inc/xtime0.h @@ -0,0 +1,99 @@ +// xtime0.h internal header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// This must be as small as possible, because its contents are +// injected into the msvcprt.lib and msvcprtd.lib import libraries. +// Do not include or define anything else here. +// In particular, basic_string must not be included here. + +#pragma once +#ifndef _XTIME0_H +#define _XTIME0_H + +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_EXTERN_C + +// clang-format off +struct __std_win_system_time { // typedef struct _SYSTEMTIME { + unsigned short _Year; // WORD wYear; + unsigned short _Month; // WORD wMonth; + unsigned short _Day_of_week; // WORD wDayOfWeek; + unsigned short _Day; // WORD wDay; + unsigned short _Hour; // WORD wHour; + unsigned short _Minute; // WORD wMinute; + unsigned short _Second; // WORD wSecond; + unsigned short _Milliseconds; // WORD wMilliseconds; +}; // } SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME; +// clang-format on + +struct __std_utc_components_1s { + short _Year; + signed char _Month; + signed char _Day; + signed char _Weekday; + signed char _Hour; + signed char _Minute; + signed char _Second; +}; + +// SystemTimeToFileTime +// returns -1 on failure +_NODISCARD long long __stdcall __std_win_system_time_to_file_time(const __std_win_system_time* _System_time) noexcept; + +// FileTimeToSystemTime +_NODISCARD bool __stdcall __std_win_file_time_to_system_time( + long long _File_time, __std_win_system_time* _Out_system_time) noexcept; + +enum class __std_utc_components_to_file_seconds_errc { + _Success = 0, + + // input is invalid regardless of whether there are leap seconds + // _Out_file_seconds is not set + _Invalid_parameter = 1 << 0, + + // __std_utc_components_to_file_seconds only + // second 60 without a positive leap second, or second 59 deleted by a negative leap second + // sets _Out_file_seconds to the value corresponding to second 00 of the next minute + _Nonexistent = 1 << 1, + + // SystemTimeToFileTime returns unexpected sub-second values + _Unknown_smear = 1 << 2, +}; + +_BITMASK_OPS(__std_utc_components_to_file_seconds_errc) + +enum class __std_file_seconds_to_utc_components_errc { + _Success = 0, + + _Invalid_parameter = 1, + + // FileTimeToSystemTime returns unexpected sub-second values + _Unknown_smear = 4, +}; + +// converts UTC (whole seconds) into file_time +_NODISCARD int __stdcall __std_utc_components_to_file_seconds( + const __std_utc_components_1s* _Utc_time, long long* _Out_file_seconds) noexcept; + +// converts file_time into UTC +_NODISCARD int __stdcall __std_file_seconds_to_utc_components( + long long _File_seconds, __std_utc_components_1s* _Out_utc_time) noexcept; + +_END_EXTERN_C + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) + +#endif // defined(_XTIME0_H) diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp new file mode 100644 index 0000000000..d019798fe5 --- /dev/null +++ b/stl/src/xtime0.cpp @@ -0,0 +1,266 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// This must be as small as possible, because its contents are +// injected into the msvcprt.lib and msvcprtd.lib import libraries. +// Do not include or define anything else here. +// In particular, basic_string must not be included here. + +#include +#include +#include + +#include "awint.hpp" + +static_assert(sizeof(__std_win_system_time) == sizeof(SYSTEMTIME)); +static_assert(alignof(__std_win_system_time) == alignof(SYSTEMTIME)); + +_EXTERN_C + +static constexpr long long file_time_to_ticks(const FILETIME& ft) noexcept { + return ((static_cast(ft.dwHighDateTime)) << 32) + static_cast(ft.dwLowDateTime); +} + +static constexpr FILETIME file_time_from_ticks(const long long ticks) noexcept { + return { + .dwLowDateTime = static_cast(ticks), + .dwHighDateTime = static_cast(ticks >> 32), + }; +} + +// SystemTimeToFileTime +// returns -1 on failure +_NODISCARD long long __stdcall __std_win_system_time_to_file_time(const __std_win_system_time* _System_time) noexcept { + if (FILETIME ft; SystemTimeToFileTime(reinterpret_cast(_System_time), &ft) != FALSE) { + return file_time_to_ticks(ft); + } + + return -1; +} + +// FileTimeToSystemTime +_NODISCARD bool __stdcall __std_win_file_time_to_system_time( + long long _File_time, __std_win_system_time* _Out_system_time) noexcept { + const FILETIME ft = file_time_from_ticks(_File_time); + return FileTimeToSystemTime(&ft, reinterpret_cast(_Out_system_time)) != FALSE; +} + +static constexpr void increase_minute(SYSTEMTIME& st) noexcept { + using namespace std::chrono; + + ++st.wMinute; + if (st.wMinute < 60) { + return; + } + + st.wMinute -= 60; + ++st.wHour; + if (st.wHour < 24) { + return; + } + + st.wHour -= 24; + const sys_days sys_d = year{st.wYear} / month{st.wMonth} / day{st.wDay + 1u}; + st.wDayOfWeek = static_cast(weekday{sys_d}.c_encoding()); + const year_month_day ymd = sys_d; + st.wDay = static_cast(static_cast(ymd.day())); + st.wMonth = static_cast(static_cast(ymd.month())); + st.wYear = static_cast(static_cast(ymd.year())); +} + +static constexpr int ticks_per_sec = 10'000'000; + +struct utc_components_to_file_time_result { + long long ticks; + __std_utc_components_to_file_seconds_errc ec; +}; + +// converts UTC (whole seconds) into file_time +_NODISCARD static utc_components_to_file_time_result utc_components_to_file_time( + const __std_utc_components_1s& utc_time) noexcept { + using enum __std_utc_components_to_file_seconds_errc; + + SYSTEMTIME st{ + .wYear = static_cast(utc_time._Year), + .wMonth = static_cast(utc_time._Month), + .wDay = static_cast(utc_time._Day), + .wHour = static_cast(utc_time._Hour), + .wMinute = static_cast(utc_time._Minute), + .wSecond = static_cast(utc_time._Second), + .wMilliseconds = 0, + }; + + if (st.wSecond < 60) { + // second 00-59 + if (FILETIME ft; SystemTimeToFileTime(&st, &ft) != FALSE) { + const long long ticks = file_time_to_ticks(ft); + return {.ticks = ticks, .ec = _Success}; + } + + if (st.wSecond == 59) { + --st.wSecond; + if (FILETIME prev_sec_ft; SystemTimeToFileTime(&st, &prev_sec_ft) != FALSE) { + // negative leap second + const long long prev_sec_ticks = file_time_to_ticks(prev_sec_ft); + return {.ticks = prev_sec_ticks + ticks_per_sec, .ec = _Nonexistent}; + } + } + + return {.ticks = -1, .ec = _Invalid_parameter}; + } + + if (st.wSecond != 60) { + return {.ticks = -1, .ec = _Invalid_parameter}; + } + + // second 60 + // + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_leap_second_info + // The bahavior of SystemTimeToFileTime and FileTimeToSystemTime during the 2-second period of + // [23:59:59 UTC, 00:00:00 UTC) around a leap second insertion changes depending on whether + // PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND is set for the process. + // + // When the flag is not set (default), SYSTEMTIME doesn't observe second 60. It stretches second 59 to cover the + // 2-second period instead. + // + // When the flag is set, SYSTEMTIME observes second 60 and represents UTC as-is. + // + // FILETIME | FileTimeToSystemTime + // | Flag Not Set | Flag Set + // ---------------- | ------------ | ------------ + // 23:59:59.000 UTC | 23:59:59.000 | 23:59:59.000 + // 23:59:59.500 UTC | 23:59:59.250 | 23:59:59.500 + // 23:59:60.000 UTC | 23:59:59.500 | 23:59:60.000 + // 23:59:60.500 UTC | 23:59:59.750 | 23:59:60.500 + // 00:00:00.000 UTC | 00:00:00.000 | 00:00:00.000 + // + // SYSTEMTIME | SystemTimeToFileTime + // | Flag Not Set | Flag Set + // ------------ | ---------------- | ---------------- + // 23:59:59.000 | 23:59:59.000 UTC | 23:59:59.000 UTC + // 23:59:59.500 | 23:59:60.000 UTC | 23:59:59.500 UTC + // 23:59:60.000 | Error | 23:59:60.000 UTC + // 23:59:60.500 | Error | 23:59:60.500 UTC + // 00:00:00.000 | 00:00:00.000 UTC | 00:00:00.000 UTC + // + // To handle both cases properly, we call SystemTimeToFileTime with the previous second 59 and the next second 00, + // and then determine whether the second 60 represents a valid positive leap second. + + --st.wSecond; + FILETIME prev_sec_ft; + if (SystemTimeToFileTime(&st, &prev_sec_ft) == FALSE) { + // second 59 is invalid + --st.wSecond; + if (SystemTimeToFileTime(&st, &prev_sec_ft) != FALSE) { + // negative leap second + const long long prev_sec_ticks = file_time_to_ticks(prev_sec_ft); + return {.ticks = prev_sec_ticks + ticks_per_sec, .ec = _Nonexistent}; + } + + return {.ticks = -1, .ec = _Invalid_parameter}; + } + + const long long prev_sec_ticks = file_time_to_ticks(prev_sec_ft); + + st.wSecond = 0; + increase_minute(st); + FILETIME next_sec_ft; + if (SystemTimeToFileTime(&st, &next_sec_ft) == FALSE) { + return {.ticks = -1, .ec = _Invalid_parameter}; + } + + const long long next_sec_ticks = file_time_to_ticks(next_sec_ft); + const long long difference = next_sec_ticks - prev_sec_ticks; + _STL_INTERNAL_CHECK(difference == 2 * ticks_per_sec || difference == ticks_per_sec); + + if (difference == 2 * ticks_per_sec) { + // positive leap second + return {.ticks = prev_sec_ticks + ticks_per_sec, .ec = _Success}; + } + + if (difference == ticks_per_sec) { + // non-existent second 60 + return {.ticks = next_sec_ticks, .ec = _Nonexistent}; + } + + // unknown leap second smearing behavior + return {.ticks = next_sec_ticks, .ec = _Unknown_smear}; +} + +_NODISCARD int __stdcall __std_utc_components_to_file_seconds( + const __std_utc_components_1s* _Utc_time, long long* _Out_file_seconds) noexcept { + using enum __std_utc_components_to_file_seconds_errc; + + const auto [ticks, ec] = utc_components_to_file_time(*_Utc_time); + _STL_INTERNAL_CHECK(ec == _Invalid_parameter || ticks % ticks_per_sec == 0); + + if (ec == _Invalid_parameter) { + *_Out_file_seconds = -1; + return static_cast(ec); + } + + *_Out_file_seconds = ticks / ticks_per_sec; + + if (ticks % ticks_per_sec == 0) { + return static_cast(ec); + } + + // unknown leap second smearing behavior + if (_Utc_time->_Day >= 15) { + ++*_Out_file_seconds; + } + + return static_cast(ec | _Unknown_smear); +} + +// converts file_time into UTC +_NODISCARD int __stdcall __std_file_seconds_to_utc_components( + const long long _File_seconds, __std_utc_components_1s* _Out_utc_time) noexcept { + using enum __std_file_seconds_to_utc_components_errc; + + const long long ticks = _File_seconds * ticks_per_sec; + const FILETIME ft = file_time_from_ticks(ticks); + SYSTEMTIME st; + if (FileTimeToSystemTime(&ft, &st) == FALSE) { + *_Out_utc_time = {}; + return static_cast(_Invalid_parameter); + } + + _STL_INTERNAL_CHECK(st.wMilliseconds == 0 || st.wMilliseconds == 500 && st.wSecond == 59 && st.wMinute = 59); + _Out_utc_time->_Year = static_cast(st.wYear); + _Out_utc_time->_Month = static_cast(st.wMonth); + _Out_utc_time->_Day = static_cast(st.wDay); + _Out_utc_time->_Weekday = static_cast(st.wDayOfWeek); + _Out_utc_time->_Hour = static_cast(st.wHour); + _Out_utc_time->_Minute = static_cast(st.wMinute); + _Out_utc_time->_Second = static_cast(st.wSecond); + + if (st.wMilliseconds == 0) { + // regular time point, or + // during leap second insertion and process is leap second aware + return static_cast(_Success); + } + + if (st.wMilliseconds == 500 && st.wSecond == 59 && st.wMinute == 59) { + // during leap second insertion + // process is leap second unaware, 23:59:60.000 UTC is reported as 23:59:59.500 in SYSTEMTIME + ++_Out_utc_time->_Second; + return static_cast(_Success); + } + + // unknown leap second smearing behavior + if (st.wSecond < 60 && st.wDay >= 15) { + if (st.wSecond < 59) { + ++_Out_utc_time->_Second; + } else if (st.wMinute < 59) { + _Out_utc_time->_Second = 0; + ++_Out_utc_time->_Minute; + } else { + ++_Out_utc_time->_Second; + } + } + + return static_cast(_Unknown_smear); +} + +_END_EXTERN_C From 375fe5af6b6d45da22b9512d9183ac1020b692f2 Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 18:00:43 +0800 Subject: [PATCH 02/20] Code cleanup - Return `enum class` instead of `int` error code from `extern C` functions. - Add more `[[nodiscard]]`. - Remove `_STL_INTERNAL_CHECK` in import lib code, where the checks are never performed. Also modified the fallback behavior for unknown leap second smearing. --- stl/inc/xtime0.h | 20 +++++++++---------- stl/src/xtime0.cpp | 50 ++++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/stl/inc/xtime0.h b/stl/inc/xtime0.h index 6eb57ce844..c68e92811c 100644 --- a/stl/inc/xtime0.h +++ b/stl/inc/xtime0.h @@ -54,39 +54,39 @@ _NODISCARD long long __stdcall __std_win_system_time_to_file_time(const __std_wi _NODISCARD bool __stdcall __std_win_file_time_to_system_time( long long _File_time, __std_win_system_time* _Out_system_time) noexcept; -enum class __std_utc_components_to_file_seconds_errc { +enum class __std_utc_to_file_time_errc { _Success = 0, // input is invalid regardless of whether there are leap seconds // _Out_file_seconds is not set - _Invalid_parameter = 1 << 0, + _Invalid_parameter = 0b001, // __std_utc_components_to_file_seconds only // second 60 without a positive leap second, or second 59 deleted by a negative leap second // sets _Out_file_seconds to the value corresponding to second 00 of the next minute - _Nonexistent = 1 << 1, + _Nonexistent = 0b010, // SystemTimeToFileTime returns unexpected sub-second values - _Unknown_smear = 1 << 2, + _Unknown_smear = 0b100, }; -_BITMASK_OPS(__std_utc_components_to_file_seconds_errc) +_BITMASK_OPS(__std_utc_to_file_time_errc) -enum class __std_file_seconds_to_utc_components_errc { +enum class __std_file_time_to_utc_errc { _Success = 0, - _Invalid_parameter = 1, + _Invalid_parameter = static_cast(__std_utc_to_file_time_errc::_Invalid_parameter), // FileTimeToSystemTime returns unexpected sub-second values - _Unknown_smear = 4, + _Unknown_smear = static_cast(__std_utc_to_file_time_errc::_Unknown_smear), }; // converts UTC (whole seconds) into file_time -_NODISCARD int __stdcall __std_utc_components_to_file_seconds( +_NODISCARD __std_utc_to_file_time_errc __stdcall __std_utc_components_to_file_seconds( const __std_utc_components_1s* _Utc_time, long long* _Out_file_seconds) noexcept; // converts file_time into UTC -_NODISCARD int __stdcall __std_file_seconds_to_utc_components( +_NODISCARD __std_file_time_to_utc_errc __stdcall __std_file_seconds_to_utc_components( long long _File_seconds, __std_utc_components_1s* _Out_utc_time) noexcept; _END_EXTERN_C diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp index d019798fe5..4ec96389ae 100644 --- a/stl/src/xtime0.cpp +++ b/stl/src/xtime0.cpp @@ -17,11 +17,11 @@ static_assert(alignof(__std_win_system_time) == alignof(SYSTEMTIME)); _EXTERN_C -static constexpr long long file_time_to_ticks(const FILETIME& ft) noexcept { +_NODISCARD static constexpr long long file_time_to_ticks(const FILETIME& ft) noexcept { return ((static_cast(ft.dwHighDateTime)) << 32) + static_cast(ft.dwLowDateTime); } -static constexpr FILETIME file_time_from_ticks(const long long ticks) noexcept { +_NODISCARD static constexpr FILETIME file_time_from_ticks(const long long ticks) noexcept { return { .dwLowDateTime = static_cast(ticks), .dwHighDateTime = static_cast(ticks >> 32), @@ -70,15 +70,15 @@ static constexpr void increase_minute(SYSTEMTIME& st) noexcept { static constexpr int ticks_per_sec = 10'000'000; -struct utc_components_to_file_time_result { +struct utc_to_file_time_result { long long ticks; - __std_utc_components_to_file_seconds_errc ec; + __std_utc_to_file_time_errc ec; }; -// converts UTC (whole seconds) into file_time -_NODISCARD static utc_components_to_file_time_result utc_components_to_file_time( +// converts UTC (whole seconds) into file_clock +_NODISCARD static utc_to_file_time_result utc_components_to_file_time( const __std_utc_components_1s& utc_time) noexcept { - using enum __std_utc_components_to_file_seconds_errc; + using enum __std_utc_to_file_time_errc; SYSTEMTIME st{ .wYear = static_cast(utc_time._Year), @@ -146,10 +146,11 @@ _NODISCARD static utc_components_to_file_time_result utc_components_to_file_time // To handle both cases properly, we call SystemTimeToFileTime with the previous second 59 and the next second 00, // and then determine whether the second 60 represents a valid positive leap second. + // the previous second 59 --st.wSecond; FILETIME prev_sec_ft; if (SystemTimeToFileTime(&st, &prev_sec_ft) == FALSE) { - // second 59 is invalid + // second 59 is invalid, try second 58 --st.wSecond; if (SystemTimeToFileTime(&st, &prev_sec_ft) != FALSE) { // negative leap second @@ -162,6 +163,7 @@ _NODISCARD static utc_components_to_file_time_result utc_components_to_file_time const long long prev_sec_ticks = file_time_to_ticks(prev_sec_ft); + // the next second 00 st.wSecond = 0; increase_minute(st); FILETIME next_sec_ft; @@ -171,7 +173,6 @@ _NODISCARD static utc_components_to_file_time_result utc_components_to_file_time const long long next_sec_ticks = file_time_to_ticks(next_sec_ft); const long long difference = next_sec_ticks - prev_sec_ticks; - _STL_INTERNAL_CHECK(difference == 2 * ticks_per_sec || difference == ticks_per_sec); if (difference == 2 * ticks_per_sec) { // positive leap second @@ -187,46 +188,46 @@ _NODISCARD static utc_components_to_file_time_result utc_components_to_file_time return {.ticks = next_sec_ticks, .ec = _Unknown_smear}; } -_NODISCARD int __stdcall __std_utc_components_to_file_seconds( +// converts UTC (whole seconds) into file_time +_NODISCARD __std_utc_to_file_time_errc __stdcall __std_utc_components_to_file_seconds( const __std_utc_components_1s* _Utc_time, long long* _Out_file_seconds) noexcept { - using enum __std_utc_components_to_file_seconds_errc; + using enum __std_utc_to_file_time_errc; const auto [ticks, ec] = utc_components_to_file_time(*_Utc_time); - _STL_INTERNAL_CHECK(ec == _Invalid_parameter || ticks % ticks_per_sec == 0); if (ec == _Invalid_parameter) { *_Out_file_seconds = -1; - return static_cast(ec); + return ec; } *_Out_file_seconds = ticks / ticks_per_sec; if (ticks % ticks_per_sec == 0) { - return static_cast(ec); + return ec; } // unknown leap second smearing behavior + // assuming positive leap second, smeared clock runs behind UTC before the leap second if (_Utc_time->_Day >= 15) { ++*_Out_file_seconds; } - return static_cast(ec | _Unknown_smear); + return ec | _Unknown_smear; } // converts file_time into UTC -_NODISCARD int __stdcall __std_file_seconds_to_utc_components( +_NODISCARD __std_file_time_to_utc_errc __stdcall __std_file_seconds_to_utc_components( const long long _File_seconds, __std_utc_components_1s* _Out_utc_time) noexcept { - using enum __std_file_seconds_to_utc_components_errc; + using enum __std_file_time_to_utc_errc; const long long ticks = _File_seconds * ticks_per_sec; const FILETIME ft = file_time_from_ticks(ticks); SYSTEMTIME st; if (FileTimeToSystemTime(&ft, &st) == FALSE) { *_Out_utc_time = {}; - return static_cast(_Invalid_parameter); + return _Invalid_parameter; } - _STL_INTERNAL_CHECK(st.wMilliseconds == 0 || st.wMilliseconds == 500 && st.wSecond == 59 && st.wMinute = 59); _Out_utc_time->_Year = static_cast(st.wYear); _Out_utc_time->_Month = static_cast(st.wMonth); _Out_utc_time->_Day = static_cast(st.wDay); @@ -238,29 +239,34 @@ _NODISCARD int __stdcall __std_file_seconds_to_utc_components( if (st.wMilliseconds == 0) { // regular time point, or // during leap second insertion and process is leap second aware - return static_cast(_Success); + return _Success; } if (st.wMilliseconds == 500 && st.wSecond == 59 && st.wMinute == 59) { // during leap second insertion // process is leap second unaware, 23:59:60.000 UTC is reported as 23:59:59.500 in SYSTEMTIME ++_Out_utc_time->_Second; - return static_cast(_Success); + return _Success; } // unknown leap second smearing behavior + // assuming positive leap second, smeared clock runs behind UTC before the leap second if (st.wSecond < 60 && st.wDay >= 15) { if (st.wSecond < 59) { ++_Out_utc_time->_Second; } else if (st.wMinute < 59) { _Out_utc_time->_Second = 0; ++_Out_utc_time->_Minute; + } else if (st.wHour < 23) { + _Out_utc_time->_Second = 0; + _Out_utc_time->_Minute = 0; + ++_Out_utc_time->_Hour; } else { ++_Out_utc_time->_Second; } } - return static_cast(_Unknown_smear); + return _Unknown_smear; } _END_EXTERN_C From 319e51844dfb34cbf12318dda977c3b7732227f3 Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 19:16:40 +0800 Subject: [PATCH 03/20] Move _Days_from_civil out for use in C++14 mode --- stl/inc/chrono | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index e4850fd835..66a234deb5 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -663,6 +663,27 @@ namespace chrono { } // CLOCKS + + // _Civil_from_days and _Days_from_civil perform conversions between the dates in the (proleptic) Gregorian + // calendar and the continuous count of days since 1970-01-01. + + // See comments above year_month_day::_Civil_from_days for an explanation of the algorithm. + + // courtesy of Howard Hinnant + // https://howardhinnant.github.io/date_algorithms.html#days_from_civil + _NODISCARD constexpr int _Days_from_civil( + const int _Year, const unsigned int _Month, const unsigned int _Day) noexcept { + static_assert(numeric_limits::digits >= 18); + static_assert(numeric_limits::digits >= 26); + const int _Yp = static_cast(_Year) - (_Month <= 2); + const int _Century = (_Yp >= 0 ? _Yp : _Yp - 99) / 100; + const unsigned int _Mp = _Month + (_Month > 2 ? static_cast(-3) : 9); // [0, 11] + // Formula 1 + const int _Day_of_year = static_cast(((979 * _Mp + 19) >> 5) + _Day) - 1; + // Formula 2 + return ((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468; + } + struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime using rep = long long; using period = ratio<1, 10'000'000>; // 100 nanoseconds @@ -1589,19 +1610,10 @@ namespace chrono { const unsigned int _Month = _Mp + (_Mp < 10 ? 3 : static_cast(-9)); // [1, 12] return year_month_day{_CHRONO year{_Yp + (_Month <= 2)}, _CHRONO month{_Month}, _CHRONO day{_Day}}; } - // courtesy of Howard Hinnant - // https://howardhinnant.github.io/date_algorithms.html#days_from_civil + _NODISCARD constexpr days _Days_from_civil() const noexcept { - static_assert(numeric_limits::digits >= 18); - static_assert(numeric_limits::digits >= 26); - const unsigned int _Mo = static_cast(_Month); // [1, 12] - const int _Yp = static_cast(_Year) - (_Mo <= 2); - const int _Century = (_Yp >= 0 ? _Yp : _Yp - 99) / 100; - const unsigned int _Mp = _Mo + (_Mo > 2 ? static_cast(-3) : 9); // [0, 11] - // Formula 1 - const int _Day_of_year = static_cast(((979 * _Mp + 19) >> 5) + static_cast(_Day)) - 1; - // Formula 2 - return days{((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468}; + return days{_CHRONO _Days_from_civil( + static_cast(_Year), static_cast(_Month), static_cast(_Day))}; } }; From 22ccb75318e2c08e6ee2bc628da9b670d42e5dc3 Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 21:10:25 +0800 Subject: [PATCH 04/20] Implement `system_clock::now` --- stl/inc/chrono | 31 ++++++++++++++++++++++++++++--- stl/inc/xtime0.h | 12 ++++++------ stl/src/xtime0.cpp | 12 ++++++------ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 66a234deb5..1c2be38ad6 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -12,6 +12,7 @@ #include #include #include +#include #include #if _HAS_CXX20 @@ -673,8 +674,10 @@ namespace chrono { // https://howardhinnant.github.io/date_algorithms.html#days_from_civil _NODISCARD constexpr int _Days_from_civil( const int _Year, const unsigned int _Month, const unsigned int _Day) noexcept { - static_assert(numeric_limits::digits >= 18); - static_assert(numeric_limits::digits >= 26); + static_assert(numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert( + numeric_limits::digits >= 26, "This algorithm has not been ported to a 16 bit signed integer"); const int _Yp = static_cast(_Year) - (_Month <= 2); const int _Century = (_Yp >= 0 ? _Yp : _Yp - 99) / 100; const unsigned int _Mp = _Month + (_Month > 2 ? static_cast(-3) : 9); // [0, 11] @@ -692,7 +695,29 @@ namespace chrono { static constexpr bool is_steady = false; _NODISCARD static time_point now() noexcept { // get current time - return time_point(duration(_Xtime_get_ticks())); + constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI + + const duration _File_time{_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment}; + const auto _File_seconds = _CHRONO duration_cast(_File_time); + + __std_utc_components_1s _Utc; + const __std_file_time_to_utc_errc _Ec = __std_file_seconds_to_utc_components(_File_seconds.count(), &_Utc); + _STL_INTERNAL_CHECK(_Ec == __std_file_time_to_utc_errc::_Success); + if (_Ec == __std_file_time_to_utc_errc::_Invalid_parameter) { + return time_point{_File_time - duration{__std_fs_file_time_epoch_adjustment}}; + } + + using _Days = _CHRONO duration>; + using _Int_seconds = _CHRONO duration; + const duration _Ymd = _Days{_Days_from_civil(_Utc._Year, _Utc._Month, _Utc._Day)}; + const duration _Hms = hours{_Utc._Hour} + minutes{_Utc._Minute} + _Int_seconds{_Utc._Second}; + + if (_Utc._Second < 60) { + return time_point{_Ymd + _Hms + (_File_time - _File_seconds)}; + } else { + // positive leap second + return time_point{_Ymd + _Hms - duration{1}}; + } } _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept { // convert to __time64_t diff --git a/stl/inc/xtime0.h b/stl/inc/xtime0.h index c68e92811c..a4d8c623a0 100644 --- a/stl/inc/xtime0.h +++ b/stl/inc/xtime0.h @@ -38,12 +38,12 @@ struct __std_win_system_time { // typedef struct _SYSTEMTIME { struct __std_utc_components_1s { short _Year; - signed char _Month; - signed char _Day; - signed char _Weekday; - signed char _Hour; - signed char _Minute; - signed char _Second; + unsigned char _Month; + unsigned char _Day; + unsigned char _Weekday; + unsigned char _Hour; + unsigned char _Minute; + unsigned char _Second; }; // SystemTimeToFileTime diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp index 4ec96389ae..13ed687ba9 100644 --- a/stl/src/xtime0.cpp +++ b/stl/src/xtime0.cpp @@ -229,12 +229,12 @@ _NODISCARD __std_file_time_to_utc_errc __stdcall __std_file_seconds_to_utc_compo } _Out_utc_time->_Year = static_cast(st.wYear); - _Out_utc_time->_Month = static_cast(st.wMonth); - _Out_utc_time->_Day = static_cast(st.wDay); - _Out_utc_time->_Weekday = static_cast(st.wDayOfWeek); - _Out_utc_time->_Hour = static_cast(st.wHour); - _Out_utc_time->_Minute = static_cast(st.wMinute); - _Out_utc_time->_Second = static_cast(st.wSecond); + _Out_utc_time->_Month = static_cast(st.wMonth); + _Out_utc_time->_Day = static_cast(st.wDay); + _Out_utc_time->_Weekday = static_cast(st.wDayOfWeek); + _Out_utc_time->_Hour = static_cast(st.wHour); + _Out_utc_time->_Minute = static_cast(st.wMinute); + _Out_utc_time->_Second = static_cast(st.wSecond); if (st.wMilliseconds == 0) { // regular time point, or From 2a9b591ecb63e0670685ce29d146367acc28cfba Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 21:11:34 +0800 Subject: [PATCH 05/20] Update `_To_xtime_10_day_clamped` to call `_Xtime_get_ticks` directly --- stl/inc/chrono | 12 +++++++++--- stl/inc/condition_variable | 4 ++-- stl/inc/mutex | 6 +++--- stl/inc/thread | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 1c2be38ad6..5c8c0297b0 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -6560,15 +6560,15 @@ namespace chrono { // HELPERS template -_NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const _CHRONO duration<_Rep, _Period>& _Rel_time) noexcept( - is_arithmetic_v<_Rep>) { +_NODISCARD bool _To_xtime_10_day_clamped_v2( + _CSTD xtime& _Xt, const _CHRONO duration<_Rep, _Period>& _Rel_time) noexcept(is_arithmetic_v<_Rep>) { // Convert duration to xtime, maximum 10 days from now, returns whether clamping occurred. // If clamped, timeouts will be transformed into spurious non-timeout wakes, due to ABI restrictions where // the other side of the DLL boundary overflows int32_t milliseconds. // Every function calling this one is TRANSITION, ABI constexpr _CHRONO nanoseconds _Ten_days{_CHRONO hours{24} * 10}; constexpr _CHRONO duration _Ten_days_d{_Ten_days}; - _CHRONO nanoseconds _Tx0 = _CHRONO system_clock::now().time_since_epoch(); + _CHRONO nanoseconds _Tx0 = _CHRONO system_clock::duration{_Xtime_get_ticks()}; const bool _Clamped = _Ten_days_d < _Rel_time; if (_Clamped) { _Tx0 += _Ten_days; @@ -6583,6 +6583,12 @@ _NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const _CHRONO duratio return _Clamped; } +template +_NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const _CHRONO duration<_Rep, _Period>& _Rel_time) noexcept( + is_arithmetic_v<_Rep>) { + return _To_xtime_10_day_clamped_v2(_Xt, _Rel_time); +} + // duration LITERALS inline namespace literals { inline namespace chrono_literals { diff --git a/stl/inc/condition_variable b/stl/inc/condition_variable index 3c17c8227a..f1c718bc20 100644 --- a/stl/inc/condition_variable +++ b/stl/inc/condition_variable @@ -124,7 +124,7 @@ public: // TRANSITION, ABI: The standard says that we should use a steady clock, // but unfortunately our ABI speaks struct xtime, which is relative to the system clock. _CSTD xtime _Tgt; - const bool _Clamped = _To_xtime_10_day_clamped(_Tgt, _Rel_time); + const bool _Clamped = _To_xtime_10_day_clamped_v2(_Tgt, _Rel_time); const cv_status _Result = _Wait_until(_Lck, &_Tgt); if (_Clamped) { return cv_status::no_timeout; @@ -221,7 +221,7 @@ public: // TRANSITION, ABI: The standard says that we should use a steady clock, // but unfortunately our ABI speaks struct xtime, which is relative to the system clock. _CSTD xtime _Tgt; - (void) _To_xtime_10_day_clamped(_Tgt, _Rel_time); + (void) _To_xtime_10_day_clamped_v2(_Tgt, _Rel_time); const int _Res = _Cnd_timedwait(_Mycnd(), _Myptr->_Mymtx(), &_Tgt); _Guard.unlock(); diff --git a/stl/inc/mutex b/stl/inc/mutex index aafefe48bc..b87307079b 100644 --- a/stl/inc/mutex +++ b/stl/inc/mutex @@ -625,7 +625,7 @@ public: // TRANSITION, ABI: The standard says that we should use a steady clock, // but unfortunately our ABI speaks struct xtime, which is relative to the system clock. _CSTD xtime _Tgt; - const bool _Clamped = _To_xtime_10_day_clamped(_Tgt, _Rel_time); + const bool _Clamped = _To_xtime_10_day_clamped_v2(_Tgt, _Rel_time); const cv_status _Result = wait_until(_Lck, &_Tgt); if (_Clamped) { return cv_status::no_timeout; @@ -653,7 +653,7 @@ public: } _CSTD xtime _Tgt; - (void) _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now); + (void) _To_xtime_10_day_clamped_v2(_Tgt, _Abs_time - _Now); const cv_status _Result = wait_until(_Lck, &_Tgt); if (_Result == cv_status::no_timeout) { return cv_status::no_timeout; @@ -736,7 +736,7 @@ private: } _CSTD xtime _Tgt; - const bool _Clamped = _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now); + const bool _Clamped = _To_xtime_10_day_clamped_v2(_Tgt, _Abs_time - _Now); if (wait_until(_Lck, &_Tgt) == cv_status::timeout && !_Clamped) { return _Pred(); } diff --git a/stl/inc/thread b/stl/inc/thread index 0d7be907f6..d0f95a7450 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -196,7 +196,7 @@ namespace this_thread { } _CSTD xtime _Tgt; - (void) _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now); + (void) _To_xtime_10_day_clamped_v2(_Tgt, _Abs_time - _Now); _Thrd_sleep(&_Tgt); } } From 1fa5f71175c731726ab62bfc468892ad23ecb304 Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 21:15:33 +0800 Subject: [PATCH 06/20] Add tests for `Clock::now` --- tests/std/test.lst | 1 + .../env.lst | 4 ++ .../test.cpp | 48 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index cba3615d38..086cff0267 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -230,6 +230,7 @@ tests\P0220R1_string_view tests\P0325R4_to_array tests\P0339R6_polymorphic_allocator tests\P0355R7_calendars_and_time_zones_clocks +tests\P0355R7_calendars_and_time_zones_clocks_now tests\P0355R7_calendars_and_time_zones_dates tests\P0355R7_calendars_and_time_zones_dates_literals tests\P0355R7_calendars_and_time_zones_formatting diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst new file mode 100644 index 0000000000..7979e67048 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_winsdk_matrix.lst diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp new file mode 100644 index 0000000000..21b99cfbce --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +using namespace std::chrono; + +#if _HAS_CXX17 +#include +using namespace std::filesystem; +#endif + +#include + +#if _HAS_CXX20 +[[nodiscard]] sys_time get_system_time() noexcept { + SYSTEMTIME st; + GetSystemTime(&st); + return sys_days{year{st.wYear} / month{st.wMonth} / day{st.wDay}} + hours{st.wHour} + minutes{st.wMinute} + + seconds{st.wSecond} + milliseconds{st.wMilliseconds}; +} + +template +void test_clock_now() { + constexpr auto tolerance = 16ms; + + const auto before = get_system_time(); + const auto now = time_point_cast(clock_cast(Clock::now())); + const auto after = get_system_time(); + + assert(before - tolerance <= now && now <= after + tolerance); +} +#endif // _HAS_CXX20 + +int main() { +#if _HAS_CXX20 + test_clock_now(); + test_clock_now(); + test_clock_now(); + test_clock_now(); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + (void) system_clock::now(); +#if _HAS_CXX17 + (void) file_time_type::clock::now(); +#endif // _HAS_CXX17 +#endif // ^^^ !_HAS_CXX20 ^^^ + return 0; +} From 5c6f6259673316f1ac515baa4da00b92f6062bb0 Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 21:40:26 +0800 Subject: [PATCH 07/20] _Uglify import lib code --- stl/src/xtime0.cpp | 192 +++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 94 deletions(-) diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp index 13ed687ba9..8a1b3b48f1 100644 --- a/stl/src/xtime0.cpp +++ b/stl/src/xtime0.cpp @@ -15,24 +15,24 @@ static_assert(sizeof(__std_win_system_time) == sizeof(SYSTEMTIME)); static_assert(alignof(__std_win_system_time) == alignof(SYSTEMTIME)); -_EXTERN_C - -_NODISCARD static constexpr long long file_time_to_ticks(const FILETIME& ft) noexcept { - return ((static_cast(ft.dwHighDateTime)) << 32) + static_cast(ft.dwLowDateTime); +_NODISCARD static constexpr long long _File_time_to_ticks(const FILETIME& _Ft) noexcept { + return ((static_cast(_Ft.dwHighDateTime)) << 32) + static_cast(_Ft.dwLowDateTime); } -_NODISCARD static constexpr FILETIME file_time_from_ticks(const long long ticks) noexcept { +_NODISCARD static constexpr FILETIME _File_time_from_ticks(const long long _Ticks) noexcept { return { - .dwLowDateTime = static_cast(ticks), - .dwHighDateTime = static_cast(ticks >> 32), + .dwLowDateTime = static_cast(_Ticks), + .dwHighDateTime = static_cast(_Ticks >> 32), }; } +_EXTERN_C + // SystemTimeToFileTime // returns -1 on failure _NODISCARD long long __stdcall __std_win_system_time_to_file_time(const __std_win_system_time* _System_time) noexcept { - if (FILETIME ft; SystemTimeToFileTime(reinterpret_cast(_System_time), &ft) != FALSE) { - return file_time_to_ticks(ft); + if (FILETIME _Ft; SystemTimeToFileTime(reinterpret_cast(_System_time), &_Ft) != FALSE) { + return _File_time_to_ticks(_Ft); } return -1; @@ -41,76 +41,78 @@ _NODISCARD long long __stdcall __std_win_system_time_to_file_time(const __std_wi // FileTimeToSystemTime _NODISCARD bool __stdcall __std_win_file_time_to_system_time( long long _File_time, __std_win_system_time* _Out_system_time) noexcept { - const FILETIME ft = file_time_from_ticks(_File_time); - return FileTimeToSystemTime(&ft, reinterpret_cast(_Out_system_time)) != FALSE; + const FILETIME _Ft = _File_time_from_ticks(_File_time); + return FileTimeToSystemTime(&_Ft, reinterpret_cast(_Out_system_time)) != FALSE; } -static constexpr void increase_minute(SYSTEMTIME& st) noexcept { +_END_EXTERN_C + +static constexpr void _Increase_minute(SYSTEMTIME& _St) noexcept { using namespace std::chrono; - ++st.wMinute; - if (st.wMinute < 60) { + ++_St.wMinute; + if (_St.wMinute < 60) { return; } - st.wMinute -= 60; - ++st.wHour; - if (st.wHour < 24) { + _St.wMinute -= 60; + ++_St.wHour; + if (_St.wHour < 24) { return; } - st.wHour -= 24; - const sys_days sys_d = year{st.wYear} / month{st.wMonth} / day{st.wDay + 1u}; - st.wDayOfWeek = static_cast(weekday{sys_d}.c_encoding()); - const year_month_day ymd = sys_d; - st.wDay = static_cast(static_cast(ymd.day())); - st.wMonth = static_cast(static_cast(ymd.month())); - st.wYear = static_cast(static_cast(ymd.year())); + _St.wHour -= 24; + const sys_days _Sys_d = year{_St.wYear} / month{_St.wMonth} / day{_St.wDay + 1u}; + _St.wDayOfWeek = static_cast(weekday{_Sys_d}.c_encoding()); + const year_month_day _Ymd = _Sys_d; + _St.wDay = static_cast(static_cast(_Ymd.day())); + _St.wMonth = static_cast(static_cast(_Ymd.month())); + _St.wYear = static_cast(static_cast(_Ymd.year())); } -static constexpr int ticks_per_sec = 10'000'000; +static constexpr int _Ticks_per_sec = 10'000'000; -struct utc_to_file_time_result { - long long ticks; - __std_utc_to_file_time_errc ec; +struct _Utc_to_file_time_result { + long long _Ticks; + __std_utc_to_file_time_errc _Ec; }; // converts UTC (whole seconds) into file_clock -_NODISCARD static utc_to_file_time_result utc_components_to_file_time( - const __std_utc_components_1s& utc_time) noexcept { +_NODISCARD static _Utc_to_file_time_result _Utc_components_to_file_time( + const __std_utc_components_1s& _Utc_time) noexcept { using enum __std_utc_to_file_time_errc; - SYSTEMTIME st{ - .wYear = static_cast(utc_time._Year), - .wMonth = static_cast(utc_time._Month), - .wDay = static_cast(utc_time._Day), - .wHour = static_cast(utc_time._Hour), - .wMinute = static_cast(utc_time._Minute), - .wSecond = static_cast(utc_time._Second), + SYSTEMTIME _St{ + .wYear = static_cast(_Utc_time._Year), + .wMonth = static_cast(_Utc_time._Month), + .wDay = static_cast(_Utc_time._Day), + .wHour = static_cast(_Utc_time._Hour), + .wMinute = static_cast(_Utc_time._Minute), + .wSecond = static_cast(_Utc_time._Second), .wMilliseconds = 0, }; - if (st.wSecond < 60) { + if (_St.wSecond < 60) { // second 00-59 - if (FILETIME ft; SystemTimeToFileTime(&st, &ft) != FALSE) { - const long long ticks = file_time_to_ticks(ft); - return {.ticks = ticks, .ec = _Success}; + if (FILETIME _Ft; SystemTimeToFileTime(&_St, &_Ft) != FALSE) { + const long long _Ticks = _File_time_to_ticks(_Ft); + return {._Ticks = _Ticks, ._Ec = _Success}; } - if (st.wSecond == 59) { - --st.wSecond; - if (FILETIME prev_sec_ft; SystemTimeToFileTime(&st, &prev_sec_ft) != FALSE) { + if (_St.wSecond == 59) { + --_St.wSecond; + if (FILETIME _Prev_sec_ft; SystemTimeToFileTime(&_St, &_Prev_sec_ft) != FALSE) { // negative leap second - const long long prev_sec_ticks = file_time_to_ticks(prev_sec_ft); - return {.ticks = prev_sec_ticks + ticks_per_sec, .ec = _Nonexistent}; + const long long _Prev_sec_ticks = _File_time_to_ticks(_Prev_sec_ft); + return {._Ticks = _Prev_sec_ticks + _Ticks_per_sec, ._Ec = _Nonexistent}; } } - return {.ticks = -1, .ec = _Invalid_parameter}; + return {._Ticks = -1, ._Ec = _Invalid_parameter}; } - if (st.wSecond != 60) { - return {.ticks = -1, .ec = _Invalid_parameter}; + if (_St.wSecond != 60) { + return {._Ticks = -1, ._Ec = _Invalid_parameter}; } // second 60 @@ -147,63 +149,65 @@ _NODISCARD static utc_to_file_time_result utc_components_to_file_time( // and then determine whether the second 60 represents a valid positive leap second. // the previous second 59 - --st.wSecond; - FILETIME prev_sec_ft; - if (SystemTimeToFileTime(&st, &prev_sec_ft) == FALSE) { + --_St.wSecond; + FILETIME _Prev_sec_ft; + if (SystemTimeToFileTime(&_St, &_Prev_sec_ft) == FALSE) { // second 59 is invalid, try second 58 - --st.wSecond; - if (SystemTimeToFileTime(&st, &prev_sec_ft) != FALSE) { + --_St.wSecond; + if (SystemTimeToFileTime(&_St, &_Prev_sec_ft) != FALSE) { // negative leap second - const long long prev_sec_ticks = file_time_to_ticks(prev_sec_ft); - return {.ticks = prev_sec_ticks + ticks_per_sec, .ec = _Nonexistent}; + const long long _Prev_sec_ticks = _File_time_to_ticks(_Prev_sec_ft); + return {._Ticks = _Prev_sec_ticks + _Ticks_per_sec, ._Ec = _Nonexistent}; } - return {.ticks = -1, .ec = _Invalid_parameter}; + return {._Ticks = -1, ._Ec = _Invalid_parameter}; } - const long long prev_sec_ticks = file_time_to_ticks(prev_sec_ft); + const long long _Prev_sec_ticks = _File_time_to_ticks(_Prev_sec_ft); // the next second 00 - st.wSecond = 0; - increase_minute(st); - FILETIME next_sec_ft; - if (SystemTimeToFileTime(&st, &next_sec_ft) == FALSE) { - return {.ticks = -1, .ec = _Invalid_parameter}; + _St.wSecond = 0; + _Increase_minute(_St); + FILETIME _Next_sec_ft; + if (SystemTimeToFileTime(&_St, &_Next_sec_ft) == FALSE) { + return {._Ticks = -1, ._Ec = _Invalid_parameter}; } - const long long next_sec_ticks = file_time_to_ticks(next_sec_ft); - const long long difference = next_sec_ticks - prev_sec_ticks; + const long long _Next_sec_ticks = _File_time_to_ticks(_Next_sec_ft); + const long long _Difference = _Next_sec_ticks - _Prev_sec_ticks; - if (difference == 2 * ticks_per_sec) { + if (_Difference == 2 * _Ticks_per_sec) { // positive leap second - return {.ticks = prev_sec_ticks + ticks_per_sec, .ec = _Success}; + return {._Ticks = _Prev_sec_ticks + _Ticks_per_sec, ._Ec = _Success}; } - if (difference == ticks_per_sec) { + if (_Difference == _Ticks_per_sec) { // non-existent second 60 - return {.ticks = next_sec_ticks, .ec = _Nonexistent}; + return {._Ticks = _Next_sec_ticks, ._Ec = _Nonexistent}; } // unknown leap second smearing behavior - return {.ticks = next_sec_ticks, .ec = _Unknown_smear}; + return {._Ticks = _Next_sec_ticks, ._Ec = _Unknown_smear}; } +_EXTERN_C + // converts UTC (whole seconds) into file_time _NODISCARD __std_utc_to_file_time_errc __stdcall __std_utc_components_to_file_seconds( const __std_utc_components_1s* _Utc_time, long long* _Out_file_seconds) noexcept { using enum __std_utc_to_file_time_errc; - const auto [ticks, ec] = utc_components_to_file_time(*_Utc_time); + const auto [_Ticks, _Ec] = _Utc_components_to_file_time(*_Utc_time); - if (ec == _Invalid_parameter) { + if (_Ec == _Invalid_parameter) { *_Out_file_seconds = -1; - return ec; + return _Ec; } - *_Out_file_seconds = ticks / ticks_per_sec; + *_Out_file_seconds = _Ticks / _Ticks_per_sec; - if (ticks % ticks_per_sec == 0) { - return ec; + if (_Ticks % _Ticks_per_sec == 0) { + return _Ec; } // unknown leap second smearing behavior @@ -212,7 +216,7 @@ _NODISCARD __std_utc_to_file_time_errc __stdcall __std_utc_components_to_file_se ++*_Out_file_seconds; } - return ec | _Unknown_smear; + return _Ec | _Unknown_smear; } // converts file_time into UTC @@ -220,29 +224,29 @@ _NODISCARD __std_file_time_to_utc_errc __stdcall __std_file_seconds_to_utc_compo const long long _File_seconds, __std_utc_components_1s* _Out_utc_time) noexcept { using enum __std_file_time_to_utc_errc; - const long long ticks = _File_seconds * ticks_per_sec; - const FILETIME ft = file_time_from_ticks(ticks); - SYSTEMTIME st; - if (FileTimeToSystemTime(&ft, &st) == FALSE) { + const long long _Ticks = _File_seconds * _Ticks_per_sec; + const FILETIME _Ft = _File_time_from_ticks(_Ticks); + SYSTEMTIME _St; + if (FileTimeToSystemTime(&_Ft, &_St) == FALSE) { *_Out_utc_time = {}; return _Invalid_parameter; } - _Out_utc_time->_Year = static_cast(st.wYear); - _Out_utc_time->_Month = static_cast(st.wMonth); - _Out_utc_time->_Day = static_cast(st.wDay); - _Out_utc_time->_Weekday = static_cast(st.wDayOfWeek); - _Out_utc_time->_Hour = static_cast(st.wHour); - _Out_utc_time->_Minute = static_cast(st.wMinute); - _Out_utc_time->_Second = static_cast(st.wSecond); + _Out_utc_time->_Year = static_cast(_St.wYear); + _Out_utc_time->_Month = static_cast(_St.wMonth); + _Out_utc_time->_Day = static_cast(_St.wDay); + _Out_utc_time->_Weekday = static_cast(_St.wDayOfWeek); + _Out_utc_time->_Hour = static_cast(_St.wHour); + _Out_utc_time->_Minute = static_cast(_St.wMinute); + _Out_utc_time->_Second = static_cast(_St.wSecond); - if (st.wMilliseconds == 0) { + if (_St.wMilliseconds == 0) { // regular time point, or // during leap second insertion and process is leap second aware return _Success; } - if (st.wMilliseconds == 500 && st.wSecond == 59 && st.wMinute == 59) { + if (_St.wMilliseconds == 500 && _St.wSecond == 59 && _St.wMinute == 59) { // during leap second insertion // process is leap second unaware, 23:59:60.000 UTC is reported as 23:59:59.500 in SYSTEMTIME ++_Out_utc_time->_Second; @@ -251,13 +255,13 @@ _NODISCARD __std_file_time_to_utc_errc __stdcall __std_file_seconds_to_utc_compo // unknown leap second smearing behavior // assuming positive leap second, smeared clock runs behind UTC before the leap second - if (st.wSecond < 60 && st.wDay >= 15) { - if (st.wSecond < 59) { + if (_St.wSecond < 60 && _St.wDay >= 15) { + if (_St.wSecond < 59) { ++_Out_utc_time->_Second; - } else if (st.wMinute < 59) { + } else if (_St.wMinute < 59) { _Out_utc_time->_Second = 0; ++_Out_utc_time->_Minute; - } else if (st.wHour < 23) { + } else if (_St.wHour < 23) { _Out_utc_time->_Second = 0; _Out_utc_time->_Minute = 0; ++_Out_utc_time->_Hour; From c3038de48ff2c67bc69ded29f46425f941ffe990 Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 21:40:50 +0800 Subject: [PATCH 08/20] Shorten link to fit in 120 columns --- stl/src/xtime0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp index 8a1b3b48f1..b5d407f494 100644 --- a/stl/src/xtime0.cpp +++ b/stl/src/xtime0.cpp @@ -117,7 +117,7 @@ _NODISCARD static _Utc_to_file_time_result _Utc_components_to_file_time( // second 60 // - // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_leap_second_info + // https://docs.microsoft.com/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_leap_second_info // The bahavior of SystemTimeToFileTime and FileTimeToSystemTime during the 2-second period of // [23:59:59 UTC, 00:00:00 UTC) around a leap second insertion changes depending on whether // PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND is set for the process. From 7f2a5bd9e38fa135e405dee9a6782d7954dcd0e2 Mon Sep 17 00:00:00 2001 From: statementreply Date: Tue, 4 May 2021 22:53:57 +0800 Subject: [PATCH 09/20] Move bit operator overloads out of `extern C` --- stl/inc/xtime0.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xtime0.h b/stl/inc/xtime0.h index a4d8c623a0..66ecbd77ec 100644 --- a/stl/inc/xtime0.h +++ b/stl/inc/xtime0.h @@ -70,8 +70,6 @@ enum class __std_utc_to_file_time_errc { _Unknown_smear = 0b100, }; -_BITMASK_OPS(__std_utc_to_file_time_errc) - enum class __std_file_time_to_utc_errc { _Success = 0, @@ -91,6 +89,8 @@ _NODISCARD __std_file_time_to_utc_errc __stdcall __std_file_seconds_to_utc_compo _END_EXTERN_C +_BITMASK_OPS(__std_utc_to_file_time_errc) + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) From a698c6d955d7a09929a616ac3254ce8dd26402ab Mon Sep 17 00:00:00 2001 From: statementreply Date: Mon, 26 Jul 2021 15:12:00 +0800 Subject: [PATCH 10/20] =?UTF-8?q?Implement=20`file=5Ftime`=20=E2=86=94=20`?= =?UTF-8?q?sys=5Ftime`=20conversion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stl/inc/chrono | 168 ++++++++++++++++-- stl/inc/xtime0.h | 3 +- stl/src/xtime0.cpp | 13 +- .../test.cpp | 14 +- 4 files changed, 170 insertions(+), 28 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index b9fdef7409..cecfb1f973 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -690,6 +690,28 @@ namespace chrono { return ((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468; } + struct system_clock; + + // convert from date time breakdown (whole seconds) into sys_time + template + constexpr time_point> _Sys_seconds_from_civil( + const __std_utc_components_1s& _Utc) noexcept { + using _Common = common_type_t<_Duration, seconds>; + using _Days = _CHRONO duration>; + using _Int_seconds = _CHRONO duration; + const _Common _Ymd = _Days{_Days_from_civil(_Utc._Year, _Utc._Month, _Utc._Day)}; + const _Common _Hms = hours{_Utc._Hour} + minutes{_Utc._Minute} + _Int_seconds{_Utc._Second}; + return time_point{_Ymd + _Hms}; + } + + template + struct _Utc_components { + static_assert(_Is_duration_v<_Duration>, "_Duration should be a specialization of std::chrono::duration"); + + __std_utc_components_1s _Whole_sec; + _Duration _Sub_sec; + }; + struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime using rep = long long; using period = ratio<1, 10'000'000>; // 100 nanoseconds @@ -697,30 +719,53 @@ namespace chrono { using time_point = _CHRONO time_point; static constexpr bool is_steady = false; +#if _HAS_CXX20 + template + _NODISCARD static constexpr _Utc_components> _To_utc_components( + const _CHRONO time_point& _Sys_time) noexcept; +#endif // _HAS_CXX20 + + template + _NODISCARD static constexpr _CHRONO time_point> + _From_utc_components(const _Utc_components<_Duration>& _Utc) noexcept { + using _Common = common_type_t<_Duration, seconds>; + using _Sys_time = _CHRONO time_point; + const _Sys_time _Sys_secs = _Sys_seconds_from_civil<_Duration>(_Utc._Whole_sec); + + if (_Utc._Whole_sec._Second < 60) { + return _Sys_secs + _Utc._Sub_sec; + } + + // positive leap second + using _Common_rep = typename _Common::rep; + if constexpr (treat_as_floating_point_v<_Common_rep>) { + // TRANSITION, GH-1909 + const _Common_rep _After_leap_sec = _Sys_secs.time_since_epoch().count(); + const _Common_rep _Before_leap_sec = + _STD nextafter(_After_leap_sec, -numeric_limits<_Common_rep>::infinity()); + return _Sys_time{_Common{_Before_leap_sec}}; + } else { + return _Sys_secs - _Common{1}; + } + } + _NODISCARD static time_point now() noexcept { // get current time constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI const duration _File_time{_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment}; const auto _File_seconds = _CHRONO duration_cast(_File_time); - __std_utc_components_1s _Utc; - const __std_file_time_to_utc_errc _Ec = __std_file_seconds_to_utc_components(_File_seconds.count(), &_Utc); + _Utc_components _Utc; + const __std_file_time_to_utc_errc _Ec = + __std_file_seconds_to_utc_components(_File_seconds.count(), &_Utc._Whole_sec); _STL_INTERNAL_CHECK(_Ec == __std_file_time_to_utc_errc::_Success); - if (_Ec == __std_file_time_to_utc_errc::_Invalid_parameter) { - return time_point{_File_time - duration{__std_fs_file_time_epoch_adjustment}}; - } - - using _Days = _CHRONO duration>; - using _Int_seconds = _CHRONO duration; - const duration _Ymd = _Days{_Days_from_civil(_Utc._Year, _Utc._Month, _Utc._Day)}; - const duration _Hms = hours{_Utc._Hour} + minutes{_Utc._Minute} + _Int_seconds{_Utc._Second}; + _Utc._Sub_sec = _File_time - _File_seconds; - if (_Utc._Second < 60) { - return time_point{_Ymd + _Hms + (_File_time - _File_seconds)}; - } else { - // positive leap second - return time_point{_Ymd + _Hms - duration{1}}; + if (_Ec != __std_file_time_to_utc_errc::_Invalid_parameter) { + return _From_utc_components(_Utc); } + + return time_point{_File_time - duration{__std_fs_file_time_epoch_adjustment}}; } _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept { // convert to __time64_t @@ -3172,6 +3217,39 @@ namespace chrono { // [time.clock.utc] + // convert from sys_seconds into date time breakdown + _NODISCARD constexpr __std_utc_components_1s _Civil_from_sys_seconds(const sys_seconds _Sys_time) noexcept { + using _Int_seconds = _CHRONO duration; + + const sys_days _Days = _CHRONO floor(_Sys_time); + const year_month_day _Ymd{_Days}; + + auto _Second = static_cast<_Int_seconds>(_Sys_time - _Days); + const auto _Hour = _CHRONO duration_cast(_Second); + _Second -= _Hour; + const auto _Minute = _CHRONO duration_cast(_Second); + _Second -= _Minute; + + return { + ._Year = static_cast(static_cast(_Ymd.year())), + ._Month = static_cast(static_cast(_Ymd.month())), + ._Day = static_cast(static_cast(_Ymd.day())), + ._Hour = static_cast(_Hour.count()), + ._Minute = static_cast(_Minute.count()), + ._Second = static_cast(_Second.count()), + }; + } + + template + _NODISCARD constexpr _Utc_components> system_clock::_To_utc_components( + const _CHRONO time_point& _Sys_time) noexcept { + const sys_seconds _Sys_secs = _CHRONO floor(_Sys_time); + return { + ._Whole_sec = _CHRONO _Civil_from_sys_seconds(_Sys_secs), + ._Sub_sec = _Sys_time - _Sys_secs, + }; + } + class utc_clock; template using utc_time = time_point; @@ -3298,6 +3376,14 @@ namespace chrono { const auto _Offset = _It == _Ls_vector.begin() ? seconds{0} : (--_It)->_Elapsed(); return utc_time>{_Sys_time.time_since_epoch() + _Offset}; } + + template + _NODISCARD static _Utc_components> _To_utc_components( + const utc_time<_Duration>& _Utc_time) noexcept; // FIXME + + template + _NODISCARD static utc_time> _From_utc_components( + const _Utc_components<_Duration>& _Utc) noexcept; // FIXME }; // [time.clock.tai] @@ -3442,6 +3528,58 @@ namespace filesystem { return _File_time; } + + template + _NODISCARD static chrono::_Utc_components> _To_utc_components( + const chrono::file_time<_Duration>& _File_time) noexcept { + using namespace chrono; + const auto _File_seconds = _CHRONO floor(_File_time); + + _Utc_components _Utc; + const __std_file_time_to_utc_errc _Ec = + __std_file_seconds_to_utc_components(_File_seconds.time_since_epoch().count(), &_Utc._Whole_sec); + _Utc._Sub_sec = _File_time - _File_seconds; + + if (_Ec != __std_file_time_to_utc_errc::_Invalid_parameter) { + return _Utc; + } + + constexpr sys_seconds _Epoch = sys_days{year{1601} / January / 1}; + _Utc._Whole_sec = _Civil_from_sys_seconds(_Epoch + _File_seconds.time_since_epoch()); + return _Utc; + } + + template + _NODISCARD static chrono::file_time> _From_utc_components( + const chrono::_Utc_components<_Duration>& _Utc) noexcept { + using namespace chrono; + using _Common = common_type_t<_Duration, chrono::seconds>; + + long long _File_seconds; + const __std_utc_to_file_time_errc _Ec = + __std_utc_components_to_file_seconds(&_Utc._Whole_sec, &_File_seconds); + + if (_Ec == __std_utc_to_file_time_errc::_Nonexistent) { + // unrepresentable second 60, deleted second 59, or nonexistent second 60 + using _Common_rep = typename _Common::rep; + if constexpr (treat_as_floating_point_v<_Common_rep>) { + // TRANSITION, GH-1909 + const _Common_rep _Next_representable = _Common{seconds{_File_seconds}}.count(); + const _Common_rep _Prev_representable = + _STD nextafter(_Next_representable, -numeric_limits<_Common_rep>::infinity()); + return file_time<_Common>{_Common{_Prev_representable}}; + } else { + return file_time<_Common>{seconds{_File_seconds} - _Common{1}}; + } + } + + if (_Ec != __std_utc_to_file_time_errc::_Invalid_parameter) { + return file_time<_Common>{seconds{_File_seconds} + _Utc._Sub_sec}; + } + + constexpr sys_time<_Common> _Epoch = sys_days{year{1601} / January / 1}; + return file_time<_Common>{system_clock::_From_utc_components(_Utc) - _Epoch}; + } #endif // ^^^ _HAS_CXX20 }; } // namespace filesystem diff --git a/stl/inc/xtime0.h b/stl/inc/xtime0.h index 66ecbd77ec..a2deb06092 100644 --- a/stl/inc/xtime0.h +++ b/stl/inc/xtime0.h @@ -40,7 +40,6 @@ struct __std_utc_components_1s { short _Year; unsigned char _Month; unsigned char _Day; - unsigned char _Weekday; unsigned char _Hour; unsigned char _Minute; unsigned char _Second; @@ -62,7 +61,7 @@ enum class __std_utc_to_file_time_errc { _Invalid_parameter = 0b001, // __std_utc_components_to_file_seconds only - // second 60 without a positive leap second, or second 59 deleted by a negative leap second + // unrepresentable second 60, deleted second 59, or nonexistent second 60 // sets _Out_file_seconds to the value corresponding to second 00 of the next minute _Nonexistent = 0b010, diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp index b5d407f494..04fffbf25c 100644 --- a/stl/src/xtime0.cpp +++ b/stl/src/xtime0.cpp @@ -232,13 +232,12 @@ _NODISCARD __std_file_time_to_utc_errc __stdcall __std_file_seconds_to_utc_compo return _Invalid_parameter; } - _Out_utc_time->_Year = static_cast(_St.wYear); - _Out_utc_time->_Month = static_cast(_St.wMonth); - _Out_utc_time->_Day = static_cast(_St.wDay); - _Out_utc_time->_Weekday = static_cast(_St.wDayOfWeek); - _Out_utc_time->_Hour = static_cast(_St.wHour); - _Out_utc_time->_Minute = static_cast(_St.wMinute); - _Out_utc_time->_Second = static_cast(_St.wSecond); + _Out_utc_time->_Year = static_cast(_St.wYear); + _Out_utc_time->_Month = static_cast(_St.wMonth); + _Out_utc_time->_Day = static_cast(_St.wDay); + _Out_utc_time->_Hour = static_cast(_St.wHour); + _Out_utc_time->_Minute = static_cast(_St.wMinute); + _Out_utc_time->_Second = static_cast(_St.wSecond); if (_St.wMilliseconds == 0) { // regular time point, or diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp index f1db6f7286..17cc460095 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp @@ -255,20 +255,26 @@ void test_file_clock_from_utc(const leap_second& leap) { void test_utc_clock_from_sys(const leap_second& leap, seconds offset) { // Generalized from N4885 [time.clock.utc.members]/3 Example 1. - auto t = leap.date() - 2ns; + auto t = leap.date() - 2us; + if (leap.value() < 0s) { + t += leap.value(); + } auto u = utc_clock::from_sys(t); assert(u.time_since_epoch() - t.time_since_epoch() == offset); - t += 1ns; + t += 1us; u = utc_clock::from_sys(t); assert(u.time_since_epoch() - t.time_since_epoch() == offset); - t += 1ns; + t += 1us; + if (leap.value() < 0s) { + t -= leap.value(); + } u = utc_clock::from_sys(t); offset += leap.value(); assert(u.time_since_epoch() - t.time_since_epoch() == offset); - t += 1ns; + t += 1us; u = utc_clock::from_sys(t); assert(u.time_since_epoch() - t.time_since_epoch() == offset); } From 49bce5ab3306408ce6a22e67e00d106952cbc7d4 Mon Sep 17 00:00:00 2001 From: statementreply Date: Mon, 26 Jul 2021 15:21:51 +0800 Subject: [PATCH 11/20] Code review comments Co-authored-by: Miya Natsuhara --- stl/inc/chrono | 8 +------- stl/inc/xtime0.h | 2 ++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index cecfb1f973..686757a7d5 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -681,7 +681,7 @@ namespace chrono { "This algorithm has not been ported to a 16 bit unsigned integer"); static_assert( numeric_limits::digits >= 26, "This algorithm has not been ported to a 16 bit signed integer"); - const int _Yp = static_cast(_Year) - (_Month <= 2); + const int _Yp = _Year - (_Month <= 2); const int _Century = (_Yp >= 0 ? _Yp : _Yp - 99) / 100; const unsigned int _Mp = _Month + (_Month > 2 ? static_cast(-3) : 9); // [0, 11] // Formula 1 @@ -6731,12 +6731,6 @@ _NODISCARD bool _To_xtime_10_day_clamped_v2( return _Clamped; } -template -_NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const _CHRONO duration<_Rep, _Period>& _Rel_time) noexcept( - is_arithmetic_v<_Rep>) { - return _To_xtime_10_day_clamped_v2(_Xt, _Rel_time); -} - // duration LITERALS inline namespace literals { inline namespace chrono_literals { diff --git a/stl/inc/xtime0.h b/stl/inc/xtime0.h index a2deb06092..989d95c137 100644 --- a/stl/inc/xtime0.h +++ b/stl/inc/xtime0.h @@ -14,6 +14,8 @@ #include +#include + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) From 7373b982041e46790a715a07e6f716be07ec70b5 Mon Sep 17 00:00:00 2001 From: statementreply Date: Mon, 26 Jul 2021 15:25:39 +0800 Subject: [PATCH 12/20] `nextafter` requires `#include ` --- stl/inc/chrono | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 686757a7d5..386f82edf3 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -8,6 +8,7 @@ #define _CHRONO_ #include #if _STL_COMPILER_PREPROCESSOR +#include #include #include #include @@ -19,7 +20,6 @@ #include <__msvc_tzdb.hpp> #include #include -#include #include #include #include From 1a22cafda4fd21a044f6c0b774d6caaecab2a2b7 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 21 Aug 2021 17:24:41 +0800 Subject: [PATCH 13/20] Remove `#include ` from import library source --- stl/src/xtime0.cpp | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp index 04fffbf25c..51438a6e0b 100644 --- a/stl/src/xtime0.cpp +++ b/stl/src/xtime0.cpp @@ -6,8 +6,6 @@ // Do not include or define anything else here. // In particular, basic_string must not be included here. -#include -#include #include #include "awint.hpp" @@ -26,6 +24,23 @@ _NODISCARD static constexpr FILETIME _File_time_from_ticks(const long long _Tick }; } +_NODISCARD static constexpr bool _Is_leap_year(const WORD _Year) noexcept { + return _Year % 4 == 0 && (_Year % 100 != 0 || _Year % 400 == 0); +} + +_NODISCARD static constexpr WORD _Days_of_month(const WORD _Year, const WORD _Month) noexcept { + if (_Month == 2) { + return _Is_leap_year(_Year) ? WORD{29} : WORD{28}; + } + + // _Month | _Month >> 3 | _Month ^ (_Month >> 3) | Result + // 0b00xx0 (4, 6) | 0b00000 | 0b00xx0 | 0b11110 (30) + // 0b00xx1 (1, 3, 5, 7) | 0b00000 | 0b00xx1 | 0b11111 (31) + // 0b01xx0 (8, 10, 12) | 0b00001 | 0b01xx1 | 0b11111 (31) + // 0b01xx1 (9, 11) | 0b00001 | 0b01xx0 | 0b11110 (30) + return static_cast((_Month ^ (_Month >> 3)) | WORD{30}); +} + _EXTERN_C // SystemTimeToFileTime @@ -47,9 +62,9 @@ _NODISCARD bool __stdcall __std_win_file_time_to_system_time( _END_EXTERN_C +// increases the STSTEMTIME by one clock minute (not necessarily 60 seconds because of leap seconds) +// _St.wDayOfWeek is ignored and is not updated static constexpr void _Increase_minute(SYSTEMTIME& _St) noexcept { - using namespace std::chrono; - ++_St.wMinute; if (_St.wMinute < 60) { return; @@ -62,12 +77,20 @@ static constexpr void _Increase_minute(SYSTEMTIME& _St) noexcept { } _St.wHour -= 24; - const sys_days _Sys_d = year{_St.wYear} / month{_St.wMonth} / day{_St.wDay + 1u}; - _St.wDayOfWeek = static_cast(weekday{_Sys_d}.c_encoding()); - const year_month_day _Ymd = _Sys_d; - _St.wDay = static_cast(static_cast(_Ymd.day())); - _St.wMonth = static_cast(static_cast(_Ymd.month())); - _St.wYear = static_cast(static_cast(_Ymd.year())); + const WORD _Days = _Days_of_month(_St.wYear, _St.wMonth); + ++_St.wDay; + if (_St.wDay <= _Days) { + return; + } + + _St.wDay -= _Days; + ++_St.wMonth; + if (_St.wMonth <= 12) { + return; + } + + _St.wMonth -= 12; + ++_St.wYear; } static constexpr int _Ticks_per_sec = 10'000'000; From e4e88ec658f956878ff7e329ec384f8704ac5068 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 21 Aug 2021 17:45:35 +0800 Subject: [PATCH 14/20] Centralize FILETIME offset value --- stl/inc/chrono | 8 ++------ stl/inc/xtimec.h | 2 ++ stl/src/xtime.cpp | 4 ++-- .../P0355R7_calendars_and_time_zones_clocks/test.cpp | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index e529e0fab0..b9c9d5da80 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -736,9 +736,7 @@ namespace chrono { } _NODISCARD static time_point now() noexcept { // get current time - constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI - - const duration _File_time{_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment}; + const duration _File_time{_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment}; // TRANSITION, ABI const auto _File_seconds = _CHRONO duration_cast(_File_time); _Utc_components _Utc; @@ -3448,8 +3446,6 @@ namespace chrono { #if _HAS_CXX17 namespace filesystem { - inline constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI - struct _File_time_clock { // Implementation of trivial-clock using rep = long long; using period = chrono::system_clock::period; @@ -5289,7 +5285,7 @@ namespace chrono { _Duration _Dur; if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_LeapSecondRep::_File_time)) { constexpr auto _File_clock_adj{_CHRONO duration_cast<_Duration>( - filesystem::_File_time_clock::duration{filesystem::__std_fs_file_time_epoch_adjustment})}; + filesystem::_File_time_clock::duration{__std_fs_file_time_epoch_adjustment})}; _Tp = file_time<_Duration>{_Dur} + _File_clock_adj; } else { _Istr.setstate(ios_base::failbit); diff --git a/stl/inc/xtimec.h b/stl/inc/xtimec.h index 64741469db..9e82c57929 100644 --- a/stl/inc/xtimec.h +++ b/stl/inc/xtimec.h @@ -36,6 +36,8 @@ _CRTIMP2_PURE long long __cdecl _Query_perf_frequency(); _END_EXTERN_C +_INLINE_VAR constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/src/xtime.cpp b/stl/src/xtime.cpp index 6d4bb94dc6..cc4070e7b8 100644 --- a/stl/src/xtime.cpp +++ b/stl/src/xtime.cpp @@ -42,7 +42,6 @@ static xtime xtime_diff(const xtime* xt, } -constexpr long long _Epoch = 0x19DB1DED53E8000LL; constexpr long _Nsec100_per_sec = _Nsec_per_sec / 100; _EXTERN_C @@ -50,7 +49,8 @@ _EXTERN_C long long _Xtime_get_ticks() { // get system time in 100-nanosecond intervals since the epoch FILETIME ft; __crtGetSystemTimePreciseAsFileTime(&ft); - return ((static_cast(ft.dwHighDateTime)) << 32) + static_cast(ft.dwLowDateTime) - _Epoch; + return ((static_cast(ft.dwHighDateTime)) << 32) + static_cast(ft.dwLowDateTime) + - __std_fs_file_time_epoch_adjustment; } static void sys_get_time(xtime* xt) { // get system time with nanosecond resolution diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp index 17cc460095..23ac3610eb 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp @@ -293,7 +293,7 @@ void test_file_clock_to_utc(const leap_second& leap, seconds offset) { offset = 27s; } - offset -= duration_cast(file_clock::duration{filesystem::__std_fs_file_time_epoch_adjustment}); + offset -= duration_cast(file_clock::duration{__std_fs_file_time_epoch_adjustment}); auto u = file_clock::to_utc(t); assert(u.time_since_epoch() - t.time_since_epoch() == offset); From 4296d97d3e05e1499f25760002a7f03d751ce398 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 21 Aug 2021 17:48:13 +0800 Subject: [PATCH 15/20] Explain the magic tolerance --- .../tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp index 21b99cfbce..2ea11ddec5 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp @@ -22,6 +22,7 @@ using namespace std::filesystem; template void test_clock_now() { + // Typical system clock resolution: (1/64) s = 15.625 ms constexpr auto tolerance = 16ms; const auto before = get_system_time(); From c5ee629eee826ae1cb2065d284120e974d6d0865 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 4 Sep 2021 23:09:51 +0800 Subject: [PATCH 16/20] =?UTF-8?q?Implement=20`file=5Ftime`=20=E2=86=94=20`?= =?UTF-8?q?utc=5Ftime`=20conversion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stl/inc/chrono | 131 ++++++++++-------- stl/src/xtime0.cpp | 2 + .../test.cpp | 8 +- 3 files changed, 80 insertions(+), 61 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index a1dcdf5018..afa02a5c7a 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -698,6 +698,21 @@ namespace chrono { _Duration _Sub_sec; }; + // get the last representable value before the time point + template + _NODISCARD static constexpr time_point<_Clock, _Duration> _Prev_time_point( + const time_point<_Clock, _Duration>& _Time) noexcept(is_arithmetic_v) { + using _Rep = typename _Duration::rep; + if constexpr (treat_as_floating_point_v<_Rep>) { + // TRANSITION, GH-1909 + const _Rep _Val = _Time.time_since_epoch().count(); + const _Rep _Prev_val = _STD nextafter(_Val, numeric_limits<_Rep>::lowest()); + return time_point<_Clock, _Duration>{_Duration{_Prev_val}}; + } else { + return _Time - _Duration{1}; + } + } + struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime using rep = long long; using period = ratio<1, 10'000'000>; // 100 nanoseconds @@ -720,18 +735,9 @@ namespace chrono { if (_Utc._Whole_sec._Second < 60) { return _Sys_secs + _Utc._Sub_sec; - } - - // positive leap second - using _Common_rep = typename _Common::rep; - if constexpr (treat_as_floating_point_v<_Common_rep>) { - // TRANSITION, GH-1909 - const _Common_rep _After_leap_sec = _Sys_secs.time_since_epoch().count(); - const _Common_rep _Before_leap_sec = - _STD nextafter(_After_leap_sec, -numeric_limits<_Common_rep>::infinity()); - return _Sys_time{_Common{_Before_leap_sec}}; } else { - return _Sys_secs - _Common{1}; + _STL_INTERNAL_CHECK(_Utc._Whole_sec._Second == 60); + return _CHRONO _Prev_time_point<_Common>(_Sys_secs); } } @@ -3313,28 +3319,18 @@ namespace chrono { using time_point = _CHRONO time_point; static constexpr bool is_steady = system_clock::is_steady; - _NODISCARD static time_point now() { - return from_sys(system_clock::now()); - } + _NODISCARD static time_point now(); template _NODISCARD static sys_time> to_sys(const utc_time<_Duration>& _Utc_time) { using _CommonType = common_type_t<_Duration, seconds>; const auto _Lsi{_CHRONO get_leap_second_info(_Utc_time)}; - _CommonType _Ticks; if (_Lsi.is_leap_second) { - const auto _Leap_sec_minus_one = _CHRONO floor(_Utc_time.time_since_epoch()) - _Lsi.elapsed; - if constexpr (is_integral_v) { - constexpr auto _Delta{seconds{1} - _CommonType{1}}; - _Ticks = _Leap_sec_minus_one + _Delta; - } else { - const auto _Leap_sec_begin = _CHRONO ceil<_CommonType>(_Leap_sec_minus_one + seconds{1}); - _Ticks = _CommonType{_STD nextafter(_Leap_sec_begin.count(), typename _CommonType::rep{0})}; - } + return _CHRONO _Prev_time_point(sys_time<_CommonType>{ + _CHRONO floor(_Utc_time.time_since_epoch()) - (_Lsi.elapsed - seconds{1})}); } else { - _Ticks = _Utc_time.time_since_epoch() - _Lsi.elapsed; + return sys_time<_CommonType>{_Utc_time.time_since_epoch() - _Lsi.elapsed}; } - return sys_time<_CommonType>{_Ticks}; } template @@ -3348,11 +3344,50 @@ namespace chrono { template _NODISCARD static _Utc_components> _To_utc_components( - const utc_time<_Duration>& _Utc_time) noexcept; // FIXME + const utc_time<_Duration>& _Utc_time) noexcept { + const utc_seconds _Utc_secs = _CHRONO floor(_Utc_time); + const auto _Lsi = _CHRONO get_leap_second_info(_Utc_secs); + const sys_seconds _Sys_secs{_Utc_secs.time_since_epoch() - _Lsi.elapsed}; + _Utc_components> _Utc = { + ._Whole_sec = _CHRONO _Civil_from_sys_seconds(_Sys_secs), + ._Sub_sec = _Utc_time - _Utc_secs, + }; + + if (_Lsi.is_leap_second) { + ++_Utc._Whole_sec._Second; + } + + return _Utc; + } template _NODISCARD static utc_time> _From_utc_components( - const _Utc_components<_Duration>& _Utc) noexcept; // FIXME + const _Utc_components<_Duration>& _Utc) noexcept { + using _Common = common_type_t<_Duration, seconds>; + const auto& _Tzdb = _CHRONO get_tzdb(); + const auto& _Ls_vector = _Tzdb.leap_seconds; + const sys_seconds _Sys_secs = _Sys_seconds_from_civil(_Utc._Whole_sec); + + // Check for the next leap second + _STL_INTERNAL_CHECK(_Utc._Whole_sec._Second < 61); + const sys_seconds _Sys_next_min = _Sys_secs - seconds{_Utc._Whole_sec._Second} + minutes{1}; + const auto _Next_leap = _STD lower_bound(_Ls_vector.begin(), _Ls_vector.end(), _Sys_next_min); + + // number of seconds of this minute + const seconds _Secs_of_this_min = _Next_leap != _Ls_vector.end() && _Next_leap->date() == _Sys_next_min + ? minutes{1} + _Next_leap->value() + : minutes{1}; + + // utc_time - sys_time at the beginning of this minute + const seconds _Offset = + _Next_leap == _Ls_vector.begin() ? seconds::zero() : _STD _Prev_iter(_Next_leap)->_Elapsed(); + + if (seconds(_Utc._Whole_sec._Second) < _Secs_of_this_min) { + return utc_time<_Common>{_Sys_secs.time_since_epoch() + _Offset + _Utc._Sub_sec}; + } else { + return _CHRONO _Prev_time_point(utc_time<_Common>{_Sys_next_min.time_since_epoch() + _Offset}); + } + } }; // [time.clock.tai] @@ -3465,41 +3500,23 @@ namespace filesystem { template _NODISCARD static chrono::utc_time> to_utc( const chrono::file_time<_Duration>& _File_time) { - using namespace chrono; - using _CommonType = common_type_t<_Duration, seconds>; - const auto _Ticks = _File_time.time_since_epoch() - - _CHRONO duration_cast(duration{__std_fs_file_time_epoch_adjustment}); - - if (_Ticks < _Cutoff.time_since_epoch()) { - return utc_clock::from_sys(sys_time<_CommonType>{_Ticks}); - } else { - return utc_time<_CommonType>{_Ticks + _Skipped_filetime_leap_seconds}; - } + return chrono::utc_clock::_From_utc_components(_To_utc_components(_File_time)); } template _NODISCARD static chrono::file_time> from_utc( const chrono::utc_time<_Duration>& _Utc_time) { - using namespace chrono; - file_time> _File_time{ - _CHRONO duration_cast(duration{__std_fs_file_time_epoch_adjustment})}; - - if (_Utc_time < utc_seconds{_Cutoff.time_since_epoch()} + _Skipped_filetime_leap_seconds) { - _File_time += utc_clock::to_sys(_Utc_time).time_since_epoch(); - } else { - _File_time += _Utc_time.time_since_epoch() - _Skipped_filetime_leap_seconds; - } - - return _File_time; + return _From_utc_components(chrono::utc_clock::_To_utc_components(_Utc_time)); } template _NODISCARD static chrono::_Utc_components> _To_utc_components( const chrono::file_time<_Duration>& _File_time) noexcept { using namespace chrono; + using _Common = common_type_t<_Duration, chrono::seconds>; const auto _File_seconds = _CHRONO floor(_File_time); - _Utc_components _Utc; + _Utc_components<_Common> _Utc; const __std_file_time_to_utc_errc _Ec = __std_file_seconds_to_utc_components(_File_seconds.time_since_epoch().count(), &_Utc._Whole_sec); _Utc._Sub_sec = _File_time - _File_seconds; @@ -3522,19 +3539,11 @@ namespace filesystem { long long _File_seconds; const __std_utc_to_file_time_errc _Ec = __std_utc_components_to_file_seconds(&_Utc._Whole_sec, &_File_seconds); + _STL_INTERNAL_CHECK(_Ec != __std_utc_to_file_time_errc::_Unknown_smear); if (_Ec == __std_utc_to_file_time_errc::_Nonexistent) { // unrepresentable second 60, deleted second 59, or nonexistent second 60 - using _Common_rep = typename _Common::rep; - if constexpr (treat_as_floating_point_v<_Common_rep>) { - // TRANSITION, GH-1909 - const _Common_rep _Next_representable = _Common{seconds{_File_seconds}}.count(); - const _Common_rep _Prev_representable = - _STD nextafter(_Next_representable, -numeric_limits<_Common_rep>::infinity()); - return file_time<_Common>{_Common{_Prev_representable}}; - } else { - return file_time<_Common>{seconds{_File_seconds} - _Common{1}}; - } + return _CHRONO _Prev_time_point(file_time<_Common>{seconds{_File_seconds}}); } if (_Ec != __std_utc_to_file_time_errc::_Invalid_parameter) { @@ -3551,6 +3560,10 @@ namespace filesystem { namespace chrono { #if _HAS_CXX20 + _NODISCARD inline utc_clock::time_point utc_clock::now() { + return file_clock::to_utc(file_clock::now()); + } + // [time.clock.conv] template diff --git a/stl/src/xtime0.cpp b/stl/src/xtime0.cpp index 51438a6e0b..d7ae4dd09a 100644 --- a/stl/src/xtime0.cpp +++ b/stl/src/xtime0.cpp @@ -105,6 +105,8 @@ _NODISCARD static _Utc_to_file_time_result _Utc_components_to_file_time( const __std_utc_components_1s& _Utc_time) noexcept { using enum __std_utc_to_file_time_errc; + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime + // The wDayOfWeek member of the SYSTEMTIME structure is ignored. SYSTEMTIME _St{ .wYear = static_cast(_Utc_time._Year), .wMonth = static_cast(_Utc_time._Month), diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp index 23ac3610eb..0a244fc0c0 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp @@ -446,10 +446,12 @@ void test() { test_utc_clock_to_sys(leap); test_utc_clock_to_sys(leap); test_utc_clock_to_sys>(leap); + test_utc_clock_from_sys(leap, offset); +#if 0 // TRANSITION: file_clock does not use STL leap second list for clock conversions test_file_clock_from_utc(leap); test_file_clock_from_utc(leap); - test_utc_clock_from_sys(leap, offset); test_file_clock_to_utc(leap, offset); +#endif offset += leap.value(); assert(leap._Elapsed() == offset); } @@ -473,10 +475,12 @@ void test() { test_utc_clock_to_sys(leap); test_utc_clock_to_sys(leap); test_utc_clock_to_sys>(leap); + test_utc_clock_from_sys(leap, offset); +#if 0 // TRANSITION: file_clock does not use STL leap second list for clock conversions test_file_clock_from_utc(leap); test_file_clock_from_utc(leap); - test_utc_clock_from_sys(leap, offset); test_file_clock_to_utc(leap, offset); +#endif offset += leap.value(); assert(leap._Elapsed() == offset); } From 96d850160c84e7bad40cbf1b2c155bfc627717b6 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 4 Sep 2021 23:10:52 +0800 Subject: [PATCH 17/20] Fix tests --- .../P0355R7_calendars_and_time_zones_clocks_now/test.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp index 2ea11ddec5..3889c95b3c 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/test.cpp @@ -10,9 +10,9 @@ using namespace std::chrono; using namespace std::filesystem; #endif -#include - #if _HAS_CXX20 +#include + [[nodiscard]] sys_time get_system_time() noexcept { SYSTEMTIME st; GetSystemTime(&st); @@ -35,6 +35,7 @@ void test_clock_now() { int main() { #if _HAS_CXX20 + test_clock_now(); test_clock_now(); test_clock_now(); test_clock_now(); From 0c3c99837f4ed08464f37cfb4bb8d54fef0980a4 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sun, 5 Sep 2021 15:18:50 +0800 Subject: [PATCH 18/20] Fix tests --- .../test.cpp | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp index 373f7da353..be4f3a42f2 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -901,7 +901,7 @@ void parse_whitespace() { fail_parse("", "%n", time); } -void insert_leap_second(const sys_days& date, const seconds& value) { +tzdb copy_tzdb(const ptrdiff_t max_count) { const auto& my_tzdb = get_tzdb_list().front(); vector zones; vector links; @@ -910,11 +910,18 @@ void insert_leap_second(const sys_days& date, const seconds& value) { transform(my_tzdb.links.begin(), my_tzdb.links.end(), back_inserter(links), [](const auto& link) { return time_zone_link{link.name(), link.target()}; }); + vector leap_seconds(my_tzdb.leap_seconds.begin(), + my_tzdb.leap_seconds.begin() + min(max_count, static_cast(my_tzdb.leap_seconds.size()))); + const bool all_ls_positive = + all_of(leap_seconds.cbegin(), leap_seconds.cend(), [](const leap_second& ls) { return ls.value() > 0s; }); - auto leap_vec = my_tzdb.leap_seconds; - leap_vec.emplace_back(date, value == 1s, leap_vec.back()._Elapsed()); - get_tzdb_list()._Emplace_front( - tzdb{my_tzdb.version, move(zones), move(links), move(leap_vec), my_tzdb._All_ls_positive && (value == 1s)}); + return {my_tzdb.version, move(zones), move(links), move(leap_seconds), all_ls_positive}; +} + +void insert_leap_second(tzdb& my_tzdb, const sys_days& date, const seconds& value) { + const bool is_positive = value > 0s; + my_tzdb.leap_seconds.emplace_back(date, is_positive, my_tzdb.leap_seconds.back()._Elapsed()); + my_tzdb._All_ls_positive = my_tzdb._All_ls_positive && is_positive; } void test_gh_1952() { @@ -930,9 +937,11 @@ void test_gh_1952() { test_parse(time_str, fmt, sys_parsed); assert(sys_parsed == clock_cast(utc_ref)); +#if 0 // FIXME: file_clock IO file_time file_parsed; test_parse(time_str, fmt, file_parsed); assert(file_parsed == clock_cast(utc_ref)); +#endif local_time local_parsed; test_parse(time_str, fmt, local_parsed); @@ -1048,8 +1057,12 @@ void parse_timepoints() { // Historical leap seconds don't allow complete testing, because they've all been positive and there haven't been // any since 2016 (as of 2021). - insert_leap_second(1d / January / 2020y, -1s); - insert_leap_second(1d / January / 2022y, 1s); + { + tzdb my_tzdb = copy_tzdb(27); + insert_leap_second(my_tzdb, 1d / January / 2020y, -1s); + insert_leap_second(my_tzdb, 1d / January / 2022y, 1s); + get_tzdb_list()._Emplace_front(move(my_tzdb)); + } utc_seconds ut_ref = utc_clock::from_sys(sys_days{1d / July / 1972y}) - 1s; // leap second insertion test_parse("june 30 23:59:60 1972", "%c", ut); @@ -1071,7 +1084,9 @@ void parse_timepoints() { ref = sys_days{1d / January / 2020y} - 1s; // negative leap second, UTC time doesn't exist fail_parse("dec 31 23:59:59 2019", "%c", ut); fail_parse("dec 31 23:59:59 2019", "%c", st); +#if 0 // TRANSITION: file_clock does not use STL leap second list for clock conversions fail_parse("dec 31 23:59:59 2019", "%c", ft); +#endif test_parse("dec 31 23:59:59 2019", "%c", lt); // Not UTC, might be valid depending on the time zone. assert(lt.time_since_epoch() == ref.time_since_epoch()); @@ -1106,8 +1121,10 @@ void parse_timepoints() { ut_ref = utc_clock::from_sys(sys_days{1d / January / 2022y}) - 1s; // leap second insertion test_parse("dec 31 23:59:60 2021", "%c", ut); assert(ut == ut_ref); +#if 0 // TRANSITION: file_clock does not use STL leap second list for clock conversions test_parse("dec 31 23:59:60 2021", "%c", ft); assert(ft == clock_cast(ut_ref)); +#endif // GH-1606: reads too many leading zeros From 5683bc07682b0b4a0c9ea6a75c784967fe2e32a7 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 2 Oct 2021 19:58:17 +0800 Subject: [PATCH 19/20] Implement `file_time` formatting --- stl/inc/chrono | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index afa02a5c7a..dbbf9d7537 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5648,6 +5648,11 @@ namespace chrono { } else { _Write_fractional_seconds<_Fractional_width>(_Os, _Hms.seconds(), _Hms.subseconds()); } + } else if constexpr (is_same_v<_Clock, file_clock>) { + const auto _Utc = file_clock::_To_utc_components(_Val); + const hh_mm_ss _Hms{_Utc._Sub_sec}; + constexpr auto _Fractional_width = decltype(_Hms)::fractional_width; + _Write_fractional_seconds<_Fractional_width>(_Os, seconds{_Utc._Whole_sec._Second}, _Hms.to_duration()); } else { const auto _Dp = _CHRONO floor(_Val); _Write_seconds(_Os, hh_mm_ss{_Val - _Dp}); @@ -5743,6 +5748,14 @@ namespace chrono { _Tm.tm_min = _Hms.tm_min; _Tm.tm_hour = _Hms.tm_hour; return _Tm; + } else if constexpr (_Is_specialization_v<_Ty, _Utc_components>) { + const year_month_day _Ymd{ + year{_Val._Whole_sec._Year}, month{_Val._Whole_sec._Month}, day{_Val._Whole_sec._Day}}; + tm _Tm = _Fill_tm(_Ymd); + _Tm.tm_sec = _Val._Whole_sec._Second; + _Tm.tm_min = _Val._Whole_sec._Minute; + _Tm.tm_hour = _Val._Whole_sec._Hour; + return _Tm; } tm _Time; @@ -6618,9 +6631,8 @@ struct formatter<_CHRONO file_time<_Duration>, _CharT> { template auto format(const _CHRONO file_time<_Duration>& _Val, _FormatContext& _FormatCtx) { - const auto _Utc = _CHRONO file_clock::to_utc(_Val); - const auto _Sys = _CHRONO utc_clock::to_sys(_Utc); - return _Impl._Write(_FormatCtx, _Utc, _Fill_tm(_Sys)); + const auto _Utc = _CHRONO file_clock::_To_utc_components(_Val); + return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Utc)); } private: From fd4ce85465b72de86d101c0f639d8bd640b969b7 Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 25 Jun 2022 20:22:09 +0800 Subject: [PATCH 20/20] usual_winsdk_matrix.lst no longer exists --- .../tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst index 7979e67048..19f025bd0e 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks_now/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_winsdk_matrix.lst +RUNALL_INCLUDE ..\usual_matrix.lst