STL/stl/inc/chrono

6142 строки
264 KiB
C++

// chrono standard header
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef _CHRONO_
#define _CHRONO_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#include <__msvc_chrono.hpp>
#if _HAS_CXX17
#include <system_error>
#include <xfilesystem_abi.h>
#include <xstring>
#endif // _HAS_CXX17
#if _HAS_CXX20
#include <__msvc_tzdb.hpp>
#include <atomic>
#include <cmath>
#include <compare>
#include <concepts>
#include <cstdint>
#include <format>
#include <forward_list>
#include <iomanip>
#include <istream>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
#include <xloctime>
#include <xthreads.h>
#endif // _HAS_CXX20
#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
_STD_BEGIN
#if _HAS_CXX17
_NODISCARD inline int _Check_convert_result(const __std_fs_convert_result _Result) {
if (_Result._Err != __std_win_error::_Success) {
_Throw_system_error_from_std_win_error(_Result._Err);
}
return _Result._Len;
}
template <class _Traits, class _Alloc>
_NODISCARD basic_string<typename _Traits::char_type, _Traits, _Alloc> _Convert_wide_to_narrow(
const __std_code_page _Code_page, const wstring_view _Input, const _Alloc& _Al) {
basic_string<typename _Traits::char_type, _Traits, _Alloc> _Output(_Al);
if (!_Input.empty()) {
if (!_STD _In_range<int>(_Input.size())) {
_Throw_system_error(errc::invalid_argument);
}
const int _Len = _Check_convert_result(
__std_fs_convert_wide_to_narrow(_Code_page, _Input.data(), static_cast<int>(_Input.size()), nullptr, 0));
_Output.resize(static_cast<size_t>(_Len));
const auto _Data_as_char = reinterpret_cast<char*>(_Output.data());
(void) _Check_convert_result(__std_fs_convert_wide_to_narrow(
_Code_page, _Input.data(), static_cast<int>(_Input.size()), _Data_as_char, _Len));
}
return _Output;
}
#endif // _HAS_CXX17
#if _HAS_CXX20
namespace chrono {
// [time.duration.io]
#define _IF_PERIOD_RETURN_SUFFIX_ELSE(_TYPE, _SUFFIX) \
if constexpr (is_same_v<_Period, _TYPE>) { \
if constexpr (is_same_v<_CharT, char>) { \
return _SUFFIX; \
} else { \
return L##_SUFFIX; \
} \
} else
template <class _CharT, class _Period>
_NODISCARD constexpr const _CharT* _Get_literal_unit_suffix() {
_IF_PERIOD_RETURN_SUFFIX_ELSE(atto, "as")
_IF_PERIOD_RETURN_SUFFIX_ELSE(femto, "fs")
_IF_PERIOD_RETURN_SUFFIX_ELSE(pico, "ps")
_IF_PERIOD_RETURN_SUFFIX_ELSE(nano, "ns")
_IF_PERIOD_RETURN_SUFFIX_ELSE(micro, "us")
_IF_PERIOD_RETURN_SUFFIX_ELSE(milli, "ms")
_IF_PERIOD_RETURN_SUFFIX_ELSE(centi, "cs")
_IF_PERIOD_RETURN_SUFFIX_ELSE(deci, "ds")
_IF_PERIOD_RETURN_SUFFIX_ELSE(seconds::period, "s")
_IF_PERIOD_RETURN_SUFFIX_ELSE(deca, "das")
_IF_PERIOD_RETURN_SUFFIX_ELSE(hecto, "hs")
_IF_PERIOD_RETURN_SUFFIX_ELSE(kilo, "ks")
_IF_PERIOD_RETURN_SUFFIX_ELSE(mega, "Ms")
_IF_PERIOD_RETURN_SUFFIX_ELSE(giga, "Gs")
_IF_PERIOD_RETURN_SUFFIX_ELSE(tera, "Ts")
_IF_PERIOD_RETURN_SUFFIX_ELSE(peta, "Ps")
_IF_PERIOD_RETURN_SUFFIX_ELSE(exa, "Es")
_IF_PERIOD_RETURN_SUFFIX_ELSE(minutes::period, "min")
_IF_PERIOD_RETURN_SUFFIX_ELSE(hours::period, "h")
_IF_PERIOD_RETURN_SUFFIX_ELSE(ratio<86400>, "d")
{
return nullptr;
}
}
#undef _IF_PERIOD_RETURN_SUFFIX_ELSE
template <class _CharT>
_NODISCARD _CharT* _Get_general_unit_suffix(_CharT* _Rnext, const intmax_t _Num, const intmax_t _Den) {
// Returns the head pointer of the string, built in reverse.
_STL_INTERNAL_CHECK(_Num > 0 && _Den > 0);
*--_Rnext = '\0';
*--_Rnext = 's';
*--_Rnext = ']';
if (_Den != 1) {
_Rnext = _UIntegral_to_buff(_Rnext, static_cast<uintmax_t>(_Den));
*--_Rnext = '/';
}
_Rnext = _UIntegral_to_buff(_Rnext, static_cast<uintmax_t>(_Num));
*--_Rnext = '[';
return _Rnext;
}
template <class _Period, class _CharT, class _Traits>
void _Write_unit_suffix(basic_ostream<_CharT, _Traits>& _Os) {
constexpr auto _Suffix = _Get_literal_unit_suffix<_CharT, _Period>();
if constexpr (_Suffix == nullptr) {
_CharT _Buffer[2 * (numeric_limits<intmax_t>::digits10 + 1) + 5] = {}; // 2 numbers + "[/]s\0"
const _CharT* const _Begin =
_Get_general_unit_suffix<_CharT>(_STD end(_Buffer), _Period::num, _Period::den);
_Os << _Begin;
} else {
_Os << _Suffix;
}
}
_EXPORT_STD template <class _CharT, class _Traits, class _Rep, class _Period>
basic_ostream<_CharT, _Traits>& operator<<(
basic_ostream<_CharT, _Traits>& _Os, const duration<_Rep, _Period>& _Dur) {
basic_ostringstream<_CharT, _Traits> _Sstr;
_Sstr.flags(_Os.flags());
_Sstr.imbue(_Os.getloc());
_Sstr.precision(_Os.precision());
_Sstr << _Dur.count();
_Write_unit_suffix<_Period>(_Sstr);
return _Os << _Sstr.str();
}
_EXPORT_STD struct local_t {};
_EXPORT_STD template <class _Duration>
using local_time = time_point<local_t, _Duration>;
_EXPORT_STD using local_seconds = local_time<seconds>;
_EXPORT_STD using local_days = local_time<days>;
_EXPORT_STD struct last_spec {
explicit last_spec() = default;
};
_EXPORT_STD inline constexpr last_spec last{};
_EXPORT_STD class day {
public:
day() = default;
constexpr explicit day(unsigned int _Val) noexcept : _Day{static_cast<unsigned char>(_Val)} {}
constexpr day& operator++() noexcept {
++_Day;
return *this;
}
constexpr day operator++(int) noexcept {
return day{_Day++};
}
constexpr day& operator--() noexcept {
--_Day;
return *this;
}
constexpr day operator--(int) noexcept {
return day{_Day--};
}
constexpr day& operator+=(const days& _Days) noexcept {
_Day += static_cast<unsigned char>(_Days.count());
return *this;
}
constexpr day& operator-=(const days& _Days) noexcept {
_Day -= static_cast<unsigned char>(_Days.count());
return *this;
}
_NODISCARD constexpr explicit operator unsigned int() const noexcept {
return _Day;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Day >= 1 && _Day <= 31;
}
private:
unsigned char _Day;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const day& _Left, const day& _Right) noexcept {
return static_cast<unsigned int>(_Left) == static_cast<unsigned int>(_Right);
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(const day& _Left, const day& _Right) noexcept {
return static_cast<unsigned int>(_Left) <=> static_cast<unsigned int>(_Right);
}
_EXPORT_STD _NODISCARD constexpr day operator+(const day& _Left, const days& _Right) noexcept {
return day{static_cast<unsigned int>(_Left) + _Right.count()};
}
_EXPORT_STD _NODISCARD constexpr day operator+(const days& _Left, const day& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr day operator-(const day& _Left, const days& _Right) noexcept {
return day{static_cast<unsigned int>(_Left) - _Right.count()};
}
_EXPORT_STD _NODISCARD constexpr days operator-(const day& _Left, const day& _Right) noexcept {
return days{
static_cast<int>(static_cast<unsigned int>(_Left)) - static_cast<int>(static_cast<unsigned int>(_Right))};
}
_EXPORT_STD class month {
public:
month() = default;
constexpr explicit month(unsigned int _Val) noexcept : _Month{static_cast<unsigned char>(_Val)} {}
constexpr month& operator++() noexcept {
*this += months{1};
return *this;
}
constexpr month operator++(int) noexcept {
month _Temp{*this};
++*this;
return _Temp;
}
constexpr month& operator--() noexcept {
*this -= months{1};
return *this;
}
constexpr month operator--(int) noexcept {
month _Temp{*this};
--*this;
return _Temp;
}
constexpr month& operator+=(const months& _Months) noexcept;
constexpr month& operator-=(const months& _Months) noexcept;
_NODISCARD constexpr explicit operator unsigned int() const noexcept {
return _Month;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Month >= 1 && _Month <= 12;
}
private:
unsigned char _Month;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const month& _Left, const month& _Right) noexcept {
return static_cast<unsigned int>(_Left) == static_cast<unsigned int>(_Right);
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(const month& _Left, const month& _Right) noexcept {
return static_cast<unsigned int>(_Left) <=> static_cast<unsigned int>(_Right);
}
_EXPORT_STD _NODISCARD constexpr month operator+(const month& _Left, const months& _Right) noexcept {
const auto _Mo = static_cast<long long>(static_cast<unsigned int>(_Left)) + (_Right.count() - 1);
const auto _Div = (_Mo >= 0 ? _Mo : _Mo - 11) / 12;
return month{static_cast<unsigned int>(_Mo - _Div * 12 + 1)};
}
_EXPORT_STD _NODISCARD constexpr month operator+(const months& _Left, const month& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr month operator-(const month& _Left, const months& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr months operator-(const month& _Left, const month& _Right) noexcept {
const auto _Mo = static_cast<unsigned int>(_Left) - static_cast<unsigned int>(_Right);
return months{_Mo <= 11 ? _Mo : _Mo + 12};
}
constexpr month& month::operator+=(const months& _Months) noexcept {
*this = *this + _Months;
return *this;
}
constexpr month& month::operator-=(const months& _Months) noexcept {
*this = *this - _Months;
return *this;
}
_EXPORT_STD class year {
public:
year() = default;
constexpr explicit year(int _Val) noexcept : _Year{static_cast<short>(_Val)} {}
constexpr year& operator++() noexcept {
++_Year;
return *this;
}
constexpr year operator++(int) noexcept {
return year{_Year++};
}
constexpr year& operator--() noexcept {
--_Year;
return *this;
}
constexpr year operator--(int) noexcept {
return year{_Year--};
}
constexpr year& operator+=(const years& _Years) noexcept {
#ifdef __EDG__ // TRANSITION, VSO-1271098
_Year = static_cast<short>(_Year + _Years.count());
#else // ^^^ workaround / no workaround vvv
_Year += static_cast<short>(_Years.count());
#endif // ^^^ no workaround ^^^
return *this;
}
constexpr year& operator-=(const years& _Years) noexcept {
#ifdef __EDG__ // TRANSITION, VSO-1271098
_Year = static_cast<short>(_Year - _Years.count());
#else // ^^^ workaround / no workaround vvv
_Year -= static_cast<short>(_Years.count());
#endif // ^^^ no workaround ^^^
return *this;
}
_NODISCARD constexpr year operator+() const noexcept {
return *this;
}
_NODISCARD constexpr year operator-() const noexcept {
return year{-_Year};
}
_NODISCARD constexpr bool is_leap() const noexcept {
return _Year % 4 == 0 && (_Year % 100 != 0 || _Year % 400 == 0);
}
_NODISCARD constexpr explicit operator int() const noexcept {
return _Year;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Year_min <= _Year && _Year <= _Year_max;
}
_NODISCARD static constexpr year(min)() noexcept {
return year{_Year_min};
}
_NODISCARD static constexpr year(max)() noexcept {
return year{_Year_max};
}
private:
short _Year;
static constexpr int _Year_min = -32767;
static constexpr int _Year_max = 32767;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const year& _Left, const year& _Right) noexcept {
return static_cast<int>(_Left) == static_cast<int>(_Right);
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(const year& _Left, const year& _Right) noexcept {
return static_cast<int>(_Left) <=> static_cast<int>(_Right);
}
_EXPORT_STD _NODISCARD constexpr year operator+(const year& _Left, const years& _Right) noexcept {
return year{static_cast<int>(_Left) + _Right.count()};
}
_EXPORT_STD _NODISCARD constexpr year operator+(const years& _Left, const year& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr year operator-(const year& _Left, const years& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr years operator-(const year& _Left, const year& _Right) noexcept {
return years{static_cast<int>(_Left) - static_cast<int>(_Right)};
}
_EXPORT_STD class weekday_indexed;
_EXPORT_STD class weekday_last;
_EXPORT_STD class weekday {
public:
weekday() = default;
constexpr explicit weekday(unsigned int _Val) noexcept
: _Weekday{static_cast<unsigned char>(_Val == 7 ? 0 : _Val)} {}
constexpr weekday(const sys_days& _Sys_day) noexcept
: _Weekday{static_cast<unsigned char>(_Weekday_from_days(_Sys_day.time_since_epoch().count()))} {}
constexpr explicit weekday(const local_days& _Local_day) noexcept
: _Weekday{static_cast<unsigned char>(_Weekday_from_days(_Local_day.time_since_epoch().count()))} {}
constexpr weekday& operator++() noexcept {
return *this += days{1};
}
constexpr weekday operator++(int) noexcept {
weekday _Temp{*this};
++*this;
return _Temp;
}
constexpr weekday& operator--() noexcept {
return *this -= days{1};
}
constexpr weekday operator--(int) noexcept {
weekday _Temp{*this};
--*this;
return _Temp;
}
constexpr weekday& operator+=(const days& _Days) noexcept;
constexpr weekday& operator-=(const days& _Days) noexcept;
_NODISCARD constexpr unsigned int c_encoding() const noexcept {
return _Weekday;
}
_NODISCARD constexpr unsigned int iso_encoding() const noexcept {
return _Weekday == 0u ? 7u : _Weekday;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Weekday <= 6;
}
_NODISCARD constexpr weekday_indexed operator[](unsigned int _Index) const noexcept;
_NODISCARD constexpr weekday_last operator[](last_spec) const noexcept;
private:
unsigned char _Weekday;
// courtesy of Howard Hinnant
// https://howardhinnant.github.io/date_algorithms.html#weekday_from_days
_NODISCARD static constexpr unsigned int _Weekday_from_days(int _Tp) noexcept {
return static_cast<unsigned int>(_Tp >= -4 ? (_Tp + 4) % 7 : (_Tp + 5) % 7 + 6);
}
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const weekday& _Left, const weekday& _Right) noexcept {
return _Left.c_encoding() == _Right.c_encoding();
}
_EXPORT_STD _NODISCARD constexpr weekday operator+(const weekday& _Left, const days& _Right) noexcept {
const auto _Wd = static_cast<long long>(_Left.c_encoding()) + _Right.count();
const auto _Div = (_Wd >= 0 ? _Wd : _Wd - 6) / 7;
return weekday{static_cast<unsigned int>(_Wd - _Div * 7)};
}
_EXPORT_STD _NODISCARD constexpr weekday operator+(const days& _Left, const weekday& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr weekday operator-(const weekday& _Left, const days& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr days operator-(const weekday& _Left, const weekday& _Right) noexcept {
const auto _Wd = _Left.c_encoding() - _Right.c_encoding();
const auto _Wk = _Wd <= 6 ? _Wd : _Wd + 7;
return days{_Wk};
}
constexpr weekday& weekday::operator+=(const days& _Days) noexcept {
*this = *this + _Days;
return *this;
}
constexpr weekday& weekday::operator-=(const days& _Days) noexcept {
*this = *this - _Days;
return *this;
}
_EXPORT_STD class weekday_indexed {
public:
weekday_indexed() = default;
constexpr weekday_indexed(const weekday& _Wd, unsigned int _Idx) noexcept
: _Weekday{_Wd}, _Index{static_cast<unsigned char>(_Idx)} {}
_NODISCARD constexpr weekday weekday() const noexcept {
return _Weekday;
}
_NODISCARD constexpr unsigned int index() const noexcept {
return _Index;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Weekday.ok() && _Index >= 1 && _Index <= 5;
}
private:
_CHRONO weekday _Weekday;
unsigned char _Index;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(
const weekday_indexed& _Left, const weekday_indexed& _Right) noexcept {
return _Left.weekday() == _Right.weekday() && _Left.index() == _Right.index();
}
_EXPORT_STD class weekday_last {
public:
constexpr explicit weekday_last(const weekday& _Wd) noexcept : _Weekday{_Wd} {}
_NODISCARD constexpr weekday weekday() const noexcept {
return _Weekday;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Weekday.ok();
}
private:
_CHRONO weekday _Weekday;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const weekday_last& _Left, const weekday_last& _Right) noexcept {
return _Left.weekday() == _Right.weekday();
}
_NODISCARD constexpr weekday_indexed weekday::operator[](unsigned int _Index) const noexcept {
return {*this, _Index};
}
_NODISCARD constexpr weekday_last weekday::operator[](last_spec) const noexcept {
return weekday_last{*this};
}
_EXPORT_STD class month_day {
public:
month_day() = default;
constexpr month_day(const month& _Month_, const day& _Day_) noexcept : _Month{_Month_}, _Day{_Day_} {}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
_NODISCARD constexpr day day() const noexcept {
return _Day;
}
_NODISCARD constexpr bool ok() const noexcept {
if (!_Month.ok() || !_Day.ok()) {
return false;
}
const auto _Da = static_cast<unsigned int>(_Day);
const auto _Mo = static_cast<unsigned int>(_Month);
if (_Mo == 2) {
return _Da <= 29;
}
if (_Mo == 4 || _Mo == 6 || _Mo == 9 || _Mo == 11) {
return _Da <= 30;
}
return true;
}
private:
_CHRONO month _Month;
_CHRONO day _Day;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const month_day& _Left, const month_day& _Right) noexcept {
return _Left.month() == _Right.month() && _Left.day() == _Right.day();
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(
const month_day& _Left, const month_day& _Right) noexcept {
const auto _Comp = _Left.month() <=> _Right.month();
if (_Comp != 0) {
return _Comp;
}
return _Left.day() <=> _Right.day();
}
_EXPORT_STD class month_day_last {
public:
constexpr explicit month_day_last(const month& _Month_) noexcept : _Month{_Month_} {}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Month.ok();
}
private:
_CHRONO month _Month;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(
const month_day_last& _Left, const month_day_last& _Right) noexcept {
return _Left.month() == _Right.month();
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(
const month_day_last& _Left, const month_day_last& _Right) noexcept {
return _Left.month() <=> _Right.month();
}
_EXPORT_STD class month_weekday {
public:
constexpr month_weekday(const month& _Month_, const weekday_indexed& _Wdi) noexcept
: _Month{_Month_}, _Weekday_index{_Wdi} {}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
_NODISCARD constexpr weekday_indexed weekday_indexed() const noexcept {
return _Weekday_index;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Month.ok() && _Weekday_index.ok();
}
private:
_CHRONO month _Month;
_CHRONO weekday_indexed _Weekday_index;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const month_weekday& _Left, const month_weekday& _Right) noexcept {
return _Left.month() == _Right.month() && _Left.weekday_indexed() == _Right.weekday_indexed();
}
_EXPORT_STD class month_weekday_last {
public:
constexpr month_weekday_last(const month& _Month_, const weekday_last& _Wdl) noexcept
: _Month{_Month_}, _Weekday_last{_Wdl} {}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
_NODISCARD constexpr weekday_last weekday_last() const noexcept {
return _Weekday_last;
}
_NODISCARD constexpr bool ok() const noexcept {
return _Month.ok() && _Weekday_last.ok();
}
private:
_CHRONO month _Month;
_CHRONO weekday_last _Weekday_last;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(
const month_weekday_last& _Left, const month_weekday_last& _Right) noexcept {
return _Left.month() == _Right.month() && _Left.weekday_last() == _Right.weekday_last();
}
_EXPORT_STD class year_month {
public:
year_month() = default;
constexpr year_month(const year& _Year_, const month& _Month_) noexcept : _Year{_Year_}, _Month{_Month_} {}
_NODISCARD constexpr year year() const noexcept {
return _Year;
}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
template <int = 0>
constexpr year_month& operator+=(const months& _Months) noexcept;
template <int = 0>
constexpr year_month& operator-=(const months& _Months) noexcept;
constexpr year_month& operator+=(const years& _Years) noexcept;
constexpr year_month& operator-=(const years& _Years) noexcept;
_NODISCARD constexpr bool ok() const noexcept {
return _Year.ok() && _Month.ok();
}
private:
_CHRONO year _Year;
_CHRONO month _Month;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(const year_month& _Left, const year_month& _Right) noexcept {
return _Left.year() == _Right.year() && _Left.month() == _Right.month();
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(
const year_month& _Left, const year_month& _Right) noexcept {
const auto _Comp = _Left.year() <=> _Right.year();
if (_Comp != 0) {
return _Comp;
}
return _Left.month() <=> _Right.month();
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month operator+(const year_month& _Left, const months& _Right) noexcept {
const auto _Mo = static_cast<long long>(static_cast<unsigned int>(_Left.month())) + (_Right.count() - 1);
const auto _Div = (_Mo >= 0 ? _Mo : _Mo - 11) / 12;
return year_month{_Left.year() + years{_Div}, month{static_cast<unsigned int>(_Mo - _Div * 12 + 1)}};
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month operator+(const months& _Left, const year_month& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month operator-(const year_month& _Left, const months& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr months operator-(const year_month& _Left, const year_month& _Right) noexcept {
return _Left.year() - _Right.year()
+ months{static_cast<int>(static_cast<unsigned int>(_Left.month()))
- static_cast<int>(static_cast<unsigned int>(_Right.month()))};
}
_EXPORT_STD _NODISCARD constexpr year_month operator+(const year_month& _Left, const years& _Right) noexcept {
return {year{_Left.year() + _Right}, _Left.month()};
}
_EXPORT_STD _NODISCARD constexpr year_month operator+(const years& _Left, const year_month& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr year_month operator-(const year_month& _Left, const years& _Right) noexcept {
return _Left + -_Right;
}
template <int>
constexpr year_month& year_month::operator+=(const months& _Months) noexcept {
*this = *this + _Months;
return *this;
}
template <int>
constexpr year_month& year_month::operator-=(const months& _Months) noexcept {
*this = *this - _Months;
return *this;
}
constexpr year_month& year_month::operator+=(const years& _Years) noexcept {
*this = *this + _Years;
return *this;
}
constexpr year_month& year_month::operator-=(const years& _Years) noexcept {
*this = *this - _Years;
return *this;
}
// To prevent UB by going out of bounds, four extra days with an invalid day are added.
inline constexpr day _Last_day_table[] = {day{31}, day{28}, day{31}, day{30}, day{31}, day{30}, day{31}, day{31},
day{30}, day{31}, day{30}, day{31}, day{255}, day{255}, day{255}, day{255}};
_NODISCARD constexpr day _Last_day(const year& _Year, const month& _Month) {
if (_Month == month{2} && _Year.is_leap()) {
return day{29};
}
return _Last_day_table[(static_cast<unsigned int>(_Month) - 1) & 0xF];
}
_EXPORT_STD class year_month_day_last;
_EXPORT_STD class year_month_day {
public:
year_month_day() = default;
constexpr year_month_day(const year& _Year_, const month& _Month_, const day& _Day_) noexcept
: _Year{_Year_}, _Month{_Month_}, _Day{_Day_} {}
constexpr year_month_day(const year_month_day_last& _Ymdl) noexcept;
constexpr year_month_day(const sys_days& _Sys_days) noexcept
: year_month_day{_Civil_from_days(_Sys_days.time_since_epoch().count())} {}
constexpr explicit year_month_day(const local_days& _Local_days) noexcept
: year_month_day{_Civil_from_days(_Local_days.time_since_epoch().count())} {}
template <int = 0>
constexpr year_month_day& operator+=(const months& _Months) noexcept;
template <int = 0>
constexpr year_month_day& operator-=(const months& _Months) noexcept;
constexpr year_month_day& operator+=(const years& _Years) noexcept;
constexpr year_month_day& operator-=(const years& _Years) noexcept;
_NODISCARD constexpr year year() const noexcept {
return _Year;
}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
_NODISCARD constexpr day day() const noexcept {
return _Day;
}
_NODISCARD constexpr operator sys_days() const noexcept {
return sys_days{_Days_from_civil()};
}
_NODISCARD constexpr explicit operator local_days() const noexcept {
return local_days{static_cast<sys_days>(*this).time_since_epoch()};
}
_NODISCARD constexpr bool ok() const noexcept {
if (!_Year.ok() || !_Month.ok()) {
return false;
}
return _Day >= _CHRONO day{1} && _Day <= _Last_day(_Year, _Month);
}
_NODISCARD constexpr int _Calculate_weekday() const noexcept {
const int _Day_int = static_cast<int>(static_cast<unsigned int>(_Day));
const int _Month_int = static_cast<int>(static_cast<unsigned int>(_Month));
const int _Era_year = static_cast<int>(_Year) - (_Month_int <= 2);
const int _Era = (_Era_year >= 0 ? _Era_year : _Era_year - 399) / 400;
const int _Yoe = _Era_year - _Era * 400;
const int _Yday_era = ((979 * (_Month_int + (_Month_int > 2 ? -3 : 9)) + 19) >> 5) + _Day_int - 1;
const int _Doe = ((1461 * _Yoe) >> 2) - _Yoe / 100 + _Yday_era;
return (_Doe + 3) % 7; // the era began on a Wednesday
}
private:
_CHRONO year _Year;
_CHRONO month _Month;
_CHRONO day _Day;
// _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.
// To simplify the handling of leap days (February 29th), the algorithm below uses a modified calendar
// internally, in which each year begins on March 1st, while January and February belong to the previous year.
// We denote the modified year and month number as _Yp and _Mp. We also define modified centuries that begin on
// each modified year whose _Yp is a multiple of 100.
// _Mp | Month | Day of Year
// --- | --------- | -----------
// 0 | March | [ 0, 30]
// 1 | April | [ 31, 60]
// 2 | May | [ 61, 91]
// 3 | June | [ 92, 121]
// 4 | July | [122, 152]
// 5 | August | [153, 183]
// 6 | September | [184, 213]
// 7 | October | [214, 244]
// 8 | November | [245, 274]
// 9 | December | [275, 305]
// 10 | January | [306, 336]
// 11 | February | [337, 365] on leap years, [337, 364] on regular years
// _Yp | First Day | Last Day (inclusive) | Leap Year?
// --- | ----------- | -------------------- | ----------
// -4 | -0004-03-01 | -0003-02-28 | No
// -3 | -0003-03-01 | -0002-02-28 | No
// -2 | -0002-03-01 | -0001-02-28 | No
// -1 | -0001-03-01 | 0000-02-29 | Yes
// 0 | 0000-03-01 | 0001-02-28 | No
// 1 | 0001-03-01 | 0002-02-28 | No
// 2 | 0002-03-01 | 0003-02-28 | No
// 3 | 0003-03-01 | 0004-02-29 | Yes
// _Century | First Day | Last Day (inclusive) | Long Century?
// -------- | ----------- | -------------------- | -------------
// -4 | -0400-03-01 | -0300-02-28 | No
// -3 | -0300-03-01 | -0200-02-28 | No
// -2 | -0200-03-01 | -0100-02-28 | No
// -1 | -0100-03-01 | 0000-02-29 | Yes
// 0 | 0000-03-01 | 0100-02-28 | No
// 1 | 0100-03-01 | 0200-02-28 | No
// 2 | 0200-03-01 | 0300-02-28 | No
// 3 | 0300-03-01 | 0400-02-29 | Yes
// The structure of the modified calendar:
// 1 ) It has a period of 4 centuries.
// 2 ) Each calendar period (146097 days) contains 3 regular centuries followed by a long century (36525 days).
// 3 ) Each regular century (36524 days) contains 24 regular 4-year spans followed by a short 4-year span.
// 3') Each long century (36525 days) contains 25 regular 4-year spans.
// 4 ) Each regular 4-year span (1461 days) contains 3 regular years followed by a leap year.
// 4') Each short 4-year span (1460 days) contains 4 regular years.
// Formula 1: Compute _Day_of_year of the first day of month _Mp
//
// _Day_of_year = (979 * _Mp + 19) >> 5
//
// A more well-known formula is 30 * _Mp + floor((3 * _Mp + 2) / 5) or floor((153 * _Mp + 2) / 5), which is used
// in Howard Hinnant's paper.
//
// The formula above returns the same result for all _Mp in [0, 11].
// Note that 979 / 2^5 = 30.59375 ~= 30.6 = 153 / 5.
// Formula 1': Compute _Mp from _Day_of_year
//
// _Mp = (535 * _Day_of_year + 333) >> 14
//
// Howard Hinnant's paper uses floor((5 * _Day_of_year + 2) / 153), the inverse of floor((153 * _Mp + 2) / 5) or
// ceil((153 * _Mp - 2) / 5).
//
// The formula above returns the same result for all _Day_of_year in [0, 365].
// Note that 2^14 / 535 = 30.624... ~= 30.6 = 153 / 5.
// Formula 2: Compute _Zx of the first day of year _Yp, where _Zx is the continuous count of days since
// 0000-03-01.
//
// _Zx = ((1461 * _Yp) >> 2) - _Century + (_Century >> 2)
//
// Start with multiplying by the number of days in regular years (365), add one day for the leap year in each
// 4-year span, subtract one day for the short 4-year span in each century, and finally add one day for the long
// century in each calendar period. This gives us 365 * _Yp + floor(_Yp / 4) - _Century + floor(_Century / 4).
// Formula 2-1: Compute _Day_of_century of the first day of year _Year_of_century
//
// _Day_of_century = (1461 * _Year_of_century) >> 2
//
// Start with multiplying by the number of days in regular years (365), add one day for the leap year in each
// 4-year span. This gives us 365 * _Year_of_century + floor(_Year_of_century / 4)
// == floor(1461 * _Year_of_century / 4).
// Formula 2-1': Compute _Year_of_century from _Day_of_century
//
// _Year_of_century = (91867 * (_Day_of_century + 1)) >> 25
//
// The inverse of floor(1461 * _Year_of_century / 4) or ceil((1461 * _Year_of_century - 3) / 4) is
// floor((4 * _Day_of_century + 3) / 1461).
//
// The formula above returns the same result for all _Day_of_century in [0, 36524].
// Note that 2^25 / 91867 = 365.2501... ~= 365.25 = 1461 / 4.
// Formula 2-2: Compute _Zx of the first day of century _Century, where _Zx is the continuous count of days
// since 0000-03-01.
//
// _Zx = (146097 * _Century) >> 2
//
// Start with multiplying by the number of days in regular centuries (36524), add one day for the long century
// in each calendar period. This gives us 36524 * _Century + floor(_Century / 4) = floor(146097 * _Century / 4).
// Formula 2-2': Compute _Century from _Zx, where _Zx is the continuous count of days since 0000-03-01.
//
// _Century = floor((4 * _Zx + 3) / 146097)
//
// This is the inverse of floor(146097 * _Year_of_century / 4) or ceil((146097 * _Year_of_century - 3) / 4)
// courtesy of Howard Hinnant
// https://howardhinnant.github.io/date_algorithms.html#civil_from_days
_NODISCARD static constexpr year_month_day _Civil_from_days(int _Tp) noexcept {
static_assert(numeric_limits<unsigned int>::digits >= 32);
static_assert(numeric_limits<int>::digits >= 26);
const int _Zx = _Tp + 719468; // Shift epoch to 0000-03-01
// Formula 2-2'
const int _Century = (_Zx >= 0 ? 4 * _Zx + 3 : 4 * _Zx - 146093) / 146097;
// Formula 2-2
const unsigned int _Day_of_century =
static_cast<unsigned int>(_Zx - ((146097 * _Century) >> 2)); // [0, 36524]
// Formula 2-1'
const unsigned int _Year_of_century = (91867 * (_Day_of_century + 1)) >> 25; // [0, 99]
const int _Yp = static_cast<int>(_Year_of_century) + _Century * 100; // Where March is the first month
// Formula 2-1
const unsigned int _Day_of_year = _Day_of_century - ((1461 * _Year_of_century) >> 2); // [0, 365]
// Formula 1'
const unsigned int _Mp = (535 * _Day_of_year + 333) >> 14; // [0, 11]
// Formula 1
const unsigned int _Day = _Day_of_year - ((979 * _Mp + 19) >> 5) + 1; // [1, 31]
const unsigned int _Month = _Mp + (_Mp < 10 ? 3 : static_cast<unsigned int>(-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<unsigned int>::digits >= 18);
static_assert(numeric_limits<int>::digits >= 26);
const unsigned int _Mo = static_cast<unsigned int>(_Month); // [1, 12]
const int _Yp = static_cast<int>(_Year) - (_Mo <= 2);
const int _Century = (_Yp >= 0 ? _Yp : _Yp - 99) / 100;
const unsigned int _Mp = _Mo + (_Mo > 2 ? static_cast<unsigned int>(-3) : 9); // [0, 11]
// Formula 1
const int _Day_of_year = static_cast<int>(((979 * _Mp + 19) >> 5) + static_cast<unsigned int>(_Day)) - 1;
// Formula 2
return days{((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468};
}
};
_EXPORT_STD _NODISCARD constexpr bool operator==(
const year_month_day& _Left, const year_month_day& _Right) noexcept {
return _Left.year() == _Right.year() && _Left.month() == _Right.month() && _Left.day() == _Right.day();
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(
const year_month_day& _Left, const year_month_day& _Right) noexcept {
auto _Comp = _Left.year() <=> _Right.year();
if (_Comp != 0) {
return _Comp;
}
_Comp = _Left.month() <=> _Right.month();
if (_Comp != 0) {
return _Comp;
}
return _Left.day() <=> _Right.day();
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_day operator+(const year_month_day& _Left, const months& _Right) noexcept {
const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right;
return {_Ym.year(), _Ym.month(), _Left.day()};
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_day operator+(const months& _Left, const year_month_day& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_day operator-(const year_month_day& _Left, const months& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator+(
const year_month_day& _Left, const years& _Right) noexcept {
return {_Left.year() + _Right, _Left.month(), _Left.day()};
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator+(
const years& _Left, const year_month_day& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator-(
const year_month_day& _Left, const years& _Right) noexcept {
return _Left + -_Right;
}
template <int>
constexpr year_month_day& year_month_day::operator+=(const months& _Months) noexcept {
*this = *this + _Months;
return *this;
}
template <int>
constexpr year_month_day& year_month_day::operator-=(const months& _Months) noexcept {
*this = *this - _Months;
return *this;
}
constexpr year_month_day& year_month_day::operator+=(const years& _Years) noexcept {
*this = *this + _Years;
return *this;
}
constexpr year_month_day& year_month_day::operator-=(const years& _Years) noexcept {
*this = *this - _Years;
return *this;
}
_EXPORT_STD class year_month_day_last {
public:
constexpr year_month_day_last(const year& _Year_, const month_day_last& _Mdl) noexcept
: _Year{_Year_}, _Month_day_last{_Mdl} {}
template <int = 0>
constexpr year_month_day_last& operator+=(const months& _Months) noexcept;
template <int = 0>
constexpr year_month_day_last& operator-=(const months& _Months) noexcept;
constexpr year_month_day_last& operator+=(const years& _Years) noexcept;
constexpr year_month_day_last& operator-=(const years& _Years) noexcept;
_NODISCARD constexpr year year() const noexcept {
return _Year;
}
_NODISCARD constexpr month month() const noexcept {
return _Month_day_last.month();
}
_NODISCARD constexpr month_day_last month_day_last() const noexcept {
return _Month_day_last;
}
_NODISCARD constexpr day day() const noexcept {
return _Last_day(year(), month());
}
_NODISCARD constexpr operator sys_days() const noexcept {
return sys_days{year_month_day{year(), month(), day()}};
}
_NODISCARD constexpr explicit operator local_days() const noexcept {
return local_days{static_cast<sys_days>(*this).time_since_epoch()};
}
_NODISCARD constexpr bool ok() const noexcept {
return _Year.ok() && _Month_day_last.ok();
}
private:
_CHRONO year _Year;
_CHRONO month_day_last _Month_day_last;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(
const year_month_day_last& _Left, const year_month_day_last& _Right) noexcept {
return _Left.year() == _Right.year() && _Left.month_day_last() == _Right.month_day_last();
}
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(
const year_month_day_last& _Left, const year_month_day_last& _Right) noexcept {
const auto _Comp = _Left.year() <=> _Right.year();
if (_Comp != 0) {
return _Comp;
}
return _Left.month_day_last() <=> _Right.month_day_last();
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_day_last operator+(
const year_month_day_last& _Left, const months& _Right) noexcept {
const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right;
return {_Ym.year(), month_day_last{_Ym.month()}};
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_day_last operator+(
const months& _Left, const year_month_day_last& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_day_last operator-(
const year_month_day_last& _Left, const months& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator+(
const year_month_day_last& _Left, const years& _Right) noexcept {
return {_Left.year() + _Right, _Left.month_day_last()};
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator+(
const years& _Left, const year_month_day_last& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator-(
const year_month_day_last& _Left, const years& _Right) noexcept {
return _Left + -_Right;
}
template <int>
constexpr year_month_day_last& year_month_day_last::operator+=(const months& _Months) noexcept {
*this = *this + _Months;
return *this;
}
template <int>
constexpr year_month_day_last& year_month_day_last::operator-=(const months& _Months) noexcept {
*this = *this - _Months;
return *this;
}
constexpr year_month_day_last& year_month_day_last::operator+=(const years& _Years) noexcept {
*this = *this + _Years;
return *this;
}
constexpr year_month_day_last& year_month_day_last::operator-=(const years& _Years) noexcept {
*this = *this - _Years;
return *this;
}
constexpr year_month_day::year_month_day(const year_month_day_last& _Ymdl) noexcept
: _Year{_Ymdl.year()}, _Month{_Ymdl.month()}, _Day{_Ymdl.day()} {}
_EXPORT_STD class year_month_weekday {
public:
year_month_weekday() = default;
constexpr year_month_weekday(const year& _Year_, const month& _Month_, const weekday_indexed& _Wdi) noexcept
: _Year{_Year_}, _Month{_Month_}, _Weekday_index{_Wdi} {}
constexpr year_month_weekday(const sys_days& _Sys_days) noexcept
: year_month_weekday{_Ymwd_from_days(_Sys_days.time_since_epoch())} {}
constexpr explicit year_month_weekday(const local_days& _Local_days) noexcept
: year_month_weekday{_Ymwd_from_days(_Local_days.time_since_epoch())} {}
template <int = 0>
constexpr year_month_weekday& operator+=(const months& _Months) noexcept;
template <int = 0>
constexpr year_month_weekday& operator-=(const months& _Months) noexcept;
constexpr year_month_weekday& operator+=(const years& _Years) noexcept;
constexpr year_month_weekday& operator-=(const years& _Years) noexcept;
_NODISCARD constexpr year year() const noexcept {
return _Year;
}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
_NODISCARD constexpr weekday weekday() const noexcept {
return _Weekday_index.weekday();
}
_NODISCARD constexpr unsigned int index() const noexcept {
return _Weekday_index.index();
}
_NODISCARD constexpr weekday_indexed weekday_indexed() const noexcept {
return _Weekday_index;
}
_NODISCARD constexpr operator sys_days() const noexcept {
const sys_days _First = year_month_day{_Year, _Month, day{1}};
const days _Diff = weekday() - _CHRONO weekday{_First};
const days _Days = _Diff + days{(static_cast<int>(index()) - 1) * 7};
return _First + _Days;
}
_NODISCARD constexpr explicit operator local_days() const noexcept {
return local_days{static_cast<sys_days>(*this).time_since_epoch()};
}
_NODISCARD constexpr bool ok() const noexcept {
if (!_Year.ok() || !_Month.ok() || !_Weekday_index.ok()) {
return false;
}
if (_Weekday_index.index() <= 4) {
return true;
}
// As index() == 5 is not always valid
// Determine the date of the first weekday and check if + days{28} is <= last day of the month
const sys_days _First_of_month = year_month_day{_Year, _Month, day{1}};
const days _First_weekday = weekday() - _CHRONO weekday{_First_of_month} + days{1};
const days _Last = _First_weekday + days{28};
return static_cast<unsigned int>(_Last.count()) <= static_cast<unsigned int>(_Last_day(_Year, _Month));
}
private:
_CHRONO year _Year;
_CHRONO month _Month;
_CHRONO weekday_indexed _Weekday_index;
_NODISCARD static constexpr year_month_weekday _Ymwd_from_days(days _Dp) noexcept {
const _CHRONO year_month_day _Ymd = sys_days{_Dp};
const _CHRONO weekday _Wd = sys_days{_Dp};
const auto _Idx = ((static_cast<unsigned int>(_Ymd.day()) - 1) / 7) + 1;
return {_Ymd.year(), _Ymd.month(), _Wd[_Idx]};
}
};
_EXPORT_STD _NODISCARD constexpr bool operator==(
const year_month_weekday& _Left, const year_month_weekday& _Right) noexcept {
return _Left.year() == _Right.year() && _Left.month() == _Right.month()
&& _Left.weekday_indexed() == _Right.weekday_indexed();
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_weekday operator+(const year_month_weekday& _Left, const months& _Right) noexcept {
const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right;
return {_Ym.year(), _Ym.month(), _Left.weekday_indexed()};
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_weekday operator+(const months& _Left, const year_month_weekday& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_weekday operator-(const year_month_weekday& _Left, const months& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator+(
const year_month_weekday& _Left, const years& _Right) noexcept {
return year_month_weekday{_Left.year() + _Right, _Left.month(), _Left.weekday_indexed()};
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator+(
const years& _Left, const year_month_weekday& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator-(
const year_month_weekday& _Left, const years& _Right) noexcept {
return _Left + -_Right;
}
template <int>
constexpr year_month_weekday& year_month_weekday::operator+=(const months& _Months) noexcept {
*this = *this + _Months;
return *this;
}
template <int>
constexpr year_month_weekday& year_month_weekday::operator-=(const months& _Months) noexcept {
*this = *this - _Months;
return *this;
}
constexpr year_month_weekday& year_month_weekday::operator+=(const years& _Years) noexcept {
*this = *this + _Years;
return *this;
}
constexpr year_month_weekday& year_month_weekday::operator-=(const years& _Years) noexcept {
*this = *this - _Years;
return *this;
}
_EXPORT_STD class year_month_weekday_last {
public:
constexpr year_month_weekday_last(const year& _Year_, const month& _Month_, const weekday_last& _Wdl) noexcept
: _Year{_Year_}, _Month{_Month_}, _Weekday_last{_Wdl} {}
template <int = 0>
constexpr year_month_weekday_last& operator+=(const months& _Months) noexcept;
template <int = 0>
constexpr year_month_weekday_last& operator-=(const months& _Months) noexcept;
constexpr year_month_weekday_last& operator+=(const years& _Years) noexcept;
constexpr year_month_weekday_last& operator-=(const years& _Years) noexcept;
_NODISCARD constexpr year year() const noexcept {
return _Year;
}
_NODISCARD constexpr month month() const noexcept {
return _Month;
}
_NODISCARD constexpr weekday weekday() const noexcept {
return _Weekday_last.weekday();
}
_NODISCARD constexpr weekday_last weekday_last() const noexcept {
return _Weekday_last;
}
_NODISCARD constexpr operator sys_days() const noexcept {
const sys_days _Last = year_month_day_last{_Year, month_day_last{_Month}};
const auto _Diff = _CHRONO weekday{_Last} - weekday();
return _Last - _Diff;
}
_NODISCARD constexpr explicit operator local_days() const noexcept {
return local_days{static_cast<sys_days>(*this).time_since_epoch()};
}
_NODISCARD constexpr bool ok() const noexcept {
return _Year.ok() && _Month.ok() && _Weekday_last.ok();
}
private:
_CHRONO year _Year;
_CHRONO month _Month;
_CHRONO weekday_last _Weekday_last;
};
_EXPORT_STD _NODISCARD constexpr bool operator==(
const year_month_weekday_last& _Left, const year_month_weekday_last& _Right) noexcept {
return _Left.year() == _Right.year() && _Left.month() == _Right.month()
&& _Left.weekday_last() == _Right.weekday_last();
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_weekday_last operator+(
const year_month_weekday_last& _Left, const months& _Right) noexcept {
const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right;
return {_Ym.year(), _Ym.month(), _Left.weekday_last()};
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_weekday_last operator+(
const months& _Left, const year_month_weekday_last& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD template <int = 0>
_NODISCARD constexpr year_month_weekday_last operator-(
const year_month_weekday_last& _Left, const months& _Right) noexcept {
return _Left + -_Right;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator+(
const year_month_weekday_last& _Left, const years& _Right) noexcept {
return {_Left.year() + _Right, _Left.month(), _Left.weekday_last()};
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator+(
const years& _Left, const year_month_weekday_last& _Right) noexcept {
return _Right + _Left;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator-(
const year_month_weekday_last& _Left, const years& _Right) noexcept {
return _Left + -_Right;
}
template <int>
constexpr year_month_weekday_last& year_month_weekday_last::operator+=(const months& _Months) noexcept {
*this = *this + _Months;
return *this;
}
template <int>
constexpr year_month_weekday_last& year_month_weekday_last::operator-=(const months& _Months) noexcept {
*this = *this - _Months;
return *this;
}
constexpr year_month_weekday_last& year_month_weekday_last::operator+=(const years& _Years) noexcept {
*this = *this + _Years;
return *this;
}
constexpr year_month_weekday_last& year_month_weekday_last::operator-=(const years& _Years) noexcept {
*this = *this - _Years;
return *this;
}
// Civil calendar conventional syntax operators
_EXPORT_STD _NODISCARD constexpr year_month operator/(const year& _Year, const month& _Month) noexcept {
return {_Year, _Month};
}
_EXPORT_STD _NODISCARD constexpr year_month operator/(const year& _Year, int _Month) noexcept {
return _Year / month{static_cast<unsigned int>(_Month)};
}
_EXPORT_STD _NODISCARD constexpr month_day operator/(const month& _Month, const day& _Day) noexcept {
return {_Month, _Day};
}
_EXPORT_STD _NODISCARD constexpr month_day operator/(const month& _Month, int _Day) noexcept {
return _Month / day{static_cast<unsigned int>(_Day)};
}
_EXPORT_STD _NODISCARD constexpr month_day operator/(int _Month, const day& _Day) noexcept {
return month{static_cast<unsigned int>(_Month)} / _Day;
}
_EXPORT_STD _NODISCARD constexpr month_day operator/(const day& _Day, const month& _Month) noexcept {
return _Month / _Day;
}
_EXPORT_STD _NODISCARD constexpr month_day operator/(const day& _Day, int _Month) noexcept {
return month{static_cast<unsigned int>(_Month)} / _Day;
}
_EXPORT_STD _NODISCARD constexpr month_day_last operator/(const month& _Month, last_spec) noexcept {
return month_day_last{_Month};
}
_EXPORT_STD _NODISCARD constexpr month_day_last operator/(int _Month, last_spec) noexcept {
return month{static_cast<unsigned int>(_Month)} / last;
}
_EXPORT_STD _NODISCARD constexpr month_day_last operator/(last_spec, const month& _Month) noexcept {
return _Month / last;
}
_EXPORT_STD _NODISCARD constexpr month_day_last operator/(last_spec, int _Month) noexcept {
return month{static_cast<unsigned int>(_Month)} / last;
}
_EXPORT_STD _NODISCARD constexpr month_weekday operator/(
const month& _Month, const weekday_indexed& _Wdi) noexcept {
return {_Month, _Wdi};
}
_EXPORT_STD _NODISCARD constexpr month_weekday operator/(int _Month, const weekday_indexed& _Wdi) noexcept {
return month{static_cast<unsigned int>(_Month)} / _Wdi;
}
_EXPORT_STD _NODISCARD constexpr month_weekday operator/(
const weekday_indexed& _Wdi, const month& _Month) noexcept {
return _Month / _Wdi;
}
_EXPORT_STD _NODISCARD constexpr month_weekday operator/(const weekday_indexed& _Wdi, int _Month) noexcept {
return month{static_cast<unsigned int>(_Month)} / _Wdi;
}
_EXPORT_STD _NODISCARD constexpr month_weekday_last operator/(
const month& _Month, const weekday_last& _Wdl) noexcept {
return {_Month, _Wdl};
}
_EXPORT_STD _NODISCARD constexpr month_weekday_last operator/(int _Month, const weekday_last& _Wdl) noexcept {
return month{static_cast<unsigned int>(_Month)} / _Wdl;
}
_EXPORT_STD _NODISCARD constexpr month_weekday_last operator/(
const weekday_last& _Wdl, const month& _Month) noexcept {
return _Month / _Wdl;
}
_EXPORT_STD _NODISCARD constexpr month_weekday_last operator/(const weekday_last& _Wdl, int _Month) noexcept {
return month{static_cast<unsigned int>(_Month)} / _Wdl;
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator/(const year_month& _Ym, const day& _Day) noexcept {
return {_Ym.year(), _Ym.month(), _Day};
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator/(const year_month& _Ym, int _Day) noexcept {
return _Ym / day{static_cast<unsigned int>(_Day)};
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator/(const year& _Year, const month_day& _Md) noexcept {
return _Year / _Md.month() / _Md.day();
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator/(int _Year, const month_day& _Md) noexcept {
return year{_Year} / _Md.month() / _Md.day();
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator/(const month_day& _Md, const year& _Year) noexcept {
return _Year / _Md.month() / _Md.day();
}
_EXPORT_STD _NODISCARD constexpr year_month_day operator/(const month_day& _Md, int _Year) noexcept {
return year{_Year} / _Md.month() / _Md.day();
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator/(const year_month& _Ym, last_spec) noexcept {
return {_Ym.year(), month_day_last{_Ym.month()}};
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator/(
const year& _Year, const month_day_last& _Mdl) noexcept {
return {_Year, _Mdl};
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator/(int _Year, const month_day_last& _Mdl) noexcept {
return year{_Year} / _Mdl;
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator/(
const month_day_last& _Mdl, const year& _Year) noexcept {
return _Year / _Mdl;
}
_EXPORT_STD _NODISCARD constexpr year_month_day_last operator/(const month_day_last& _Mdl, int _Year) noexcept {
return year{_Year} / _Mdl;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator/(
const year_month& _Ym, const weekday_indexed& _Wdi) noexcept {
return year_month_weekday{_Ym.year(), _Ym.month(), _Wdi};
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator/(
const year& _Year, const month_weekday& _Mwd) noexcept {
return year_month_weekday{_Year, _Mwd.month(), _Mwd.weekday_indexed()};
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator/(int _Year, const month_weekday& _Mwd) noexcept {
return year{_Year} / _Mwd;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator/(
const month_weekday& _Mwd, const year& _Year) noexcept {
return _Year / _Mwd;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday operator/(const month_weekday& _Mwd, int _Year) noexcept {
return year{_Year} / _Mwd;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator/(
const year_month& _Ym, const weekday_last& _Wdl) noexcept {
return {_Ym.year(), _Ym.month(), _Wdl};
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator/(
const year& _Year, const month_weekday_last& _Mwdl) noexcept {
return {_Year, _Mwdl.month(), _Mwdl.weekday_last()};
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator/(
int _Year, const month_weekday_last& _Mwdl) noexcept {
return year{_Year} / _Mwdl;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator/(
const month_weekday_last& _Mwdl, const year& _Year) noexcept {
return _Year / _Mwdl;
}
_EXPORT_STD _NODISCARD constexpr year_month_weekday_last operator/(
const month_weekday_last& _Mwdl, int _Year) noexcept {
return year{_Year} / _Mwdl;
}
// Calendrical constants
_EXPORT_STD inline constexpr weekday Sunday{0};
_EXPORT_STD inline constexpr weekday Monday{1};
_EXPORT_STD inline constexpr weekday Tuesday{2};
_EXPORT_STD inline constexpr weekday Wednesday{3};
_EXPORT_STD inline constexpr weekday Thursday{4};
_EXPORT_STD inline constexpr weekday Friday{5};
_EXPORT_STD inline constexpr weekday Saturday{6};
_EXPORT_STD inline constexpr month January{1};
_EXPORT_STD inline constexpr month February{2};
_EXPORT_STD inline constexpr month March{3};
_EXPORT_STD inline constexpr month April{4};
_EXPORT_STD inline constexpr month May{5};
_EXPORT_STD inline constexpr month June{6};
_EXPORT_STD inline constexpr month July{7};
_EXPORT_STD inline constexpr month August{8};
_EXPORT_STD inline constexpr month September{9};
_EXPORT_STD inline constexpr month October{10};
_EXPORT_STD inline constexpr month November{11};
_EXPORT_STD inline constexpr month December{12};
_NODISCARD constexpr intmax_t _Pow10(const unsigned int _Exp) {
intmax_t _Result = 1;
for (unsigned int _Ix = 0; _Ix < _Exp; ++_Ix) {
_Result *= 10;
}
return _Result;
}
template <class _To, class _From>
constexpr bool
_Duration_underflows_with = !treat_as_floating_point_v<typename _From::rep>
&& _From::period::den / _Gcd(_From::period::den, _To::period::den)
> INTMAX_MAX / (_To::period::num / _Gcd(_From::period::num, _To::period::num));
template <class _To, class _From>
_NODISCARD constexpr _To _Duration_cast_underflow_to_zero(const _From& _Dur) {
if constexpr (_Duration_underflows_with<_To, _From>) {
return _To{};
} else {
return _CHRONO duration_cast<_To>(_Dur);
}
}
template <class _To, class _From>
_NODISCARD constexpr common_type_t<_To, _From> _Remove_duration_part(const _From& _Val) {
if constexpr (_Duration_underflows_with<_To, _From>) {
return _Val;
} else {
const auto _Dp = _CHRONO duration_cast<_To>(_Val);
return _Val - _Dp;
}
}
_EXPORT_STD template <class _Duration>
class hh_mm_ss {
public:
static_assert(_Is_duration_v<_Duration>,
"N4950 [time.hms.overview]/2 mandates Duration to be a specialization of chrono::duration.");
static constexpr unsigned int fractional_width = [] {
// Returns the number of fractional digits of _Num / _Den in the range [0, 18].
// If it can't be represented, 6 is returned.
// Example: _Fractional_width(1, 8) would return 3 for 0.125.
auto _Den = _Duration::period::den;
_STL_ASSERT(_Duration::period::num > 0 && _Den > 0, "Numerator and denominator can't be less than 1.");
unsigned int _Power_of_2_in_den = 0u;
unsigned int _Power_of_5_in_den = 0u;
for (; _Den % 2 == 0; _Den /= 2, ++_Power_of_2_in_den) {
}
for (; _Den % 5 == 0; _Den /= 5, ++_Power_of_5_in_den) {
}
if (_Den != 1) {
return 6u;
}
const auto _Result = (_STD max)(_Power_of_2_in_den, _Power_of_5_in_den);
return _Result > 18u ? 6u : _Result;
}();
using precision =
duration<common_type_t<typename _Duration::rep, seconds::rep>, ratio<1, _Pow10(fractional_width)>>;
constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
constexpr explicit hh_mm_ss(_Duration _Dur)
: _Is_neg{_Dur < _Duration::zero()},
_Hours{_Duration_cast_underflow_to_zero<_CHRONO hours>(_CHRONO abs(_Dur))},
_Mins{_Duration_cast_underflow_to_zero<_CHRONO minutes>(
_Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur)))},
_Secs{_Duration_cast_underflow_to_zero<_CHRONO seconds>(
_Remove_duration_part<_CHRONO minutes>(_Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur))))} {
if constexpr (treat_as_floating_point_v<typename precision::rep>) {
// no need to deal with underflow here, because floating durations allow it
_Sub_secs = _CHRONO abs(_Dur) - hours() - minutes() - seconds();
} else {
_Sub_secs = _CHRONO duration_cast<precision>(_Remove_duration_part<_CHRONO seconds>(_CHRONO abs(_Dur)));
}
}
_NODISCARD constexpr bool is_negative() const noexcept {
return _Is_neg;
}
_NODISCARD constexpr hours hours() const noexcept {
return _Hours;
}
_NODISCARD constexpr minutes minutes() const noexcept {
return _Mins;
}
_NODISCARD constexpr seconds seconds() const noexcept {
return _Secs;
}
_NODISCARD constexpr precision subseconds() const noexcept {
return _Sub_secs;
}
_NODISCARD constexpr explicit operator precision() const noexcept {
return to_duration();
}
_NODISCARD constexpr precision to_duration() const noexcept {
const auto _Dur = _Hours + _Mins + _Secs + _Sub_secs;
return _Is_neg ? -_Dur : _Dur;
}
private:
bool _Is_neg;
_CHRONO hours _Hours;
_CHRONO minutes _Mins;
_CHRONO seconds _Secs;
precision _Sub_secs;
};
_EXPORT_STD _NODISCARD constexpr bool is_am(const hours& _Hours) noexcept {
return _Hours >= hours{0} && _Hours <= hours{11};
}
_EXPORT_STD _NODISCARD constexpr bool is_pm(const hours& _Hours) noexcept {
return _Hours >= hours{12} && _Hours <= hours{23};
}
_EXPORT_STD _NODISCARD constexpr hours make12(const hours& _Hours) noexcept {
const auto _H_count{_Hours.count()};
auto _Ret{_H_count == 0 ? 12 : _H_count};
if (_Ret > 12) {
_Ret -= 12;
}
return hours{_Ret};
}
_EXPORT_STD _NODISCARD constexpr hours make24(const hours& _Hours, bool _Is_pm) noexcept {
const auto _H_count{_Hours.count()};
auto _Ret{_H_count == 12 ? 0 : _H_count};
if (_Is_pm) {
_Ret += 12;
}
return hours{_Ret};
}
// [time.zone.info]
_EXPORT_STD struct sys_info {
sys_seconds begin;
sys_seconds end;
seconds offset;
minutes save;
string abbrev;
};
_EXPORT_STD struct local_info {
static constexpr int unique = 0;
static constexpr int nonexistent = 1;
static constexpr int ambiguous = 2;
int result;
sys_info first;
sys_info second;
};
_EXPORT_STD class nonexistent_local_time : public runtime_error {
public:
template <class _Duration>
nonexistent_local_time(const local_time<_Duration>& _Tp, const local_info& _Info)
: runtime_error(_Make_string(_Tp, _Info)) {}
private:
template <class _Duration>
_NODISCARD static string _Make_string(const local_time<_Duration>& _Tp, const local_info& _Info);
};
template <class _Duration>
[[noreturn]] void _Throw_nonexistent_local_time(const local_time<_Duration>& _Tp, const local_info& _Info) {
_THROW(nonexistent_local_time{_Tp, _Info});
}
_EXPORT_STD class ambiguous_local_time : public runtime_error {
public:
template <class _Duration>
ambiguous_local_time(const local_time<_Duration>& _Tp, const local_info& _Info)
: runtime_error(_Make_string(_Tp, _Info)) {}
private:
template <class _Duration>
_NODISCARD static string _Make_string(const local_time<_Duration>& _Tp, const local_info& _Info);
};
template <class _Duration>
[[noreturn]] void _Throw_ambiguous_local_time(const local_time<_Duration>& _Tp, const local_info& _Info) {
_THROW(ambiguous_local_time{_Tp, _Info});
}
// [time.zone.timezone]
template <auto& _Get_tzdb_info, class... _Types>
_NODISCARD auto _Make_unique_tzdb_info(_Types&&... _Args) {
const auto _Raw_ptr = _Get_tzdb_info(_STD forward<_Types>(_Args)...);
using _Tzdb_info = remove_pointer_t<decltype(_Raw_ptr)>;
unique_ptr<_Tzdb_info, _Tzdb_deleter<_Tzdb_info>> _Info{_Raw_ptr};
if (_Info == nullptr) {
_Xbad_alloc();
} else if (_Info->_Err == __std_tzdb_error::_Win_error) {
_XGetLastError();
} else if (_Info->_Err == __std_tzdb_error::_Icu_error) {
_Xruntime_error("Internal error loading IANA database information");
}
return _Info;
}
_EXPORT_STD enum class choose { earliest, latest };
struct _Secret_time_zone_construct_tag {
explicit _Secret_time_zone_construct_tag() = default;
};
_EXPORT_STD class time_zone {
public:
explicit time_zone(_Secret_time_zone_construct_tag, string_view _Name_) : _Name(_Name_) {}
time_zone(time_zone&&) = default;
time_zone& operator=(time_zone&&) = default;
_NODISCARD string_view name() const noexcept {
return _Name;
}
template <class _Duration>
_NODISCARD sys_info get_info(const sys_time<_Duration>& _Sys) const {
return _Get_info(_Sys.time_since_epoch(), __std_tzdb_sys_info_type::_Full);
}
template <class _Duration>
_NODISCARD local_info get_info(const local_time<_Duration>& _Local) const {
return _Get_local_info(_Local, __std_tzdb_sys_info_type::_Full);
}
template <class _Duration>
_NODISCARD sys_time<common_type_t<_Duration, seconds>> to_sys(const local_time<_Duration>& _Local) const {
const auto _Info = get_info(_Local);
if (_Info.result == local_info::nonexistent) {
_Throw_nonexistent_local_time(_Local, _Info);
} else if (_Info.result == local_info::ambiguous) {
_Throw_ambiguous_local_time(_Local, _Info);
}
return sys_time<common_type_t<_Duration, seconds>>{_Local.time_since_epoch() - _Info.first.offset};
}
template <class _Duration>
_NODISCARD sys_time<common_type_t<_Duration, seconds>> to_sys(
const local_time<_Duration>& _Local, const choose _Choose) const {
const auto _Info = _Get_local_info(_Local, __std_tzdb_sys_info_type::_Offset_and_range);
if (_Info.result == local_info::nonexistent) {
return _Info.first.end;
}
const auto _Offset = (_Info.result == local_info::unique || _Choose == choose::earliest)
? _Info.first.offset
: _Info.second.offset;
return sys_time<common_type_t<_Duration, seconds>>{_Local.time_since_epoch() - _Offset};
}
template <class _Duration>
_NODISCARD local_time<common_type_t<_Duration, seconds>> to_local(const sys_time<_Duration>& _Sys) const {
const auto _Info = _Get_info(_Sys.time_since_epoch(), __std_tzdb_sys_info_type::_Offset_only);
return local_time<common_type_t<_Duration, seconds>>{_Sys.time_since_epoch() + _Info.offset};
}
static constexpr sys_seconds _Min_seconds{sys_days{(year::min)() / January / 1}};
static constexpr sys_seconds _Max_seconds{sys_seconds{sys_days{(year::max)() / December / 32}} - seconds{1}};
private:
template <class _Duration>
_NODISCARD sys_info _Get_info(const _Duration& _Dur, __std_tzdb_sys_info_type _Type) const {
using _Internal_duration = duration<__std_tzdb_epoch_milli, milli>;
const auto _Internal_dur = _CHRONO duration_cast<_Internal_duration>(_Dur);
// TRANSITION, vNext
// Because the signature of __std_tzdb_get_sys_info cannot be changed, _Type is encoded in the
// time zone name. In vNext, this should be a dedicated argument.
const string _Tz_arg = _Name + static_cast<char>(_Type);
const auto _Tz_len = _Name.length();
const auto _Info =
_Make_unique_tzdb_info<__std_tzdb_get_sys_info>(_Tz_arg.c_str(), _Tz_len, _Internal_dur.count());
constexpr auto _Min_internal =
_CHRONO duration_cast<_Internal_duration>(_Min_seconds.time_since_epoch()).count();
constexpr auto _Max_internal =
_CHRONO duration_cast<_Internal_duration>(_Max_seconds.time_since_epoch()).count();
const auto _Begin =
_Info->_Begin <= _Min_internal
? _Min_seconds
: sys_seconds{_CHRONO duration_cast<sys_seconds::duration>(_Internal_duration{_Info->_Begin})};
const auto _End =
_Info->_End >= _Max_internal
? _Max_seconds
: sys_seconds{_CHRONO duration_cast<sys_seconds::duration>(_Internal_duration{_Info->_End})};
return {.begin = _Begin,
.end = _End,
.offset = _CHRONO duration_cast<seconds>(_Internal_duration{_Info->_Offset}),
.save = _CHRONO duration_cast<minutes>(_Internal_duration{_Info->_Save}),
.abbrev = _Info->_Abbrev ? _Info->_Abbrev : ""};
}
template <class _Duration>
_NODISCARD local_info _Get_local_info(
const local_time<_Duration>& _Local, __std_tzdb_sys_info_type _Type) const {
local_info _Info{};
const auto _Time_since_ep = _Local.time_since_epoch();
_Info.first = _Get_info(_Time_since_ep, _Type);
const sys_seconds _Local_sys{_CHRONO duration_cast<sys_seconds::duration>(_Time_since_ep)};
const auto _Curr_sys = _Local_sys - _Info.first.offset;
if (_Info.first.begin != _Min_seconds && _Curr_sys < _Info.first.begin + days{1}) {
// get previous transition information
_Info.second = _Get_info((_Info.first.begin - seconds{1}).time_since_epoch(), _Type);
const auto _Transition = _Info.first.begin;
const auto _Prev_sys = _Local_sys - _Info.second.offset;
if (_Curr_sys >= _Transition) {
if (_Prev_sys < _Transition) {
_Info.result = local_info::ambiguous;
_STD swap(_Info.first, _Info.second);
} else {
_Info.result = local_info::unique;
_Info.second = {};
}
} else {
if (_Prev_sys >= _Transition) {
_Info.result = local_info::nonexistent;
_STD swap(_Info.first, _Info.second);
} else {
_Info.result = local_info::unique;
_Info.first = _STD move(_Info.second);
_Info.second = {};
}
}
} else if (_Info.first.end != _Max_seconds && _Curr_sys > _Info.first.end - days{1}) {
// get next transition information
_Info.second = _Get_info((_Info.first.end + seconds{1}).time_since_epoch(), _Type);
const auto _Transition = _Info.first.end;
const auto _Next_sys = _Local_sys - _Info.second.offset;
if (_Curr_sys < _Transition) {
if (_Next_sys >= _Transition) {
_Info.result = local_info::ambiguous;
} else {
_Info.result = local_info::unique;
_Info.second = {};
}
} else {
if (_Next_sys < _Transition) {
_Info.result = local_info::nonexistent;
} else {
_Info.result = local_info::unique;
_Info.first = _STD move(_Info.second);
_Info.second = {};
}
}
} else {
// local time is contained inside of first transition boundaries by at least 1 day
_Info.result = local_info::unique;
_Info.second = {};
}
return _Info;
}
string _Name;
};
_EXPORT_STD _NODISCARD inline bool operator==(const time_zone& _Left, const time_zone& _Right) noexcept {
return _Left.name() == _Right.name();
}
_EXPORT_STD _NODISCARD inline strong_ordering operator<=>(
const time_zone& _Left, const time_zone& _Right) noexcept {
return _Left.name() <=> _Right.name();
}
// [time.zone.leap]
_EXPORT_STD class leap_second {
public:
leap_second(const leap_second&) = default;
leap_second& operator=(const leap_second&) = default;
constexpr leap_second(
const sys_seconds& _Date_, const bool _Is_positive_, const seconds& _Prev_elapsed) noexcept
: _Date{_Date_}, _Is_positive{_Is_positive_} {
_Elapsed_offset = _Prev_elapsed + value();
}
_NODISCARD constexpr sys_seconds date() const noexcept {
return _Date;
}
_NODISCARD constexpr seconds value() const noexcept {
return _Is_positive ? seconds{1} : seconds{-1};
}
_NODISCARD constexpr bool _Positive() const noexcept {
return _Is_positive;
}
_NODISCARD constexpr seconds _Elapsed() const noexcept {
return _Elapsed_offset;
}
_NODISCARD friend constexpr bool operator==(const leap_second& _Left, const leap_second& _Right) noexcept {
return _Left.date() == _Right.date();
}
template <class _Duration>
_NODISCARD friend constexpr bool operator==(
const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept {
return _Left.date() == _Right;
}
template <class _Duration>
_NODISCARD friend constexpr bool operator<(
const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept {
return _Left.date() < _Right;
}
template <class _Duration>
_NODISCARD friend constexpr bool operator<(
const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept {
return _Left < _Right.date();
}
template <class _Duration>
_NODISCARD friend constexpr bool operator>(
const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept {
return _Right < _Left.date();
}
template <class _Duration>
_NODISCARD friend constexpr bool operator>(
const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept {
return _Right.date() < _Left;
}
template <class _Duration>
_NODISCARD friend constexpr bool operator<=(
const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept {
return !(_Right < _Left.date());
}
template <class _Duration>
_NODISCARD friend constexpr bool operator<=(
const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept {
return !(_Right.date() < _Left);
}
template <class _Duration>
_NODISCARD friend constexpr bool operator>=(
const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept {
return !(_Left.date() < _Right);
}
template <class _Duration>
_NODISCARD friend constexpr bool operator>=(
const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept {
return !(_Left < _Right.date());
}
template <class _Duration>
requires three_way_comparable_with<sys_seconds, sys_time<_Duration>>
_NODISCARD friend constexpr auto operator<=>(
const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept {
return _Left.date() <=> _Right;
}
_NODISCARD friend constexpr strong_ordering operator<=>(
const leap_second& _Left, const leap_second& _Right) noexcept {
return _Left.date() <=> _Right.date();
}
private:
sys_seconds _Date;
bool _Is_positive;
seconds _Elapsed_offset;
};
// [time.zone.link]
struct _Secret_time_zone_link_construct_tag {
explicit _Secret_time_zone_link_construct_tag() = default;
};
_EXPORT_STD class time_zone_link {
public:
explicit time_zone_link(_Secret_time_zone_link_construct_tag, string_view _Name_, string_view _Target_)
: _Name(_Name_), _Target(_Target_) {}
time_zone_link(time_zone_link&&) = default;
time_zone_link& operator=(time_zone_link&&) = default;
_NODISCARD string_view name() const noexcept {
return _Name;
}
_NODISCARD string_view target() const noexcept {
return _Target;
}
private:
string _Name;
string _Target;
};
_EXPORT_STD _NODISCARD inline bool operator==(const time_zone_link& _Left, const time_zone_link& _Right) noexcept {
return _Left.name() == _Right.name();
}
_EXPORT_STD _NODISCARD inline strong_ordering operator<=>(
const time_zone_link& _Left, const time_zone_link& _Right) noexcept {
return _Left.name() <=> _Right.name();
}
// [time.zone.db]
_NODISCARD inline string _Tzdb_generate_current_zone() {
auto _Info = _Make_unique_tzdb_info<__std_tzdb_get_current_zone>();
return _Info->_Tz_name;
}
template <class _Ty>
_NODISCARD const _Ty* _Locate_zone_impl(const vector<_Ty>& _Vec, string_view _Name) {
const auto _Result = _STD find_if(_Vec.begin(), _Vec.end(), [&](auto& _Tz) { return _Tz.name() == _Name; });
return _Result == _Vec.end() ? nullptr : &*_Result;
}
_EXPORT_STD struct tzdb {
string version;
vector<time_zone> zones;
vector<time_zone_link> links;
vector<leap_second> leap_seconds;
bool _All_ls_positive;
_NODISCARD const time_zone* locate_zone(string_view _Tz_name) const {
auto _Tz = _Locate_zone_impl(zones, _Tz_name);
if (_Tz != nullptr) {
return _Tz;
}
const auto _Link = _Locate_zone_impl(links, _Tz_name);
if (_Link != nullptr) {
_Tz = _Locate_zone_impl(zones, _Link->target());
if (_Tz != nullptr) {
return _Tz;
}
}
_Xruntime_error("unable to locate time_zone with given name");
}
_NODISCARD const time_zone* current_zone() const {
return locate_zone(_Tzdb_generate_current_zone());
}
};
_NODISCARD inline tuple<string, vector<time_zone>, vector<time_zone_link>> _Tzdb_generate_time_zones() {
auto _Info = _Make_unique_tzdb_info<__std_tzdb_get_time_zones>();
vector<time_zone> _Time_zones;
vector<time_zone_link> _Time_zone_links;
for (size_t _Idx = 0; _Idx < _Info->_Num_time_zones; ++_Idx) {
const string_view _Name{_Info->_Names[_Idx]};
if (_Info->_Links[_Idx] == nullptr) {
_Time_zones.emplace_back(_Secret_time_zone_construct_tag{}, _Name);
} else {
const string_view _Target{_Info->_Links[_Idx]};
_Time_zone_links.emplace_back(_Secret_time_zone_link_construct_tag{}, _Name, _Target);
}
}
return {_Info->_Version, _STD move(_Time_zones), _STD move(_Time_zone_links)};
}
_NODISCARD inline pair<decltype(tzdb::leap_seconds), bool> _Tzdb_generate_leap_seconds(const size_t _Current_size) {
// Returns empty vector if no new leap seconds are found.
static constexpr leap_second _Known_leap_seconds[]{
{sys_seconds{seconds{78796800}}, true, seconds{0}},
{sys_seconds{seconds{94694400}}, true, seconds{1}},
{sys_seconds{seconds{126230400}}, true, seconds{2}},
{sys_seconds{seconds{157766400}}, true, seconds{3}},
{sys_seconds{seconds{189302400}}, true, seconds{4}},
{sys_seconds{seconds{220924800}}, true, seconds{5}},
{sys_seconds{seconds{252460800}}, true, seconds{6}},
{sys_seconds{seconds{283996800}}, true, seconds{7}},
{sys_seconds{seconds{315532800}}, true, seconds{8}},
{sys_seconds{seconds{362793600}}, true, seconds{9}},
{sys_seconds{seconds{394329600}}, true, seconds{10}},
{sys_seconds{seconds{425865600}}, true, seconds{11}},
{sys_seconds{seconds{489024000}}, true, seconds{12}},
{sys_seconds{seconds{567993600}}, true, seconds{13}},
{sys_seconds{seconds{631152000}}, true, seconds{14}},
{sys_seconds{seconds{662688000}}, true, seconds{15}},
{sys_seconds{seconds{709948800}}, true, seconds{16}},
{sys_seconds{seconds{741484800}}, true, seconds{17}},
{sys_seconds{seconds{773020800}}, true, seconds{18}},
{sys_seconds{seconds{820454400}}, true, seconds{19}},
{sys_seconds{seconds{867715200}}, true, seconds{20}},
{sys_seconds{seconds{915148800}}, true, seconds{21}},
{sys_seconds{seconds{1136073600}}, true, seconds{22}},
{sys_seconds{seconds{1230768000}}, true, seconds{23}},
{sys_seconds{seconds{1341100800}}, true, seconds{24}},
{sys_seconds{seconds{1435708800}}, true, seconds{25}},
{sys_seconds{seconds{1483228800}}, true, seconds{26}},
};
// __std_tzdb_get_leap_seconds gets leap second (LS) data from the registry, but only if it contains more
// LSs than we already know about. The registry only contains LSs after 2018, so we need to tell it how many of
// *those* we already know about. The *total* number of LSs known at this point is a combination of what the
// caller knows (_Current_size, 0 on first call) and the _Known_leap_seconds entries.
constexpr size_t _Pre_2018_count = 27;
const size_t _Known_post_2018_ls_size =
(_STD max)(_Current_size, _STD size(_Known_leap_seconds)) - _Pre_2018_count;
size_t _Reg_post_2018_ls_size; // number of post-2018 LSs found in the registry
unique_ptr<__std_tzdb_leap_info[], _Tzdb_deleter<__std_tzdb_leap_info[]>> _Reg_ls_data{
__std_tzdb_get_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size)};
if (_Reg_post_2018_ls_size > _Known_post_2018_ls_size && !_Reg_ls_data) {
_Xbad_alloc(); // registry has new data, but failed to allocate storage
} else if (_Reg_post_2018_ls_size == 0 && _Reg_ls_data) {
_XGetLastError(); // allocated storage for registry data, but failed to read
}
const size_t _New_size = _Pre_2018_count + _Reg_post_2018_ls_size; // total size with registry data
decltype(tzdb::leap_seconds) _Leap_sec_info;
bool _All_ls_positive = true;
if (_New_size > _Current_size) {
_Leap_sec_info.reserve(_New_size);
_Leap_sec_info.assign(_STD cbegin(_Known_leap_seconds), _STD cend(_Known_leap_seconds));
for (size_t _Idx = 0; _Idx < _Reg_post_2018_ls_size; ++_Idx) {
// Leap seconds occur at _Ls._Hour:59:59. We store the next second after, so we need to add an entire
// hour.
const auto& _Ls = _Reg_ls_data[_Idx];
const auto _Date =
static_cast<sys_days>(year_month_day{year{_Ls._Year}, month{_Ls._Month}, day{_Ls._Day}})
+ hours{_Ls._Hour + 1};
_Leap_sec_info.emplace_back(_Date, !_Ls._Negative, _Leap_sec_info.back()._Elapsed());
_All_ls_positive = _All_ls_positive && !_Ls._Negative;
}
}
return {_STD move(_Leap_sec_info), _All_ls_positive};
}
_NODISCARD inline string _Tzdb_update_version(const string_view _Version, const size_t _Num_leap_seconds) {
string _Icu_version{_Version.substr(0, _Version.find_last_of('.'))};
_Icu_version += '.';
_Icu_version += _STD to_string(_Num_leap_seconds);
return _Icu_version;
}
struct _Secret_tzdb_list_construct_tag {
explicit _Secret_tzdb_list_construct_tag() = default;
};
_EXPORT_STD class tzdb_list {
private:
using _ListType = forward_list<tzdb, _Crt_allocator<tzdb>>;
public:
using const_iterator = _ListType::const_iterator;
tzdb_list(const tzdb_list&) = delete;
tzdb_list& operator=(const tzdb_list&) = delete;
explicit tzdb_list(_Secret_tzdb_list_construct_tag) {
auto [_Icu_version, _Zones, _Links] = _Tzdb_generate_time_zones();
auto [_Leap_sec, _All_ls_positive] = _Tzdb_generate_leap_seconds(0);
auto _Version = _Icu_version + "." + _STD to_string(_Leap_sec.size());
// TRANSITION, NVCC (was DevCom-10732572), should call emplace_front with construction arguments
_Tzdb_list.emplace_front(tzdb{
_STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive});
}
_NODISCARD const tzdb& front() const noexcept {
_Shared_lock _Lk(_Tzdb_mutex);
return _Tzdb_list.front();
}
const_iterator erase_after(const_iterator _Where) noexcept /* strengthened */ {
_Unique_lock _Lk(_Tzdb_mutex);
return _Tzdb_list.erase_after(_Where);
}
_NODISCARD const_iterator begin() const noexcept {
_Shared_lock _Lk(_Tzdb_mutex);
return _Tzdb_list.begin();
}
_NODISCARD const_iterator end() const noexcept {
return _Tzdb_list.end(); // no lock necessary for forward_list::end()
}
_NODISCARD const_iterator cbegin() const noexcept {
_Shared_lock _Lk(_Tzdb_mutex);
return _Tzdb_list.cbegin();
}
_NODISCARD const_iterator cend() const noexcept {
return _Tzdb_list.cend(); // no lock necessary for forward_list::cend()
}
template <class... _ArgsTy>
void _Emplace_front(_ArgsTy&&... _Args) {
_Unique_lock _Lk(_Tzdb_mutex);
_Tzdb_list.emplace_front(_STD forward<_ArgsTy>(_Args)...);
}
const tzdb& _Reload() {
_Unique_lock _Lk(_Tzdb_mutex);
auto [_Leap_sec, _All_ls_positive] = _Tzdb_generate_leap_seconds(_Tzdb_list.front().leap_seconds.size());
if (!_Leap_sec.empty()) {
const auto& _Tzdb = _Tzdb_list.front();
vector<time_zone> _Zones;
_Zones.reserve(_Tzdb.zones.size());
for (const auto& _Tz : _Tzdb.zones) {
_Zones.emplace_back(_Secret_time_zone_construct_tag{}, _Tz.name());
}
vector<time_zone_link> _Links;
_Links.reserve(_Tzdb.links.size());
for (const auto& _Link : _Tzdb.links) {
_Links.emplace_back(_Secret_time_zone_link_construct_tag{}, _Link.name(), _Link.target());
}
auto _Version = _Tzdb_update_version(_Tzdb.version, _Leap_sec.size());
// TRANSITION, NVCC (was DevCom-10732572), should call emplace_front with construction arguments
_Tzdb_list.emplace_front(tzdb{
_STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive});
}
return _Tzdb_list.front();
}
private:
_ListType _Tzdb_list;
mutable _Smtx_t _Tzdb_mutex = {};
struct _NODISCARD _Shared_lock {
explicit _Shared_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} {
_Smtx_lock_shared(_Mtx);
}
_Shared_lock(const _Shared_lock&) = delete;
_Shared_lock& operator=(const _Shared_lock&) = delete;
~_Shared_lock() {
_Smtx_unlock_shared(_Mtx);
}
_Smtx_t* _Mtx;
};
struct _NODISCARD _Unique_lock {
explicit _Unique_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} {
_Smtx_lock_exclusive(_Mtx);
}
_Unique_lock(const _Unique_lock&) = delete;
_Unique_lock& operator=(const _Unique_lock&) = delete;
~_Unique_lock() {
_Smtx_unlock_exclusive(_Mtx);
}
_Smtx_t* _Mtx;
};
};
inline atomic<tzdb_list*> _Global_tzdb_list;
_EXPORT_STD _NODISCARD inline tzdb_list& get_tzdb_list() {
auto _Tzdb_ptr = _Global_tzdb_list.load();
if (_Tzdb_ptr != nullptr) {
return *_Tzdb_ptr;
}
auto _My_tzdb = static_cast<tzdb_list*>(__std_calloc_crt(1, sizeof(tzdb_list)));
if (_My_tzdb == nullptr) {
_Xruntime_error("bad allocation"); // not bad_alloc, see N4950 [time.zone.db.access]/4
}
_TRY_BEGIN
_STD construct_at(_My_tzdb, _Secret_tzdb_list_construct_tag{});
_CATCH(const runtime_error&)
__std_free_crt(_My_tzdb);
_RERAISE;
_CATCH(const exception& _Except)
#if _HAS_EXCEPTIONS
__std_free_crt(_My_tzdb);
_Xruntime_error(_Except.what());
#endif // _HAS_EXCEPTIONS
_CATCH_END
if (_Global_tzdb_list.compare_exchange_strong(_Tzdb_ptr, _My_tzdb)) {
_Tzdb_ptr = _My_tzdb;
} else {
_STD destroy_at(_My_tzdb);
__std_free_crt(_My_tzdb);
}
return *_Tzdb_ptr;
}
_EXPORT_STD _NODISCARD inline const tzdb& get_tzdb() {
return _CHRONO get_tzdb_list().front();
}
_EXPORT_STD _NODISCARD inline const time_zone* locate_zone(string_view _Tz_name) {
return _CHRONO get_tzdb().locate_zone(_Tz_name);
}
_EXPORT_STD _NODISCARD inline const time_zone* current_zone() {
return _CHRONO get_tzdb().current_zone();
}
_EXPORT_STD inline const tzdb& reload_tzdb() {
_TRY_BEGIN
return _CHRONO get_tzdb_list()._Reload();
_CATCH(const runtime_error&)
_RERAISE;
_CATCH(const exception& _Except)
#if _HAS_EXCEPTIONS
_Xruntime_error(_Except.what());
#endif // _HAS_EXCEPTIONS
_CATCH_END
}
_EXPORT_STD _NODISCARD inline string remote_version() {
const auto& _Tzdb = _CHRONO get_tzdb();
const auto& _Version = _Tzdb.version;
const auto [_Leap_sec, _Ignored] = _Tzdb_generate_leap_seconds(_Tzdb.leap_seconds.size());
return _Leap_sec.empty() ? _Version : _Tzdb_update_version(_Version, _Leap_sec.size());
}
// [time.zone.zonedtraits]
_EXPORT_STD template <class _Ty>
struct zoned_traits {};
template <>
struct zoned_traits<const time_zone*> {
_NODISCARD static const time_zone* default_zone() {
return _CHRONO get_tzdb().locate_zone("UTC");
}
_NODISCARD static const time_zone* locate_zone(string_view _Name) {
return _CHRONO get_tzdb().locate_zone(_Name);
}
};
// [time.zone.zonedtime]
_EXPORT_STD template <class _Duration, class _TimeZonePtr = const time_zone*>
class zoned_time {
private:
static_assert(_Is_duration_v<_Duration>,
"N4950 [time.zone.zonedtime.overview]/2 mandates Duration to be a specialization of chrono::duration.");
using _Traits = zoned_traits<_TimeZonePtr>;
public:
using duration = common_type_t<_Duration, seconds>;
template <class _Traits2 = _Traits, class = void_t<decltype(_Traits2::default_zone())>>
zoned_time() : _Zone{_Traits::default_zone()} {}
zoned_time(const zoned_time&) = default;
zoned_time& operator=(const zoned_time&) = default;
template <class _Traits2 = _Traits, class = void_t<decltype(_Traits2::default_zone())>>
zoned_time(const sys_time<_Duration>& _Sys) : _Zone{_Traits::default_zone()}, _Tp{_Sys} {}
explicit zoned_time(_TimeZonePtr _Tz) noexcept /* strengthened */ : _Zone{_STD move(_Tz)} {}
// clang-format off
template <class _Traits2 = _Traits, enable_if_t<is_constructible_v<
zoned_time, decltype(_Traits2::locate_zone(string_view{}))>,
int> = 0>
// clang-format on
explicit zoned_time(string_view _Name) : _Zone{_Traits::locate_zone(_Name)} {}
template <class _Duration2, enable_if_t<is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>, int> = 0>
zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& _Zt) noexcept /* strengthened */
: _Zone{_Zt.get_time_zone()}, _Tp{_Zt.get_sys_time()} {}
zoned_time(_TimeZonePtr _Tz, const sys_time<_Duration>& _Sys) : _Zone{_STD move(_Tz)}, _Tp{_Sys} {}
// clang-format off
template <class _Traits2 = _Traits, enable_if_t<is_constructible_v<
zoned_time, decltype(_Traits2::locate_zone(string_view{})), const sys_time<_Duration>&>,
int> = 0>
// clang-format on
zoned_time(string_view _Name, type_identity_t<const sys_time<_Duration>&> _Sys)
: zoned_time{_Traits::locate_zone(_Name), _Sys} {}
template <class _Ptr = _TimeZonePtr,
enable_if_t<
is_convertible_v<decltype(_STD declval<_Ptr&>()->to_sys(local_time<_Duration>{})), sys_time<duration>>,
int> = 0>
zoned_time(_TimeZonePtr _Tz, const local_time<_Duration>& _Local)
: _Zone{_STD move(_Tz)}, _Tp{_Zone->to_sys(_Local)} {}
// clang-format off
template <class _Traits2 = _Traits, enable_if_t<is_constructible_v<
zoned_time, decltype(_Traits2::locate_zone(string_view{})), const local_time<_Duration>&>,
int> = 0>
// clang-format on
zoned_time(string_view _Name, type_identity_t<const local_time<_Duration>>& _Local)
: zoned_time{_Traits::locate_zone(_Name), _Local} {}
template <class _Ptr = _TimeZonePtr,
enable_if_t<
is_convertible_v<decltype(_STD declval<_Ptr&>()->to_sys(local_time<_Duration>{}, choose::earliest)),
sys_time<duration>>,
int> = 0>
zoned_time(_TimeZonePtr _Tz, const local_time<_Duration>& _Local, choose _Choose)
: _Zone{_STD move(_Tz)}, _Tp{_Zone->to_sys(_Local, _Choose)} {}
// clang-format off
template <class _Traits2 = _Traits, enable_if_t<is_constructible_v<
zoned_time, decltype(_Traits2::locate_zone(string_view{})), const local_time<_Duration>&, choose>,
int> = 0>
// clang-format on
zoned_time(string_view _Name, type_identity_t<const local_time<_Duration>&> _Local, choose _Choose)
: zoned_time{_Traits::locate_zone(_Name), _Local, _Choose} {}
template <class _Duration2, class _TimeZonePtr2,
enable_if_t<is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>, int> = 0>
zoned_time(_TimeZonePtr _Tz, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt) noexcept /* strengthened */
: _Zone{_STD move(_Tz)}, _Tp{_Zt.get_sys_time()} {}
template <class _Duration2, class _TimeZonePtr2,
enable_if_t<is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>, int> = 0>
zoned_time(
_TimeZonePtr _Tz, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt, choose) noexcept /* strengthened */
: zoned_time{_Tz, _Zt} {}
// clang-format off
template <class _Duration2, class _TimeZonePtr2,
class _Traits2 = _Traits, enable_if_t<is_constructible_v<
zoned_time, decltype(_Traits2::locate_zone(string_view{})),
const zoned_time<_Duration2, _TimeZonePtr2>&>,
int> = 0>
// clang-format on
zoned_time(string_view _Name, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt)
: zoned_time{_Traits::locate_zone(_Name), _Zt} {}
// clang-format off
template <class _Duration2, class _TimeZonePtr2,
class _Traits2 = _Traits, enable_if_t<is_constructible_v<
zoned_time, decltype(_Traits2::locate_zone(string_view{})),
const zoned_time<_Duration2, _TimeZonePtr2>&, choose>,
int> = 0>
// clang-format on
zoned_time(string_view _Name, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt, choose _Choose)
: zoned_time{_Traits::locate_zone(_Name), _Zt, _Choose} {}
zoned_time& operator=(const sys_time<_Duration>& _Sys) noexcept /* strengthened */ {
_Tp = _Sys;
return *this;
}
zoned_time& operator=(const local_time<_Duration>& _Local) {
_Tp = _Zone->to_sys(_Local);
return *this;
}
operator sys_time<duration>() const noexcept /* strengthened */ {
return get_sys_time();
}
explicit operator local_time<duration>() const {
return get_local_time();
}
_NODISCARD _TimeZonePtr get_time_zone() const noexcept /* strengthened */ {
return _Zone;
}
_NODISCARD local_time<duration> get_local_time() const {
return _Zone->to_local(_Tp);
}
_NODISCARD sys_time<duration> get_sys_time() const noexcept /* strengthened */ {
return _Tp;
}
_NODISCARD sys_info get_info() const {
return _Zone->get_info(_Tp);
}
private:
_TimeZonePtr _Zone;
sys_time<duration> _Tp{};
};
zoned_time() -> zoned_time<seconds>;
template <class _Duration>
zoned_time(sys_time<_Duration>) -> zoned_time<common_type_t<_Duration, seconds>>;
template <class _TimeZonePtrOrName>
using _Time_zone_representation = conditional_t<is_convertible_v<_TimeZonePtrOrName, string_view>, const time_zone*,
remove_cvref_t<_TimeZonePtrOrName>>;
template <class _TimeZonePtrOrName>
zoned_time(_TimeZonePtrOrName&&) -> zoned_time<seconds, _Time_zone_representation<_TimeZonePtrOrName>>;
template <class _TimeZonePtrOrName, class _Duration>
zoned_time(_TimeZonePtrOrName&&, sys_time<_Duration>)
-> zoned_time<common_type_t<_Duration, seconds>, _Time_zone_representation<_TimeZonePtrOrName>>;
template <class _TimeZonePtrOrName, class _Duration>
zoned_time(_TimeZonePtrOrName&&, local_time<_Duration>, choose = choose::earliest)
-> zoned_time<common_type_t<_Duration, seconds>, _Time_zone_representation<_TimeZonePtrOrName>>;
template <class _Duration, class _TimeZonePtrOrName, class _TimeZonePtr2>
zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, _TimeZonePtr2>, choose = choose::earliest)
-> zoned_time<common_type_t<_Duration, seconds>, _Time_zone_representation<_TimeZonePtrOrName>>;
_EXPORT_STD using zoned_seconds = zoned_time<seconds>;
_EXPORT_STD template <class _Duration1, class _Duration2, class _TimeZonePtr>
_NODISCARD bool operator==(
const zoned_time<_Duration1, _TimeZonePtr>& _Left, const zoned_time<_Duration2, _TimeZonePtr>& _Right) {
return _Left.get_time_zone() == _Right.get_time_zone() && _Left.get_sys_time() == _Right.get_sys_time();
}
// [time.clock.utc]
_EXPORT_STD class utc_clock;
_EXPORT_STD template <class _Duration>
using utc_time = time_point<utc_clock, _Duration>;
_EXPORT_STD using utc_seconds = utc_time<seconds>;
_EXPORT_STD struct leap_second_info {
bool is_leap_second;
seconds elapsed;
};
_EXPORT_STD template <class _Duration>
_NODISCARD leap_second_info get_leap_second_info(const utc_time<_Duration>& _Time) {
const utc_seconds _Time_floor = _CHRONO floor<seconds>(_Time);
const auto& _Tzdb = _CHRONO get_tzdb();
const auto& _Ls_vector = _Tzdb.leap_seconds;
// Find first leap second after _Time.
vector<leap_second>::const_iterator _It;
if (_Tzdb._All_ls_positive) {
// Where "target_ls" is the next leap second at or after _Time, _It either points to:
// (1) The 2nd leap second after _Time if _Time_floor is in the range [target_ls - _Elapsed() - 1,
// target_ls), or
// (2) The leap second just after _Time otherwise.
// Note that we can always use prev(_It) to determine whether _Time is *during* a leap second insertion,
// since that falls under case (2) above. However, when we fall under case (1), we need to execute an
// additional decrement to get the correct elapsed offset. For example, if leap seconds are inserted at
// seconds {100, 200, 300, 400}, we have:
//
// UTC sys *_It
// 99 99 100
// 100 X 200
// 101 100 200
// 102 101 200
// ...
// 199 198 200
// 200 199 300^
// 201 X 300
// 202 200 300
// ...
// 299 297 300
// 300 298 400^
// 301 299 400^
// 302 X 400
// 303 300 400
//
// ^_It points to 2nd leap second
_It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), sys_seconds{_Time_floor.time_since_epoch()});
} else {
seconds _Prev_elapsed{0};
for (_It = _Ls_vector.begin(); _It != _Ls_vector.end(); ++_It) {
// UTC time when leap second insertion begins. In all cases, _It->date() + _It->_Elapsed() is the *end*
// of the insertion. For a negative leap that's also the beginning, but for a positive one, insertion
// begins 1 second earlier.
const utc_seconds _This_ls_begin{
_It->date().time_since_epoch() + (_It->_Positive() ? _Prev_elapsed : _It->_Elapsed())};
if (_This_ls_begin > _Time_floor) {
break;
}
_Prev_elapsed = _It->_Elapsed();
}
}
if (_It == _Ls_vector.begin()) {
return {false, seconds{0}};
} else {
// Convert to the last leap second before or equal to _Time.
const auto& _Last_leap = *--_It;
const utc_seconds _Utc_leap_second{_Last_leap.date().time_since_epoch() + _It->_Elapsed() - seconds{1}};
const auto _Leap_cmp = _Utc_leap_second <=> _Time_floor;
if (_Tzdb._All_ls_positive && _STD is_gt(_Leap_cmp)) { // Case (1)
--_It;
}
return {_Last_leap._Positive() && _STD is_eq(_Leap_cmp), _It->_Elapsed()};
}
}
_EXPORT_STD class utc_clock {
public:
using rep = system_clock::rep;
using period = system_clock::period;
using duration = _CHRONO duration<rep, period>;
using time_point = _CHRONO time_point<utc_clock>;
static constexpr bool is_steady = system_clock::is_steady;
_NODISCARD static time_point now() {
return from_sys(system_clock::now());
}
template <class _Duration>
_NODISCARD static sys_time<common_type_t<_Duration, seconds>> 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<seconds>(_Utc_time.time_since_epoch()) - _Lsi.elapsed;
if constexpr (is_integral_v<typename _Duration::rep>) {
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})};
}
} else {
_Ticks = _Utc_time.time_since_epoch() - _Lsi.elapsed;
}
return sys_time<_CommonType>{_Ticks};
}
template <class _Duration>
_NODISCARD static utc_time<common_type_t<_Duration, seconds>> from_sys(const sys_time<_Duration>& _Sys_time) {
const auto& _Tzdb = _CHRONO get_tzdb();
const auto& _Ls_vector = _Tzdb.leap_seconds;
auto _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), _CHRONO floor<seconds>(_Sys_time));
const auto _Offset = _It == _Ls_vector.begin() ? seconds{0} : (--_It)->_Elapsed();
return utc_time<common_type_t<_Duration, seconds>>{_Sys_time.time_since_epoch() + _Offset};
}
};
// [time.clock.tai]
_EXPORT_STD class tai_clock;
_EXPORT_STD template <class _Duration>
using tai_time = time_point<tai_clock, _Duration>;
_EXPORT_STD using tai_seconds = tai_time<seconds>;
_EXPORT_STD class tai_clock {
public:
using rep = system_clock::rep;
using period = system_clock::period;
using duration = _CHRONO duration<rep, period>;
using time_point = _CHRONO time_point<tai_clock>;
static constexpr bool is_steady = system_clock::is_steady;
static constexpr seconds _Tai_epoch_adjust{378691210};
_NODISCARD static time_point now() {
return from_utc(utc_clock::now());
}
template <class _Duration>
_NODISCARD static utc_time<common_type_t<_Duration, seconds>> to_utc(
const tai_time<_Duration>& _Time) noexcept {
return utc_time<common_type_t<_Duration, seconds>>{_Time.time_since_epoch()} - _Tai_epoch_adjust;
}
template <class _Duration>
_NODISCARD static tai_time<common_type_t<_Duration, seconds>> from_utc(
const utc_time<_Duration>& _Time) noexcept {
return tai_time<common_type_t<_Duration, seconds>>{_Time.time_since_epoch()} + _Tai_epoch_adjust;
}
};
// [time.clock.gps]
_EXPORT_STD class gps_clock;
_EXPORT_STD template <class _Duration>
using gps_time = time_point<gps_clock, _Duration>;
_EXPORT_STD using gps_seconds = gps_time<seconds>;
_EXPORT_STD class gps_clock {
public:
using rep = system_clock::rep;
using period = system_clock::period;
using duration = _CHRONO duration<rep, period>;
using time_point = _CHRONO time_point<gps_clock>;
static constexpr bool is_steady = system_clock::is_steady;
static constexpr seconds _Gps_epoch_adjust{-315964809};
_NODISCARD static time_point now() {
return from_utc(utc_clock::now());
}
template <class _Duration>
_NODISCARD static utc_time<common_type_t<_Duration, seconds>> to_utc(
const gps_time<_Duration>& _Time) noexcept {
return utc_time<common_type_t<_Duration, seconds>>{_Time.time_since_epoch()} - _Gps_epoch_adjust;
}
template <class _Duration>
_NODISCARD static gps_time<common_type_t<_Duration, seconds>> from_utc(
const utc_time<_Duration>& _Time) noexcept {
return gps_time<common_type_t<_Duration, seconds>>{_Time.time_since_epoch()} + _Gps_epoch_adjust;
}
};
} // namespace chrono
namespace filesystem {
struct _File_time_clock;
} // namespace filesystem
namespace chrono {
_EXPORT_STD using file_clock = filesystem::_File_time_clock;
_EXPORT_STD template <class _Duration>
using file_time = time_point<file_clock, _Duration>;
} // namespace chrono
#endif // ^^^ _HAS_CXX20 ^^^
#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;
using duration = chrono::duration<rep, period>;
using time_point = chrono::time_point<_File_time_clock>;
static constexpr bool is_steady = false;
_NODISCARD static time_point now() noexcept { // get current time; undo epoch adjustment
return time_point(duration(_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment)); // TRANSITION, ABI
}
#if _HAS_CXX20
// Assumes that FILETIME counts leap seconds since 2018-06-01 (i.e., after the first 27 leap seconds), even
// though systems can opt out of this behavior.
static constexpr chrono::seconds _Skipped_filetime_leap_seconds{27};
static constexpr chrono::sys_days _Cutoff{
chrono::year_month_day{chrono::year{2018}, chrono::June, chrono::day{1}}};
template <class _Duration>
_NODISCARD static chrono::utc_time<common_type_t<_Duration, chrono::seconds>> 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<seconds>(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};
}
}
template <class _Duration>
_NODISCARD static chrono::file_time<common_type_t<_Duration, chrono::seconds>> from_utc(
const chrono::utc_time<_Duration>& _Utc_time) {
using namespace chrono;
file_time<common_type_t<_Duration, seconds>> _File_time{
_CHRONO duration_cast<seconds>(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;
}
#endif // ^^^ _HAS_CXX20 ^^^
};
} // namespace filesystem
#endif // ^^^ _HAS_CXX17 ^^^
#if _HAS_CXX20
namespace chrono {
// [time.clock.conv]
_EXPORT_STD template <class _DestClock, class _SourceClock>
struct clock_time_conversion {};
// [time.clock.cast.id]
template <class _Clock>
struct clock_time_conversion<_Clock, _Clock> {
template <class _Duration>
_NODISCARD time_point<_Clock, _Duration> operator()(const time_point<_Clock, _Duration>& _Time) const
noexcept(is_arithmetic_v<typename _Duration::rep>) /* strengthened */ {
return _Time;
}
};
template <>
struct clock_time_conversion<system_clock, system_clock> {
template <class _Duration>
_NODISCARD sys_time<_Duration> operator()(const sys_time<_Duration>& _Time) const
noexcept(is_arithmetic_v<typename _Duration::rep>) /* strengthened */ {
return _Time;
}
};
template <>
struct clock_time_conversion<utc_clock, utc_clock> {
template <class _Duration>
_NODISCARD utc_time<_Duration> operator()(const utc_time<_Duration>& _Time) const
noexcept(is_arithmetic_v<typename _Duration::rep>) /* strengthened */ {
return _Time;
}
};
// [time.clock.cast.sys.utc]
template <>
struct clock_time_conversion<utc_clock, system_clock> {
template <class _Duration>
_NODISCARD utc_time<common_type_t<_Duration, seconds>> operator()(const sys_time<_Duration>& _Sys_time) const {
return utc_clock::from_sys(_Sys_time);
}
};
template <>
struct clock_time_conversion<system_clock, utc_clock> {
template <class _Duration>
_NODISCARD sys_time<common_type_t<_Duration, seconds>> operator()(const utc_time<_Duration>& _Utc_time) const {
return utc_clock::to_sys(_Utc_time);
}
};
// [time.clock.cast.sys]
template <class _TimePoint, class _Clock>
constexpr bool _Is_time_point_for_clock = false;
template <class _Clock, class _Duration>
constexpr bool _Is_time_point_for_clock<time_point<_Clock, _Duration>, _Clock> = true;
template <class _SourceClock>
struct clock_time_conversion<system_clock, _SourceClock> {
template <class _Duration>
requires requires(const time_point<_SourceClock, _Duration>& _Time) { _SourceClock::to_sys(_Time); }
_NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const
noexcept(noexcept(_SourceClock::to_sys(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_SourceClock::to_sys(_Time)), system_clock>,
"N4950 [time.clock.cast.sys]/2: Mandates: SourceClock::to_sys(t) "
"returns a sys_time<Duration2> for some type Duration2");
return _SourceClock::to_sys(_Time);
}
};
template <class _DestClock>
struct clock_time_conversion<_DestClock, system_clock> {
template <class _Duration>
requires requires(const sys_time<_Duration>& _Time) { _DestClock::from_sys(_Time); }
_NODISCARD auto operator()(const sys_time<_Duration>& _Time) const
noexcept(noexcept(_DestClock::from_sys(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_DestClock::from_sys(_Time)), _DestClock>,
"N4950 [time.clock.cast.sys]/5: Mandates: DestClock::from_sys(t) "
"returns a time_point<DestClock, Duration2> for some type Duration2");
return _DestClock::from_sys(_Time);
}
};
// [time.clock.cast.utc]
template <class _SourceClock>
struct clock_time_conversion<utc_clock, _SourceClock> {
template <class _Duration>
requires requires(const time_point<_SourceClock, _Duration>& _Time) { _SourceClock::to_utc(_Time); }
_NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const
noexcept(noexcept(_SourceClock::to_utc(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_SourceClock::to_utc(_Time)), utc_clock>,
"N4950 [time.clock.cast.utc]/2: Mandates: SourceClock::to_utc(t) "
"returns a utc_time<Duration2> for some type Duration2");
return _SourceClock::to_utc(_Time);
}
};
template <class _DestClock>
struct clock_time_conversion<_DestClock, utc_clock> {
template <class _Duration>
requires requires(const utc_time<_Duration>& _Time) { _DestClock::from_utc(_Time); }
_NODISCARD auto operator()(const utc_time<_Duration>& _Time) const
noexcept(noexcept(_DestClock::from_utc(_Time))) /* strengthened */ {
static_assert(_Is_time_point_for_clock<decltype(_DestClock::from_utc(_Time)), _DestClock>,
"N4950 [time.clock.cast.utc]/5: Mandates: DestClock::from_utc(t) "
"returns a time_point<DestClock, Duration2> for some type Duration2");
return _DestClock::from_utc(_Time);
}
};
// [time.clock.cast.fn]
enum class _Clock_cast_strategy {
_Direct,
_Via_sys,
_Via_utc,
_Via_utc_from_sys,
_Via_sys_from_utc,
_Two_step_ambiguous,
_Three_step_ambiguous,
_None,
};
template <class _Conv1, class _Conv2, class _Tp>
constexpr bool _Has_two_step_conversion = requires { _Conv1{}(_Conv2{}(_STD declval<_Tp>())); };
template <class _Conv1, class _Conv2, class _Conv3, class _Tp>
constexpr bool _Has_three_step_conversion = requires { _Conv1{}(_Conv2{}(_Conv3{}(_STD declval<_Tp>()))); };
template <class _DestClock, class _SourceClock, class _Duration>
_NODISCARD consteval _Clock_cast_strategy _Choose_clock_cast() noexcept {
using _Tp = const time_point<_SourceClock, _Duration>&;
if constexpr (is_invocable_v<clock_time_conversion<_DestClock, _SourceClock>, _Tp>) {
return _Clock_cast_strategy::_Direct;
} else {
constexpr bool _Has_sys = _Has_two_step_conversion<clock_time_conversion<_DestClock, system_clock>,
clock_time_conversion<system_clock, _SourceClock>, _Tp>;
constexpr bool _Has_utc = _Has_two_step_conversion<clock_time_conversion<_DestClock, utc_clock>,
clock_time_conversion<utc_clock, _SourceClock>, _Tp>;
if constexpr (_Has_sys && _Has_utc) {
return _Clock_cast_strategy::_Two_step_ambiguous;
} else if constexpr (_Has_sys) {
return _Clock_cast_strategy::_Via_sys;
} else if constexpr (_Has_utc) {
return _Clock_cast_strategy::_Via_utc;
} else {
constexpr bool _Has_utc_from_sys = _Has_three_step_conversion< //
clock_time_conversion<_DestClock, utc_clock>, //
clock_time_conversion<utc_clock, system_clock>, //
clock_time_conversion<system_clock, _SourceClock>, _Tp>;
constexpr bool _Has_sys_from_utc = _Has_three_step_conversion< //
clock_time_conversion<_DestClock, system_clock>, //
clock_time_conversion<system_clock, utc_clock>, //
clock_time_conversion<utc_clock, _SourceClock>, _Tp>;
if constexpr (_Has_utc_from_sys && _Has_sys_from_utc) {
return _Clock_cast_strategy::_Three_step_ambiguous;
} else if constexpr (_Has_utc_from_sys) {
return _Clock_cast_strategy::_Via_utc_from_sys;
} else if constexpr (_Has_sys_from_utc) {
return _Clock_cast_strategy::_Via_sys_from_utc;
} else {
return _Clock_cast_strategy::_None;
}
}
}
}
template <class _DestClock, class _SourceClock, class _Duration>
constexpr auto _Clock_cast_choice = _Choose_clock_cast<_DestClock, _SourceClock, _Duration>();
_EXPORT_STD template <class _DestClock, class _SourceClock, class _Duration>
requires (_Clock_cast_choice<_DestClock, _SourceClock, _Duration> != _Clock_cast_strategy::_None)
_NODISCARD auto clock_cast(const time_point<_SourceClock, _Duration>& _Time) {
constexpr auto _Strat = _Clock_cast_choice<_DestClock, _SourceClock, _Duration>;
if constexpr (_Strat == _Clock_cast_strategy::_Direct) {
return clock_time_conversion<_DestClock, _SourceClock>{}(_Time);
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys) {
return clock_time_conversion<_DestClock, system_clock>{}(
clock_time_conversion<system_clock, _SourceClock>{}(_Time));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc) {
return clock_time_conversion<_DestClock, utc_clock>{}(
clock_time_conversion<utc_clock, _SourceClock>{}(_Time));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc_from_sys) {
return clock_time_conversion<_DestClock, utc_clock>{}( //
clock_time_conversion<utc_clock, system_clock>{}(
clock_time_conversion<system_clock, _SourceClock>{}(_Time)));
} else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys_from_utc) {
return clock_time_conversion<_DestClock, system_clock>{}( //
clock_time_conversion<system_clock, utc_clock>{}(
clock_time_conversion<utc_clock, _SourceClock>{}(_Time)));
} else if constexpr (_Strat == _Clock_cast_strategy::_Two_step_ambiguous) {
static_assert(false,
"A two-step clock time conversion is required to be unique, "
"either through utc_clock or system_clock, but not both (N4950 [time.clock.cast.fn]/2).");
} else if constexpr (_Strat == _Clock_cast_strategy::_Three_step_ambiguous) {
static_assert(false, "A three-step clock time conversion is required to be unique, "
"either utc-to-system or system-to-utc, but not both (N4950 [time.clock.cast.fn]/2).");
} else {
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected strategy
}
}
// [time.parse]
struct _Time_parse_fields {
using _SubsecondType = duration<int64_t, atto>;
// These are the primary fields, used to set the chrono type being parsed.
optional<int64_t> _Subsecond;
optional<int> _Second;
optional<int> _Minute;
optional<int> _Hour_24;
optional<int> _Weekday; // 0-based, starts Sunday
optional<int> _Day; // 1-based
optional<int> _Month; // 1-based
optional<int> _Day_of_year; // 1-based
optional<int> _Two_dig_year;
optional<int> _Century;
optional<int> _Utc_offset; // in minutes
optional<string> _Tz_name;
// These are the secondary fields, used to store parsed data. They must be converted to primary fields and
// checked for consistency.
optional<int> _Hour_12;
optional<int> _Ampm;
optional<int> _Iso_year;
optional<int> _Two_dig_iso_year;
optional<int> _Iso_week; // 1-based
optional<int> _Week_u; // week number, W01 begins on first Sunday
optional<int> _Week_w; // week number, W01 begins on first Monday
enum _FieldFlags : unsigned int {
_F_sec = 0x01,
_F_min = 0x02,
_F_hr = 0x04,
_F_day = 0x08,
_F_wkday = 0x10,
_F_mon = 0x20,
_F_doy = 0x40,
_F_year = 0x80,
};
_NODISCARD unsigned int _Used_fields() const {
unsigned int _Ret{0};
if (_Second || _Subsecond) {
_Ret |= _F_sec;
}
if (_Minute) {
_Ret |= _F_min;
}
if (_Hour_24) {
_Ret |= _F_hr;
}
if (_Day) {
_Ret |= _F_day;
}
if (_Weekday) {
_Ret |= _F_wkday;
}
if (_Month) {
_Ret |= _F_mon;
}
if (_Day_of_year) {
_Ret |= _F_doy;
}
if (_Two_dig_year && _Century) {
_Ret |= _F_year;
}
return _Ret;
}
_NODISCARD static bool _Test_bits(
const unsigned int _Bits, const unsigned int _Must_set, const unsigned int _Optional = 0) {
return (_Bits & ~_Optional) == _Must_set;
}
template <class _Ty>
static constexpr _Ty _Invalid_time_field{numeric_limits<_Ty>::lowest()};
static void _Initialize_time_point(tm& _Tp) {
_Tp.tm_sec = _Invalid_time_field<int>;
_Tp.tm_min = _Invalid_time_field<int>;
_Tp.tm_hour = _Invalid_time_field<int>;
_Tp.tm_wday = _Invalid_time_field<int>;
_Tp.tm_mday = _Invalid_time_field<int>;
_Tp.tm_mon = _Invalid_time_field<int>;
_Tp.tm_yday = _Invalid_time_field<int>;
_Tp.tm_year = _Invalid_time_field<int>;
}
template <class _Ty>
_NODISCARD static bool _Update(optional<_Ty>& _Val, const _Ty& _New) {
// Update a field. Ignores invalid values. If _Val already has a value, returns true or false according to
// whether the new value matches the current one or not, so that inconsistencies can be detected.
if constexpr (!is_same_v<_Ty, string>) {
if (_New == _Invalid_time_field<_Ty>) {
return true;
}
}
if (_Val.has_value()) {
return _STD exchange(*_Val, _New) == _New;
}
_Val = _New;
return true;
}
_NODISCARD static pair<int, int> _Decompose_year(const int _Year) {
int _Two_d_year = _Year % 100;
if (_Two_d_year < 0) {
_Two_d_year += 100;
}
return {_Year - _Two_d_year, _Two_d_year};
}
_NODISCARD bool _Update_if_valid(const tm& _Tp, const bool _Full_year) {
bool _No_err{true};
if (_Tp.tm_hour != _Invalid_time_field<int>) {
_No_err = _No_err && _Update(_Hour_24, _Tp.tm_hour);
_No_err = _No_err && _Update(_Ampm, _Hour_24 >= 12 ? 1 : 0);
_No_err = _No_err && _Update(_Hour_12, _CHRONO make12(hours{*_Hour_24}).count());
}
_No_err = _No_err && _Update(_Minute, _Tp.tm_min);
_No_err = _No_err && _Update(_Second, _Tp.tm_sec);
_No_err = _No_err && _Update(_Day, _Tp.tm_mday);
_No_err = _No_err && _Update(_Weekday, _Tp.tm_wday);
if (_Tp.tm_mon != _Invalid_time_field<int>) {
_No_err = _No_err && _Update(_Month, _Tp.tm_mon + 1);
}
if (_Tp.tm_yday != _Invalid_time_field<int>) {
_No_err = _No_err && _Update(_Day_of_year, _Tp.tm_yday + 1);
}
if (_Tp.tm_year != _Invalid_time_field<int>) {
// Sometimes we expect only the last two digits.
const auto _Year_parts = _Decompose_year(_Tp.tm_year + 1900);
_No_err = _No_err && _Update(_Two_dig_year, _Year_parts.second);
if (_Full_year) {
_No_err = _No_err && _Update(_Century, _Year_parts.first);
}
}
return _No_err;
}
_NODISCARD bool _Yday_to_month_day(const int _Yday, const int _Year) {
// A day-of-year that's February 28 or earlier, by itself, is a valid month_day. Any later day is
// ambiguous without a year.
if (_Out_of_range(_Day_of_year, 1, _Two_dig_year || _Iso_year ? _Days_in_year(_Year) : 59)) {
return false;
}
if (_Day_of_year <= 31) {
return _Update(_Day, _Yday) && _Update(_Month, 1);
}
const int _Feb_end{_Is_leap(_Year) ? 60 : 59};
if (_Day_of_year <= _Feb_end) {
return _Update(_Day, _Yday - 31) && _Update(_Month, 2);
}
// Shift day-of-year so that 1 == March 1. This is the same as year_month_day::_Civil_from_days, except
// _Day_of_year --> _Shifted_yday-1 and _Mp --> _Month - 3.
const int _Shifted_yday{*_Day_of_year - _Feb_end};
return _Update(_Month, (535 * _Shifted_yday + 48950) >> 14)
&& _Update(_Day, _Shifted_yday - ((979 * *_Month - 2918) >> 5));
}
static constexpr int _Era_begin_wday{3}; // Wednesday
_NODISCARD static constexpr int _Jan1_weekday(int _Year) {
--_Year;
const int _Era = (_Year >= 0 ? _Year : _Year - 399) / 400;
const int _Yoe = _Year - _Era * 400;
// Jan. 1 is always day 306 of the shifted [Mar, ..., Dec, Jan, Feb] year.
const int _Doe = ((1461 * _Yoe) >> 2) - _Yoe / 100 + 306;
return (_Doe + _Era_begin_wday) % 7;
}
_NODISCARD static constexpr int _Iso8601_weeks(int _Year) {
const int _P_y = (_Year + _Year / 4 - _Year / 100 + _Year / 400) % 7;
--_Year;
const int _P_ym1 = (_Year + _Year / 4 - _Year / 100 + _Year / 400) % 7;
return 52 + (_P_y == 4 || _P_ym1 == 3);
}
_NODISCARD static constexpr int _Iso8601_week(const int _Day_of_year, const int _Weekday, const int _Year) {
// Jan. 4 is always week 1; rollover to next week always happens on Monday.
const auto _Week{(_Day_of_year + 9 - _Prev_weekday(_Weekday, 1)) / 7};
if (_Week < 1) {
return _Iso8601_weeks(_Year - 1);
} else if (_Week > _Iso8601_weeks(_Year)) {
return 1;
} else {
return _Week;
}
}
_NODISCARD static constexpr bool _Is_leap(const int _Year) {
return _Year % 4 == 0 && (_Year % 100 != 0 || _Year % 400 == 0);
}
_NODISCARD static constexpr int _Days_in_year(const int _Year) {
return _Is_leap(_Year) ? 366 : 365;
}
_NODISCARD static constexpr int _Next_weekday(const int _Wday, const int _Shift) {
// 0 <= _Shift <= 6
int _Result = _Wday + _Shift;
if (_Result >= 7) {
_Result -= 7;
}
return _Result;
}
_NODISCARD static constexpr int _Prev_weekday(const int _Wday, const int _Shift) {
// 0 <= _Shift <= 6
return (_Wday >= _Shift ? 0 : 7) + (_Wday - _Shift);
}
_NODISCARD bool _Calculate_ymd_from_week_date(const int _Starting_wday, const int _Week, const int _Year) {
// (a) Calculate day-of-year of first _Starting_wday in January.
// (b) Shift *_Weekday so that it's relative to _Starting_wday.
// (c) Offset to desired week.
const int _Jan1_wday = _Jan1_weekday(_Year);
const int _Yday = 1 + _Prev_weekday(_Starting_wday, _Jan1_wday) // (a)
+ _Prev_weekday(*_Weekday, _Starting_wday) // (b)
+ 7 * (_Week - 1); // (c)
return _Update(_Day_of_year, _Yday) && !_Out_of_range(_Day_of_year, 1, _Days_in_year(_Year))
&& _Yday_to_month_day(*_Day_of_year, _Year);
}
_NODISCARD bool _Calculate_ymd() {
bool _No_err = true;
// Flags to indicate if a field should be checked for consistency with other data. Set to false when the
// field is used to calculate the date, as it's necessarily self-consistent in that case (barring a bug).
bool _Check_u = true;
bool _Check_w = true;
bool _Check_iso = true;
bool _Check_wday = true;
if (_Day_of_year && _Out_of_range(_Day_of_year, 1, 366)) {
return false;
}
bool _Have_year = false;
int _Year{0};
if (_Two_dig_year) {
_Year = *_Century + *_Two_dig_year;
_Have_year = true;
if (_Day_of_year) {
_No_err = _No_err && _Yday_to_month_day(*_Day_of_year, _Year);
} else if (_Week_u || _Week_w) {
_Check_wday = false;
if (_Week_u) {
_Check_u = false;
_No_err = _No_err && _Calculate_ymd_from_week_date(0 /*Sunday*/, *_Week_u, _Year);
}
if (_Week_w) {
_Check_w = false;
_No_err = _No_err && _Calculate_ymd_from_week_date(1 /*Monday*/, *_Week_w, _Year);
}
}
}
if (_Iso_year) {
// ISO weeks begin on Monday. W01 always contains January 4. There is no W00, so the beginning of
// January can be in W52 or W53 of the previous year. Likewise, the end of December can occur at the
// beginning of W01 of the following year, depending on where in the week Jan. 4 falls.
_Check_wday = false;
_Check_iso = false;
_Year = *_Iso_year;
_Have_year = true;
// Shift weekdays to Monday-based. Jan. 4 is the anchor of week 1, so calculate where the parsed weekday
// is relative to that point.
const int _Jan4_wday = _Next_weekday(_Jan1_weekday(_Year), 3 - 1);
const int _Offset_from_jan4 = _Prev_weekday(*_Weekday, 1) - _Jan4_wday;
int _Trial_yday = 4 + 7 * (*_Iso_week - 1) + _Offset_from_jan4;
const int _Ref_num_days = _Trial_yday < 1 ? _Days_in_year(_Year - 1) : _Days_in_year(_Year);
if (_Trial_yday < 1) {
_Trial_yday += _Ref_num_days;
--_Year;
} else if (_Trial_yday > _Ref_num_days) {
_Trial_yday -= _Ref_num_days;
++_Year;
}
const auto _Year_parts = _Decompose_year(_Year);
_No_err = _No_err && _Update(_Day_of_year, _Trial_yday) && _Yday_to_month_day(*_Day_of_year, _Year)
&& _Update(_Century, _Year_parts.first) && _Update(_Two_dig_year, _Year_parts.second);
}
// Must have YMD by this point, either parsed directly or calculated above.
if (!_Have_year || !_Month || !_Day || !_No_err) {
return false;
}
// consistency checks
if (_Check_wday && _Weekday) {
const auto _Era_year = _Year - (*_Month <= 2);
const int _Era = (_Era_year >= 0 ? _Era_year : _Era_year - 399) / 400;
const int _Yoe = _Era_year - _Era * 400;
const int _Yday_era = ((979 * (*_Month + (*_Month > 2 ? -3 : 9)) + 19) >> 5) + *_Day - 1;
const int _Doe = ((1461 * _Yoe) >> 2) - _Yoe / 100 + _Yday_era;
_No_err = _No_err && _Update(_Weekday, (_Doe + _Era_begin_wday) % 7);
}
if (_Check_u && _Week_u) {
_No_err = _No_err && _Update(_Week_u, (*_Day_of_year + 6 - *_Weekday) / 7);
}
if (_Check_w && _Week_w) {
_No_err = _No_err && _Update(_Week_w, (*_Day_of_year + 6 - _Prev_weekday(*_Weekday, 1)) / 7);
}
if (_Check_iso && _Iso_week) {
_No_err = _No_err && _Update(_Iso_week, _Iso8601_week(*_Day_of_year, *_Weekday, _Year));
}
return _No_err;
}
_NODISCARD bool _Calculate_hour24() {
if (_Hour_12) {
return _Update(
_Hour_24, _CHRONO make24(hours{*_Hour_12}, static_cast<bool>(_Ampm.value_or(0))).count());
} else {
return true;
}
}
_NODISCARD bool _Calculate_year_fields() {
bool _No_err = true;
// The order of these updates is significant. Updating the ISO date second allows
// formats with %g and %y, but not %C, to get the century implicitly from %y.
if (_Two_dig_year && !_Century) {
_No_err = _No_err && _Update(_Century, _Two_dig_year >= 69 ? 1900 : 2000);
}
// %C is only combined with %g if %G is missing, to avoid an unnecessary parse failure when the ISO and
// Gregorian years are in different centuries.
if (_Two_dig_iso_year && _Century && !_Iso_year) {
_No_err = _No_err && _Update(_Iso_year, *_Century + *_Two_dig_iso_year);
}
return _No_err;
}
_NODISCARD static bool _Out_of_range(const optional<int>& _Field, const int _Min, const int _Max) {
return _Field && (_Field < _Min || _Max < _Field);
}
_NODISCARD bool _Is_complete() const {
// Check for data that is incomplete, ambiguous, or obviously out-of-range. The exception is 12-hour time
// without am/pm. Most strptime implementations will assume am in this case, so we'll do that too. Don't
// check for consistency yet, because the data might not even be representable by the type being parsed,
// and calendar computations are relatively expensive.
// Most time-of-day fields are deferred until we know if we're parsing a time_point.
if (_Out_of_range(_Hour_12, 1, 12) //
|| _Out_of_range(_Weekday, 0, 6) //
|| _Out_of_range(_Day, 1, 31) //
|| _Out_of_range(_Month, 1, 12)) {
return false;
}
if (_Iso_year || _Two_dig_iso_year || _Iso_week) {
// Need to have %G or %C+%g. The century can be parsed explicitly, or derived implicitly from %y.
const bool _Has_complete_year{_Iso_year || ((_Century || _Two_dig_year) && _Two_dig_iso_year)};
if (!_Has_complete_year || !_Iso_week || !_Weekday || _Out_of_range(_Iso_week, 1, 53)) {
return false;
}
}
if (_Week_u || _Week_w) {
// Need a weekday and year to be complete.
if (!_Weekday || !_Two_dig_year) {
return false;
}
if (_Out_of_range(_Week_u, 0, 53) || _Out_of_range(_Week_w, 0, 53)) {
return false;
}
}
return true;
}
template <class _Duration1, class _Duration2>
_NODISCARD static constexpr bool _Can_represent() {
using _Rep1 = _Duration1::rep;
using _Period1 = _Duration1::period;
using _Period2 = _Duration2::period;
// Returns whether _Duration1 can represent _Duration2{1}. Assumes 1 <= _Period2 <=
// 86,400, i.e., we're interested in time periods between seconds and days.
if constexpr (is_integral_v<_Rep1>) {
// Must have _Period1 <= _Period2 and numeric_limits<_Rep1>::max() >= _Period2 / _Period1. For example,
// std::days can't represent std::seconds, and duration<int, atto>::max() is ~9.2 seconds.
constexpr auto _Max_tick = static_cast<intmax_t>((numeric_limits<make_signed_t<_Rep1>>::max)());
return ratio_less_equal_v<_Period1, _Period2>
&& ratio_greater_equal_v<ratio<_Max_tick, _Period2::num>, ratio_divide<ratio<1>, _Period1>>;
} else if (is_floating_point_v<_Rep1>) {
// With the smallest possible _Period1, ratio<1,INTMAX_MAX>, one day has a tick count of
// 86,400*INTMAX_MAX ~= 7.97e23. This is representable by float and double, so they can always represent
// at least one day. On the other hand, one second with the largest possible _Period1 needs a tick count
// of 1/(INTMAX_MAX) ~= 1.08e-19, which is also representable in both float and double. So, both
// floating-point types can represent durations between one second and one day, regardless of _Period1.
return true;
} else {
// TRANSITION: user-defined arithmetic-like types
return true;
}
}
enum class _Parse_tp_or_duration { _Time_point, _Duration };
template <_Parse_tp_or_duration _Parse_type, class _DurationType>
_NODISCARD bool _Apply_duration_fields(_DurationType& _Result) {
constexpr bool _Can_rep_sec = _Can_represent<_DurationType, seconds>();
constexpr bool _Can_rep_min = _Can_represent<_DurationType, minutes>();
constexpr bool _Can_rep_hr = _Can_represent<_DurationType, hours>();
constexpr bool _Can_rep_day = _Can_represent<_DurationType, days>();
constexpr bool _For_time_point = _Parse_type == _Parse_tp_or_duration::_Time_point;
const auto _Required{(_For_time_point ? _F_day | _F_mon | _F_year : 0)};
const auto _Optional{(_For_time_point ? _F_wkday : 0) | (_Can_rep_sec ? _F_sec : 0)
| (_Can_rep_min ? _F_min : 0) | (_Can_rep_hr ? _F_hr : 0)
| (_Can_rep_day ? _F_doy : 0)};
const auto _Used{_Used_fields()};
const auto _Time_out_of_range{
_For_time_point
&& (_Out_of_range(_Second, 0, 60) || _Out_of_range(_Minute, 0, 59) || _Out_of_range(_Hour_24, 0, 23))};
if (_Time_out_of_range || !_Test_bits(_Used, _Required, _Optional)) {
return false;
}
_Result = _DurationType::zero();
if constexpr (_Can_rep_sec) {
if (_Used & _F_sec) {
if (_Subsecond) {
const _SubsecondType _Sub{*_Subsecond};
if constexpr (!treat_as_floating_point_v<typename _DurationType::rep>) {
using _CastedType = duration<int64_t, typename _DurationType::period>;
if (_CHRONO duration_cast<_CastedType>(_Sub) != _Sub) {
return false;
}
}
_Result += _CHRONO duration_cast<_DurationType>(_Sub);
}
if (_Second) {
_Result += _CHRONO duration_cast<_DurationType>(seconds{*_Second});
}
}
}
if constexpr (_Can_rep_min) {
if (_Used & _F_min) {
_Result += _CHRONO duration_cast<_DurationType>(minutes{*_Minute});
}
if (_Utc_offset) {
_Result -= _CHRONO duration_cast<_DurationType>(minutes{*_Utc_offset});
}
}
if constexpr (_Can_rep_hr) {
if (_Used & _F_hr) {
_Result += _CHRONO duration_cast<_DurationType>(hours{*_Hour_24});
}
}
if constexpr (_Can_rep_day) {
if (_For_time_point) {
const year_month_day _Ymd{year{*_Century + *_Two_dig_year},
month{static_cast<unsigned int>(*_Month)}, day{static_cast<unsigned int>(*_Day)}};
_Result += _CHRONO duration_cast<_DurationType>(static_cast<sys_days>(_Ymd).time_since_epoch());
} else if (_Used & _F_doy) {
_Result += _CHRONO duration_cast<_DurationType>(days{*_Day_of_year});
}
}
return true;
}
template <class _Rep, class _Period>
_NODISCARD bool _Make_duration(duration<_Rep, _Period>& _Duration_result) {
const bool _Consistent = _Calculate_hour24();
if (_Consistent) {
return _Apply_duration_fields<_Parse_tp_or_duration::_Duration>(_Duration_result);
}
return false;
}
enum class _Leap_second_rep : unsigned int {
_None, // system_clock, tai_clock, gps_clock; oblivious to leap seconds
_All, // utc_clock
_File_time // _None before 2018-06-01, _All afterwards.
};
template <class _DurationType>
_NODISCARD bool _Make_time_point(_DurationType& _Dur, _Leap_second_rep _Leap) {
const bool _Consistent{_Calculate_hour24() && _Calculate_year_fields() && _Calculate_ymd()};
if (!_Consistent || !_Apply_duration_fields<_Parse_tp_or_duration::_Time_point>(_Dur)) {
return false;
}
const _Leap_second_rep _Original_leap{_Leap};
if (_Leap == _Leap_second_rep::_File_time) {
_Leap =
_Dur < file_clock::_Cutoff.time_since_epoch() ? _Leap_second_rep::_None : _Leap_second_rep::_All;
}
if (_Second > (_Leap == _Leap_second_rep::_All ? 60 : 59)) {
return false;
}
// A distinction has to be made here between clocks that always count 86,400 seconds/day (system_clock,
// tai_clock, and gps_clock) and ones that count leap seconds (utc_clock and, since 2018-06-01, file_clock).
// The correct tick count for the clocks that always count 86,400 seconds/day can be determined without
// knowing whether any leap seconds have occurred, and there aren't any invalid times due to negative leap
// second deletions.
if (_Leap == _Leap_second_rep::_All) {
if constexpr (!_Can_represent<_DurationType, seconds>()) {
// Error if _Dur can't represent the adjustment below.
return false;
} else {
// It's possible that the parsed time doesn't exist because (a) _Seconds == 60 and there *isn't* a
// leap second insertion or (b) _Seconds >= 59 and there *is* a leap second deletion.
const auto& _Tzdb{_CHRONO get_tzdb()};
const bool _Possible_insertion{_Second == 60};
const sys_seconds _Target_sys_time{
_CHRONO floor<seconds>(_Dur) - (_Possible_insertion ? seconds{1} : seconds{0})};
const auto& _Ls_vector{_Tzdb.leap_seconds};
const auto _It{_STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), _Target_sys_time)};
const bool _Match_leap{_It != _Ls_vector.end() && *_It == _Target_sys_time + seconds{1}};
if (_Match_leap) {
if (!_It->_Positive()) { // negative leap second
if (_Second >= 59) {
return false;
}
}
} else { // no leap second
if (_Possible_insertion) {
return false;
}
}
const auto _Offset = _It == _Ls_vector.begin() ? seconds{0} : _Prev_iter(_It)->_Elapsed();
_Dur += _CHRONO duration_cast<_DurationType>(_Offset);
if (_Original_leap == _Leap_second_rep::_File_time) {
_Dur -= _CHRONO duration_cast<_DurationType>(
filesystem::_File_time_clock::_Skipped_filetime_leap_seconds);
}
}
}
return true;
}
_NODISCARD bool _Make_day(day& _Day_result) {
if (_Used_fields() == _F_day) {
_Day_result = day{static_cast<unsigned int>(*_Day)};
return true;
} else {
return false;
}
}
_NODISCARD bool _Make_weekday(weekday& _Weekday_result) {
if (_Used_fields() == _F_wkday) {
_Weekday_result = weekday{static_cast<unsigned int>(*_Weekday)};
return true;
} else {
return false;
}
}
_NODISCARD bool _Make_month(month& _Month_result) {
if (_Used_fields() == _F_mon) {
_Month_result = month{static_cast<unsigned int>(*_Month)};
return true;
} else {
return false;
}
}
_NODISCARD bool _Make_month_day(month_day& _Month_day_result) {
if (_Day_of_year && !_Yday_to_month_day(*_Day_of_year, 0)) {
return false;
}
constexpr auto _Required = _F_mon | _F_day;
constexpr auto _Optional = _F_doy;
if (_Test_bits(_Used_fields(), _Required, _Optional)) {
_Month_day_result =
month_day{month{static_cast<unsigned int>(*_Month)}, day{static_cast<unsigned int>(*_Day)}};
return true;
} else {
return false;
}
}
_NODISCARD bool _Make_year(year& _Year_result) {
if (_Calculate_year_fields() && _Used_fields() == _F_year) {
_Year_result = year{*_Century + *_Two_dig_year};
return true;
} else {
return false;
}
}
_NODISCARD bool _Make_year_month(year_month& _Year_month_result) {
if (_Calculate_year_fields() && _Used_fields() == (_F_mon | _F_year)) {
_Year_month_result =
year_month{year{*_Century + *_Two_dig_year}, month{static_cast<unsigned int>(*_Month)}};
return true;
} else {
return false;
}
}
_NODISCARD bool _Make_year_month_day(
year_month_day& _Year_month_day_result, const bool _For_time_point = false) {
const bool _Consistent = _Calculate_year_fields() && _Calculate_ymd();
constexpr auto _Required = _F_day | _F_mon | _F_year;
auto _Optional = _F_wkday | _F_doy;
if (_For_time_point) {
// These fields aren't used here, but they might be used later if converting to a time_point.
_Optional |= _F_sec | _F_min | _F_hr;
}
if (_Consistent && _Test_bits(_Used_fields(), _Required, _Optional)) {
_Year_month_day_result = year_month_day{year{*_Century + *_Two_dig_year},
month{static_cast<unsigned int>(*_Month)}, day{static_cast<unsigned int>(*_Day)}};
return true;
} else {
return false;
}
}
template <class _CharT, class _Traits>
_NODISCARD istreambuf_iterator<_CharT, _Traits> _Parse_time_field(istreambuf_iterator<_CharT, _Traits> _First,
ios_base& _Iosbase, ios_base::iostate& _State, const char _Flag, const char _Modifier,
const unsigned int _Width, const unsigned int _Subsecond_precision) {
const auto& _Ctype_fac = _STD use_facet<ctype<_CharT>>(_Iosbase.getloc());
const auto& _Time_fac = _STD use_facet<time_get<_CharT>>(_Iosbase.getloc());
constexpr istreambuf_iterator<_CharT, _Traits> _Last{};
int _Val{0};
switch (_Flag) {
case 'a':
case 'A':
{
tm _Tp;
_Tp.tm_wday = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'a');
if (!_Update(_Weekday, _Tp.tm_wday)) {
_State |= ios_base::failbit;
}
break;
}
case 'b':
case 'B':
case 'h':
{
tm _Tp;
_Tp.tm_mon = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'b');
if (_Tp.tm_mon == _Invalid_time_field<int> || !_Update(_Month, ++_Tp.tm_mon)) {
_State |= ios_base::failbit;
}
break;
}
case 'C':
if (_Modifier != 'E') {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
_Val *= 100;
} else {
tm _Tp;
_Tp.tm_year = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'C', 'E');
_Val = _Tp.tm_year;
if (_Tp.tm_year != _Invalid_time_field<int>) {
_Val += 1900;
}
}
if (!_Update(_Century, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'd':
case 'e':
if (_Modifier != 'O') {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
} else {
tm _Tp;
_Tp.tm_mday = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'd', 'O');
_Val = _Tp.tm_mday;
}
if (!_Update(_Day, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'D':
_First = _Parse_time_field_restricted(_First, _Iosbase, _State, "%m/%d/%y");
break;
case 'F':
{
// If modified with a width N, the width is applied to only %Y.
_State |= _Get_int(_First, _Width == 0 ? 4u : _Width, _Val, _Ctype_fac);
const auto _Year_parts = _Decompose_year(_Val);
if (_Update(_Century, _Year_parts.first) && _Update(_Two_dig_year, _Year_parts.second)) {
_First = _Parse_time_field_restricted(_First, _Iosbase, _State, "-%m-%d");
} else {
_State |= ios_base::failbit;
}
break;
}
case 'g':
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
if (!_Update(_Two_dig_iso_year, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'G':
{
_State |= _Get_int(_First, _Width == 0 ? 4u : _Width, _Val, _Ctype_fac);
const auto _Year_parts = _Decompose_year(_Val);
if (!_Update(_Iso_year, _Val) || !_Update(_Two_dig_iso_year, _Year_parts.second)) {
_State |= ios_base::failbit;
}
break;
}
case 'H':
if (_Modifier != 'O') {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
} else {
tm _Tp;
_Tp.tm_hour = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'H', 'O');
_Val = _Tp.tm_hour;
}
if (!_Update(_Hour_24, _Val)
|| (_Val < 24
&& (!_Update(_Ampm, _Val >= 12 ? 1 : 0)
|| !_Update(_Hour_12, _CHRONO make12(hours{_Val}).count())))) {
_State |= ios_base::failbit;
}
break;
case 'I':
if (_Modifier != 'O') {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
} else {
tm _Tp;
_Tp.tm_hour = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'I', 'O');
_Val = (_Tp.tm_hour == 0) ? 12 : _Tp.tm_hour;
}
if (!_Update(_Hour_12, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'j':
_State |= _Get_int(_First, _Width == 0 ? 3u : _Width, _Val, _Ctype_fac);
if (!_Update(_Day_of_year, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'M':
if (_Modifier != 'O') {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
} else {
tm _Tp;
_Tp.tm_min = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'M', 'O');
_Val = _Tp.tm_min;
}
if (!_Update(_Minute, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'm':
if (_Modifier != 'O') {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
} else {
tm _Tp;
_Initialize_time_point(_Tp);
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'm', 'O');
_Val = _Tp.tm_mon;
if (_Tp.tm_mon != _Invalid_time_field<int>) {
++_Val;
}
}
if (!_Update(_Month, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'p':
{
tm _Tp;
_Tp.tm_hour = 0;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'p');
if (!_Update(_Ampm, _Tp.tm_hour == 0 ? 0 : 1)) {
_State |= ios_base::failbit;
}
break;
}
case 'c':
case 'r':
case 'x':
case 'X':
{
tm _Tp;
_Initialize_time_point(_Tp);
const bool _Full_year = (_Flag == 'c'); // 'x' reads two-digit year, 'r' and 'X' read times
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, _Flag, _Modifier);
if (!_Update_if_valid(_Tp, _Full_year)) {
_State |= ios_base::failbit;
}
break;
}
case 'R':
_First = _Parse_time_field_restricted(_First, _Iosbase, _State, "%H:%M");
break;
case 'T':
_First = _Parse_time_field_restricted(_First, _Iosbase, _State, "%H:%M:%S", _Subsecond_precision);
break;
case 'S':
if (_Subsecond_precision == 0) {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
if (!_Update(_Second, _Val)) {
_State |= ios_base::failbit;
}
} else {
const auto& _Numpunct_fac = _STD use_facet<numpunct<_CharT>>(_Iosbase.getloc());
_State |=
_Get_fixed(_First, _Width == 0 ? 3 + _Subsecond_precision : _Width, _Ctype_fac, _Numpunct_fac);
}
break;
case 'u':
case 'w':
if (_Flag == 'w' && _Modifier == 'O') {
tm _Tp;
_Tp.tm_wday = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'w', 'O');
_Val = _Tp.tm_wday;
} else {
_State |= _Get_int(_First, _Width == 0 ? 1u : _Width, _Val, _Ctype_fac);
if (_Flag == 'u') {
// ISO weekday: [1,7], 7 == Sunday
if (_Val == 7) {
_Val = 0;
} else if (_Val == 0) {
_Val = 7; // out of range
}
}
}
if (!_Update(_Weekday, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'U':
case 'V':
case 'W':
{
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
auto& _Field{(_Flag == 'U') ? _Week_u : (_Flag == 'W' ? _Week_w : _Iso_week)};
if (!_Update(_Field, _Val)) {
_State |= ios_base::failbit;
}
break;
}
case 'y':
if (_Modifier == '\0') {
_State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac);
} else {
tm _Tp;
_Tp.tm_year = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'y', _Modifier);
if (_Modifier == 'E') {
_Val = _Tp.tm_year + 1900; // offset from %EC base year
} else {
const auto _Year_parts = _Decompose_year(_Tp.tm_year);
_Val = _Year_parts.second;
}
}
if (!_Update(_Two_dig_year, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'Y':
{
if (_Modifier == 'E') {
tm _Tp;
_Tp.tm_year = _Invalid_time_field<int>;
_First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'Y', 'E');
_Val = _Tp.tm_year + 1900;
} else {
_State |= _Get_int(_First, _Width == 0 ? 4u : _Width, _Val, _Ctype_fac);
}
const auto _Year_parts = _Decompose_year(_Val);
if (!_Update(_Century, _Year_parts.first) || !_Update(_Two_dig_year, _Year_parts.second)) {
_State |= ios_base::failbit;
}
break;
}
case 'z':
_State |= _Get_tz_offset(_First, _Ctype_fac, _Modifier == 'E' || _Modifier == 'O', _Val);
if (!_Update(_Utc_offset, _Val)) {
_State |= ios_base::failbit;
}
break;
case 'Z':
{
string _Name;
_State |= _Get_tz_name(_First, _Ctype_fac, _Name);
if (!_Update(_Tz_name, _Name)) {
_State |= ios_base::failbit;
}
break;
}
default:
// Invalid flag
_State |= ios_base::failbit;
break;
}
return _First;
}
template <class _CharT, class _Traits>
_NODISCARD istreambuf_iterator<_CharT, _Traits> _Parse_time_field_restricted(
istreambuf_iterator<_CharT, _Traits> _First, ios_base& _Iosbase, ios_base::iostate& _State,
const char* _Fmt, const unsigned int _Subsecond_precision = 0) {
// Parses a restricted format string. It generally doesn't handle anything parsed outside of
// _Parse_time_field:
// (a) any whitespace (' ', %n, %t)
// (b) %% literal (other literals are parsed, however)
// (c) E or O modifiers
// (d) width parameter
// It also assumes a valid format string, specifically that '%' is always followed by a flag.
const auto& _Ctype_fac{_STD use_facet<ctype<_CharT>>(_Iosbase.getloc())};
constexpr istreambuf_iterator<_CharT, _Traits> _Last{};
while (*_Fmt != '\0' && (_State & ~ios_base::eofbit) == ios_base::goodbit) {
if (_First == _Last) {
_State |= ios_base::failbit | ios_base::eofbit;
break;
} else if (*_Fmt == '%') {
_First = _Parse_time_field(_First, _Iosbase, _State, *++_Fmt, '\0', 0, _Subsecond_precision);
} else if (_Ctype_fac.narrow(*_First++) != *_Fmt) {
_State |= ios_base::failbit;
}
++_Fmt;
}
return _First;
}
template <class _CharT, class _Traits>
_NODISCARD ios_base::iostate _Get_fixed(istreambuf_iterator<_CharT, _Traits>& _First, unsigned int _Width,
const ctype<_CharT>& _Ctype_fac, const numpunct<_CharT>& _Numpunct_fac) {
constexpr istreambuf_iterator<_CharT, _Traits> _Last{};
while (_First != _Last && _Ctype_fac.is(ctype_base::space, *_First) && _Width > 0) {
++_First;
--_Width;
}
int _Second_ = 0;
int64_t _Subsecond_ = 0;
int64_t _Multiplier = _SubsecondType::period::den;
bool _Found_point = false;
bool _Found_digit = false;
while (_First != _Last && _Width > 0 && _Multiplier >= 10) {
const auto _Ch = *_First;
if (_Ch == _Numpunct_fac.decimal_point() && !_Found_point) {
_Found_point = true;
} else if (_Ctype_fac.is(ctype_base::digit, _Ch)) {
_Found_digit = true;
const auto _Digit = _Ctype_fac.narrow(_Ch) - '0';
if (_Found_point) {
_Multiplier /= 10;
_Subsecond_ += _Digit * _Multiplier;
} else {
if (_Second_ > ((numeric_limits<int>::max)() - _Digit) / 10) {
return ios_base::failbit;
}
_Second_ = _Second_ * 10 + _Digit;
}
} else {
break;
}
++_First;
--_Width;
}
ios_base::iostate _State = ios_base::goodbit;
if (_First == _Last) {
_State |= ios_base::eofbit;
}
if (!(_Found_digit && _Update(_Second, _Second_) && _Update(_Subsecond, _Subsecond_))) {
_State |= ios_base::failbit;
}
return _State;
}
template <class _CharT, class _Traits>
_NODISCARD ios_base::iostate _Get_int(istreambuf_iterator<_CharT, _Traits>& _First, unsigned int _Width,
int& _Val, const ctype<_CharT>& _Ctype_fac) {
constexpr istreambuf_iterator<_CharT, _Traits> _Last{};
while (_First != _Last && _Ctype_fac.is(ctype_base::space, *_First) && _Width > 0) {
++_First;
--_Width;
}
char _Ac[_Max_int_dig];
char* _Ptr = _Ac;
if (_First != _Last && _Width > 0) {
const char _Ch = _Ctype_fac.narrow(*_First);
if (_Ch == '+' || _Ch == '-') { // copy sign
*_Ptr++ = _Ch;
++_First;
--_Width;
}
}
bool _Has_leading_zero = false;
while (_First != _Last && _Width > 0 && _Ctype_fac.narrow(*_First) == '0') { // strip leading zeros
++_First;
--_Width;
_Has_leading_zero = true;
}
if (_Has_leading_zero) {
*_Ptr++ = '0';
}
char _Ch;
while (_First != _Last && _Width > 0 && '0' <= (_Ch = _Ctype_fac.narrow(*_First))
&& _Ch <= '9') { // copy digits
*_Ptr = _Ch;
if (_Ptr < _STD cend(_Ac)) {
++_Ptr; // drop trailing digits if already too large
}
++_First;
--_Width;
}
*_Ptr = '\0';
int _Errno = 0;
char* _Ep;
const long _Ans = _CSTD _Stolx(_Ac, &_Ep, 10, &_Errno);
ios_base::iostate _State = ios_base::goodbit;
if (_First == _Last) {
_State |= ios_base::eofbit;
}
if (_Ep == _Ac || _Errno != 0) {
_State |= ios_base::failbit; // bad conversion
} else {
_Val = _Ans; // store valid result
}
return _State;
}
template <class _CharT, class _Traits>
_NODISCARD ios_base::iostate _Get_tz_offset(istreambuf_iterator<_CharT, _Traits>& _First,
const ctype<_CharT>& _Ctype_fac, const bool _Is_modified, int& _Offset) {
constexpr istreambuf_iterator<_CharT, _Traits> _Last{};
if (_First == _Last) {
return ios_base::eofbit;
}
bool _Negative = false;
switch (_Ctype_fac.narrow(*_First)) {
case '-':
_Negative = true;
[[fallthrough]];
case '+':
++_First;
break;
default:
break;
}
// For a regular offset hh[mm], simply read four digits, with the option of an EOF or non-digit after
// reading two. The modified form h[h][:mm] is similar, except for the following points:
// (a) an EOF or non-digit is allowable after reading *one* digit, not two.
// (b) after reading one digit, another optional digit keeps us in the same state, except for decrementing
// the number of optional digits allowed. In this state, reading a ':' allows parsing to continue.
int _Tz_hours = 0;
int _Tz_minutes = 0;
int _Optional_digits = 1;
for (int _Count = 0; _Count < 4; ++_Count) {
const bool _Allow_match_fail{_Count == (_Is_modified ? 1 : 2)};
if (_First == _Last) {
if (_Allow_match_fail) {
break;
} else {
return ios_base::eofbit | ios_base::failbit;
}
}
const char _Ch = _Ctype_fac.narrow(*_First++);
const bool _Is_digit = ('0' <= _Ch && _Ch <= '9');
if (_Is_modified && _Count == 1) {
if (_Ch == ':') {
continue;
} else if (_Is_digit && _Optional_digits > 0) {
_Tz_hours = 10 * _Tz_hours + (_Ch - '0');
--_Optional_digits;
--_Count;
} else {
if (_Allow_match_fail) {
break;
} else {
return ios_base::failbit;
}
}
} else if (_Is_digit) {
auto& _Part = _Count < 2 ? _Tz_hours : _Tz_minutes;
_Part = 10 * _Part + (_Ch - '0');
} else {
if (_Allow_match_fail) {
break;
} else {
return ios_base::failbit;
}
}
}
_Offset = 60 * _Tz_hours + _Tz_minutes;
if (_Negative) {
_Offset = -_Offset;
}
return ios_base::goodbit;
}
template <class _CharT, class _Traits>
_NODISCARD ios_base::iostate _Get_tz_name(
istreambuf_iterator<_CharT, _Traits>& _First, const ctype<_CharT>& _Ctype_fac, string& _Tz_name) {
constexpr istreambuf_iterator<_CharT, _Traits> _Last{};
_Tz_name.clear();
while (_First != _Last) {
const char _Ch{_Ctype_fac.narrow(*_First)};
if (_STD isalnum(static_cast<unsigned char>(_Ch)) || _Ch == '_' || _Ch == '/' || _Ch == '-'
|| _Ch == '+') {
_Tz_name.push_back(_Ch);
++_First;
} else {
break;
}
}
return _First == _Last ? ios_base::eofbit : ios_base::goodbit;
}
template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
_Time_parse_fields(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _FmtFirst,
basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr,
unsigned int _Subsecond_precision = 0) {
using _Myis = basic_istream<_CharT, _Traits>;
const auto& _Ctype_fac = _STD use_facet<ctype<_CharT>>(_Istr.getloc());
ios_base::iostate _State = ios_base::goodbit;
const _CharT* const _FmtLast = _FmtFirst + _Traits::length(_FmtFirst);
const typename _Myis::sentry _Ok{_Istr, true};
istreambuf_iterator _First{_Istr};
constexpr decltype(_First) _Last{};
if (_Ok) {
_TRY_IO_BEGIN
for (; _FmtFirst != _FmtLast && (_State & ~ios_base::eofbit) == ios_base::goodbit; ++_FmtFirst) {
if (_First == _Last) {
// EOF is not an error if the remaining flags can match zero characters.
for (; _FmtFirst != _FmtLast; ++_FmtFirst) {
char _Flag{};
if (_Ctype_fac.is(ctype_base::space, *_FmtFirst)) {
_Flag = ' ';
} else {
if (_Ctype_fac.narrow(*_FmtFirst) == '%' && ++_FmtFirst != _FmtLast) {
_Flag = _Ctype_fac.narrow(*_FmtFirst);
}
}
if (_Flag != ' ' && _Flag != 't') {
_State |= ios_base::failbit | ios_base::eofbit;
break;
}
}
break;
} else if (_Ctype_fac.narrow(*_FmtFirst) != '%') { // match literal element
if (_Ctype_fac.is(ctype_base::space, *_FmtFirst)) {
while (_First != _Last && _Ctype_fac.is(ctype_base::space, *_First)) {
++_First;
}
} else if (*_First == *_FmtFirst) {
++_First;
} else {
_State |= ios_base::failbit; // bad literal match
}
} else if (++_FmtFirst == _FmtLast) { // not enough for a valid flag
_State |= ios_base::failbit;
} else { // get flag after %
char _Flag = _Ctype_fac.narrow(*_FmtFirst);
char _Modifier = '\0';
unsigned int _Width = 0;
if (_Flag == 'E' || _Flag == 'O') {
if (++_FmtFirst == _FmtLast) {
_State |= ios_base::failbit;
break;
}
_Modifier = _STD exchange(_Flag, _Ctype_fac.narrow(*_FmtFirst));
} else if ('0' <= _Flag && _Flag <= '9') {
_Width = static_cast<unsigned int>(_Flag - '0');
while (++_FmtFirst != _FmtLast && _Ctype_fac.is(ctype_base::digit, *_FmtFirst)) {
const auto _Digit = static_cast<unsigned int>(_Ctype_fac.narrow(*_FmtFirst) - '0');
if (_Width > ((numeric_limits<unsigned int>::max)() - _Digit) / 10) {
_State |= ios_base::failbit;
break;
}
_Width = 10 * _Width + _Digit;
}
if (_FmtFirst == _FmtLast) {
_State |= ios_base::failbit;
break;
}
_Flag = _Ctype_fac.narrow(*_FmtFirst);
}
switch (_Flag) {
case 'n': // exactly one space
if (!_Ctype_fac.is(ctype_base::space, *_First++)) {
_State |= ios_base::failbit;
}
break;
case 't': // zero or one space
if (_Ctype_fac.is(ctype_base::space, *_First)) {
++_First;
}
break;
case '%':
if (_Ctype_fac.narrow(*_First++) != '%') {
_State |= ios_base::failbit;
}
break;
default:
_First = _Parse_time_field(_First, _Istr, _State, _Flag, _Modifier, _Width,
_Subsecond_precision); // convert a single field
break;
}
}
}
_CATCH_IO_(ios_base, _Istr)
}
if (!_Is_complete()) {
_State |= ios_base::failbit;
}
if (!(_State & ios_base::failbit)) {
if (_Offset != nullptr && _Utc_offset) {
*_Offset = minutes{*_Utc_offset};
}
if (_Abbrev != nullptr && _Tz_name) {
if constexpr (is_same_v<decltype(_Abbrev), string*>) {
*_Abbrev = _STD move(*_Tz_name);
} else {
_Abbrev->clear();
for (const char& _Ch : *_Tz_name) {
_Abbrev->push_back(_Ctype_fac.widen(_Ch));
}
}
}
}
_Istr.setstate(_State);
}
};
_EXPORT_STD template <class _CharT, class _Traits, class _Rep, class _Period, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
duration<_Rep, _Period>& _Duration, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr,
minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, hh_mm_ss<duration<_Rep, _Period>>::fractional_width};
if (_Istr && !_Time._Make_duration(_Duration)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, weekday& _Wd,
basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset};
if (_Istr && !_Time._Make_weekday(_Wd)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, day& _Day,
basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset};
if (_Istr && !_Time._Make_day(_Day)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
month& _Month, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset};
if (_Istr && !_Time._Make_month(_Month)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
month_day& _Md, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset};
if (_Istr && !_Time._Make_month_day(_Md)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, year& _Year,
basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset};
if (_Istr && !_Time._Make_year(_Year)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
year_month& _Ym, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset};
if (_Istr && !_Time._Make_year_month(_Ym)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
year_month_day& _Ymd, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) {
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset};
if (_Istr && !_Time._Make_year_month_day(_Ymd)) {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
utc_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr,
minutes* _Offset = nullptr) {
constexpr auto _Subsecond_precision{hh_mm_ss<_Duration>::fractional_width};
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, _Subsecond_precision};
_Duration _Dur;
if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_Leap_second_rep::_All)) {
_Tp = utc_time<_Duration>{_Dur};
} else {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
sys_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr,
minutes* _Offset = nullptr) {
constexpr auto _Subsecond_precision{hh_mm_ss<_Duration>::fractional_width};
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, _Subsecond_precision};
_Duration _Dur;
if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_Leap_second_rep::_None)) {
_Tp = sys_time<_Duration>{_Dur};
} else {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
tai_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr,
minutes* _Offset = nullptr) {
constexpr auto _Subsecond_precision{hh_mm_ss<_Duration>::fractional_width};
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, _Subsecond_precision};
_Duration _Dur;
if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_Leap_second_rep::_None)) {
_Tp = tai_time<_Duration>{_Dur + _CHRONO duration_cast<_Duration>(days{4383})};
} else {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
gps_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr,
chrono::minutes* _Offset = nullptr) {
constexpr auto _Subsecond_precision{hh_mm_ss<_Duration>::fractional_width};
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, _Subsecond_precision};
_Duration _Dur;
if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_Leap_second_rep::_None)) {
_Tp = gps_time<_Duration>{_Dur - _CHRONO duration_cast<_Duration>(days{3657})};
} else {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
file_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr,
chrono::minutes* _Offset = nullptr) {
constexpr auto _Subsecond_precision{hh_mm_ss<_Duration>::fractional_width};
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, _Subsecond_precision};
_Duration _Dur;
if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_Leap_second_rep::_File_time)) {
constexpr auto _File_clock_adj{_CHRONO duration_cast<_Duration>(
filesystem::_File_time_clock::duration{filesystem::__std_fs_file_time_epoch_adjustment})};
_Tp = file_time<_Duration>{_Dur} + _File_clock_adj;
} else {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration, class _Alloc = allocator<_CharT>>
basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt,
local_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr,
minutes* _Offset = nullptr) {
constexpr auto _Subsecond_precision{hh_mm_ss<_Duration>::fractional_width};
_Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, _Subsecond_precision};
// *_Offset is not subtracted from local_time, see N4950 [time.clock.local]/4.
_Time._Utc_offset.reset();
_Duration _Dur;
if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_Leap_second_rep::_None)) {
_Tp = local_time<_Duration>{_Dur};
} else {
_Istr.setstate(ios_base::failbit);
}
return _Istr;
}
template <class _CharT, class _Traits, class _Alloc, class _Parsable>
struct _Time_parse_iomanip_c_str {
_Time_parse_iomanip_c_str(const _CharT* _Fmt_, _Parsable& _Tp_,
basic_string<_CharT, _Traits, _Alloc>* _Abbrev_ = nullptr, minutes* _Offset_ = nullptr)
: _Fmt{_Fmt_}, _Tp{_Tp_}, _Abbrev{_Abbrev_}, _Offset{_Offset_} {}
_Time_parse_iomanip_c_str(_Time_parse_iomanip_c_str&&) = delete;
friend basic_istream<_CharT, _Traits>& operator>>(
basic_istream<_CharT, _Traits>& _Is, _Time_parse_iomanip_c_str&& _Tpi) {
from_stream(_Is, _Tpi._Fmt, _Tpi._Tp, _Tpi._Abbrev, _Tpi._Offset); // intentional ADL
return _Is;
}
const _CharT* _Fmt;
_Parsable& _Tp;
basic_string<_CharT, _Traits, _Alloc>* _Abbrev;
minutes* _Offset;
};
template <class _CharT, class _Traits, class _Alloc, class _Parsable>
struct _Time_parse_iomanip {
_Time_parse_iomanip(const basic_string<_CharT, _Traits, _Alloc>& _Fmt_, _Parsable& _Tp_,
basic_string<_CharT, _Traits, _Alloc>* _Abbrev_ = nullptr, minutes* _Offset_ = nullptr)
: _Fmt{_Fmt_}, _Tp{_Tp_}, _Abbrev{_Abbrev_}, _Offset{_Offset_} {}
_Time_parse_iomanip(_Time_parse_iomanip&&) = delete;
friend basic_istream<_CharT, _Traits>& operator>>(
basic_istream<_CharT, _Traits>& _Is, _Time_parse_iomanip&& _Tpi) {
from_stream(_Is, _Tpi._Fmt.c_str(), _Tpi._Tp, _Tpi._Abbrev, _Tpi._Offset); // intentional ADL
return _Is;
}
const basic_string<_CharT, _Traits, _Alloc>& _Fmt;
_Parsable& _Tp;
basic_string<_CharT, _Traits, _Alloc>* _Abbrev;
minutes* _Offset;
};
template <class _Parsable, class _CharT, class _Traits, class... _Rest>
concept _Can_from_stream = requires(
basic_istream<_CharT, _Traits>& __istr, const _CharT* __s, _Parsable& __parsed, _Rest&&... __rest_args) {
from_stream(__istr, +__s, __parsed, _STD forward<_Rest>(__rest_args)...); // intentional ADL
};
_EXPORT_STD template <class _CharT, _Can_from_stream<_CharT, char_traits<_CharT>> _Parsable>
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp) {
return _Time_parse_iomanip_c_str<_CharT, char_traits<_CharT>, allocator<_CharT>, _Parsable>{_Fmt, _Tp};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, _Can_from_stream<_CharT, _Traits> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp) {
return _Time_parse_iomanip{_Fmt, _Tp};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*> _Parsable>
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp, basic_string<_CharT, _Traits, _Alloc>& _Abbrev) {
return _Time_parse_iomanip_c_str{_Fmt, _Tp, _STD addressof(_Abbrev)};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp,
basic_string<_CharT, _Traits, _Alloc>& _Abbrev) {
return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev)};
}
_EXPORT_STD template <class _CharT,
_Can_from_stream<_CharT, char_traits<_CharT>, basic_string<_CharT>*, minutes*> _Parsable>
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp, minutes& _Offset) {
return _Time_parse_iomanip_c_str{_Fmt, _Tp, static_cast<basic_string<_CharT>*>(nullptr), &_Offset};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp, minutes& _Offset) {
return _Time_parse_iomanip{_Fmt, _Tp, static_cast<basic_string<_CharT, _Traits, _Alloc>*>(nullptr), &_Offset};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
_NODISCARD auto parse(
const _CharT* _Fmt, _Parsable& _Tp, basic_string<_CharT, _Traits, _Alloc>& _Abbrev, minutes& _Offset) {
return _Time_parse_iomanip_c_str{_Fmt, _Tp, _STD addressof(_Abbrev), &_Offset};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp,
basic_string<_CharT, _Traits, _Alloc>& _Abbrev, minutes& _Offset) {
return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev), &_Offset};
}
} // namespace chrono
// [time.format]
template <class _Ty, class _CharT>
concept _Chrono_parse_spec_callbacks =
_Parse_align_callbacks<_Ty, _CharT> && _Parse_width_callbacks<_Ty, _CharT>
&& _Parse_precision_callbacks<_Ty, _CharT> && _Width_adapter_callbacks<_Ty, _CharT>
&& _Precision_adapter_callbacks<_Ty, _CharT> && requires(_Ty _At, basic_string_view<_CharT> _Sv, _Fmt_align _Aln) {
{ _At._On_conversion_spec(char{}, _CharT{}) } -> same_as<void>;
{ _At._On_lit_char(_CharT{}) } -> same_as<void>;
};
template <class _Ty>
concept _Has_ok = requires(_Ty _At) {
{ _At.ok() } -> same_as<bool>;
};
// A chrono spec is either a type (with an optional modifier), OR a literal character, never both.
template <class _CharT>
struct _Chrono_spec {
_CharT _Lit_char{'\0'}; // any character other than '{', '}', or '%'
char _Modifier = '\0'; // either 'E' or 'O'
char _Type = '\0';
};
template <class _CharT>
struct _Chrono_format_specs {
int _Width = 0;
int _Precision = -1;
int _Dynamic_width_index = -1;
int _Dynamic_precision_index = -1;
_Fmt_align _Alignment = _Fmt_align::_None;
uint8_t _Fill_length = 1;
bool _Localized = false;
// At most one codepoint (so one char32_t or four utf-8 char8_t)
_CharT _Fill[4 / sizeof(_CharT)] = {_CharT{' '}};
// recursive definition in grammar, so could have any number of these
vector<_Chrono_spec<_CharT>> _Chrono_specs_list;
};
// Model of _Chrono_parse_spec_callbacks that fills a _Chrono_format_specs with the parsed data
template <class _CharT, class _ParseContext>
class _Chrono_specs_setter {
public:
constexpr explicit _Chrono_specs_setter(_Chrono_format_specs<_CharT>& _Specs_, _ParseContext& _Parse_ctx_)
: _Specs(_Specs_), _Parse_ctx(_Parse_ctx_) {}
// same as _Specs_setter
constexpr void _On_align(_Fmt_align _Aln) {
_Specs._Alignment = _Aln;
}
// same as _Specs_setter
constexpr void _On_fill(basic_string_view<_CharT> _Sv) {
if (_Sv.size() > _STD size(_Specs._Fill)) {
_Throw_format_error("Invalid fill (too long).");
}
const auto _Pos = _STD _Copy_unchecked(_Sv._Unchecked_begin(), _Sv._Unchecked_end(), _Specs._Fill);
_STD fill(_Pos, _STD end(_Specs._Fill), _CharT{});
_Specs._Fill_length = static_cast<uint8_t>(_Sv.size());
}
constexpr void _On_width(int _Width) {
_Specs._Width = _Width;
}
constexpr void _On_precision(int _Prec) {
_Specs._Precision = _Prec;
}
constexpr void _On_dynamic_width(const size_t _Arg_id) {
_Parse_ctx.check_arg_id(_Arg_id);
_Parse_ctx._Check_dynamic_spec_integral(_Arg_id);
_Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Arg_id);
}
constexpr void _On_dynamic_width(_Auto_id_tag) {
const size_t _Arg_id = _Parse_ctx.next_arg_id();
_Parse_ctx._Check_dynamic_spec_integral(_Arg_id);
_Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Arg_id);
}
constexpr void _On_dynamic_precision(const size_t _Arg_id) {
_Parse_ctx.check_arg_id(_Arg_id);
_Parse_ctx._Check_dynamic_spec_integral(_Arg_id);
_Specs._Dynamic_precision_index = _Verify_dynamic_arg_index_in_range(_Arg_id);
}
constexpr void _On_dynamic_precision(_Auto_id_tag) {
const size_t _Arg_id = _Parse_ctx.next_arg_id();
_Parse_ctx._Check_dynamic_spec_integral(_Arg_id);
_Specs._Dynamic_precision_index = _Verify_dynamic_arg_index_in_range(_Arg_id);
}
constexpr void _On_localized() {
_Specs._Localized = true;
}
constexpr void _On_conversion_spec(char _Modifier, _CharT _Type) {
if (_Modifier != '\0' && _Modifier != 'E' && _Modifier != 'O') {
_Throw_format_error("Invalid modifier specification.");
}
if (_Type < 0 || _Type > (numeric_limits<signed char>::max)()) {
_Throw_format_error("Invalid type specification.");
}
_Chrono_spec<_CharT> _Conv_spec{._Modifier = _Modifier, ._Type = static_cast<char>(_Type)};
_Specs._Chrono_specs_list.push_back(_Conv_spec);
}
constexpr void _On_lit_char(_CharT _Lit_ch) {
_Chrono_spec<_CharT> _Lit_char_spec{._Lit_char = _Lit_ch};
_Specs._Chrono_specs_list.push_back(_Lit_char_spec);
}
private:
_Chrono_format_specs<_CharT>& _Specs;
_ParseContext& _Parse_ctx;
_NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) {
if (!_STD in_range<int>(_Idx)) {
_Throw_format_error("Dynamic width or precision index too large.");
}
return static_cast<int>(_Idx);
}
};
// assumes that the required '%' at the beginning of a conversion-spec has already been consumed
template <class _CharT, _Chrono_parse_spec_callbacks<_CharT> _Callbacks_type>
_NODISCARD constexpr const _CharT* _Parse_conversion_specs(
const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) {
if (_Begin == _End || *_Begin == '}') {
_Throw_format_error("Invalid format string.");
}
char _Mod = '\0';
_CharT _Ch = *_Begin;
if (_Ch == 'E' || _Ch == 'O') { // includes modifier
_Mod = static_cast<char>(_Ch);
++_Begin;
if (_Begin == _End || *_Begin == '}') {
_Throw_format_error("Invalid format string - missing type after modifier.");
}
}
_CharT _Type = *_Begin;
_Callbacks._On_conversion_spec(_Mod, _Type);
return ++_Begin;
}
template <class _CharT, _Chrono_parse_spec_callbacks<_CharT> _Callbacks_type>
_NODISCARD constexpr const _CharT* _Parse_chrono_format_specs(
const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) {
if (_Begin == _End || *_Begin == '}') {
return _Begin;
}
_Begin = _Parse_align(_Begin, _End, _Callbacks);
if (_Begin == _End) {
return _Begin;
}
_Begin = _Parse_width(_Begin, _End, _Callbacks);
if (_Begin == _End) {
return _Begin;
}
if (*_Begin == '.') {
_Begin = _Parse_precision(_Begin, _End, _Callbacks);
if (_Begin == _End) {
return _Begin;
}
}
if (*_Begin == 'L') {
_Callbacks._On_localized();
++_Begin;
if (_Begin == _End) {
return _Begin;
}
}
if (*_Begin != '}' && *_Begin != '%') {
_Throw_format_error("Invalid format string - chrono-specs must begin with conversion-spec");
}
// chrono-spec
while (_Begin != _End && *_Begin != '}') {
// Note that in this loop, ++_Begin is safe (and we don't need _Fmt_codec)
// because '%' isn't used as a non-lead-byte in any supported multibyte encoding.
if (*_Begin == '%') { // conversion-spec
if (++_Begin == _End) {
_Throw_format_error("Invalid format string - missing type after %");
}
switch (*_Begin) {
case 'n':
_Callbacks._On_lit_char('\n');
++_Begin;
break;
case 't':
_Callbacks._On_lit_char('\t');
++_Begin;
break;
case '%':
_Callbacks._On_lit_char('%');
++_Begin;
break;
default: // some other type
_Begin = _Parse_conversion_specs(_Begin, _End, _Callbacks);
break;
}
} else { // literal-char
_Callbacks._On_lit_char(*_Begin);
++_Begin;
}
}
return _Begin;
}
namespace chrono {
template <class _Duration>
struct _Local_time_format_t {
local_time<_Duration> _Time;
const string* _Abbrev = nullptr;
const seconds* _Offset_sec = nullptr;
template <class _CharT, class _Traits>
friend basic_ostream<_CharT, _Traits>& operator<<(
basic_ostream<_CharT, _Traits>& _Os, const _Local_time_format_t& _Val) {
// Doesn't appear in the Standard, but allowed by N4950 [global.functions]/2.
// Implements N4950 [time.zone.zonedtime.nonmembers]/2 for zoned_time.
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T %Z}"), _Val);
}
};
_EXPORT_STD template <class _Duration>
_NODISCARD _Local_time_format_t<_Duration> local_time_format(const local_time<_Duration> _Time,
const string* const _Abbrev = nullptr, const seconds* const _Offset_sec = nullptr) {
return {_Time, _Abbrev, _Offset_sec};
}
// Replacement for %S, as put_time does not honor writing fractional seconds.
template <class _CharT, class _Traits, class _Ty>
void _Write_seconds(basic_ostream<_CharT, _Traits>&, const _Ty&) {
_STL_INTERNAL_CHECK(false);
}
template <unsigned int _Fractional_width, class _CharT, class _Traits, class _Precision>
void _Write_fractional_seconds(
basic_ostream<_CharT, _Traits>& _Os, const seconds& _Seconds, const _Precision& _Subseconds) {
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:02}"), _Seconds.count());
if constexpr (_Fractional_width > 0) {
_Os << _STD use_facet<numpunct<_CharT>>(_Os.getloc()).decimal_point();
if constexpr (treat_as_floating_point_v<typename _Precision::rep>) {
_Os << _STD format(
_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"), _STD floor(_Subseconds.count()), _Fractional_width);
} else {
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:0{}}"), _Subseconds.count(), _Fractional_width);
}
}
}
template <class _CharT, class _Traits, class _Duration>
void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const hh_mm_ss<_Duration>& _Val) {
_Write_fractional_seconds<hh_mm_ss<_Duration>::fractional_width>(_Os, _Val.seconds(), _Val.subseconds());
}
template <class _CharT, class _Traits, class _Clock, class _Duration>
void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const time_point<_Clock, _Duration>& _Val) {
if constexpr (is_same_v<_Clock, utc_clock>) {
const auto _Lsi = _CHRONO get_leap_second_info(_Val);
const auto _Dp =
_CHRONO floor<days>(_Val - _Lsi.elapsed) + _Lsi.elapsed - seconds{_Lsi.is_leap_second ? 1 : 0};
const hh_mm_ss _Hms{_Val - _Dp};
constexpr auto _Fractional_width = decltype(_Hms)::fractional_width;
if (_Lsi.is_leap_second) {
_Write_fractional_seconds<_Fractional_width>(_Os, _Hms.seconds() + seconds{60}, _Hms.subseconds());
} else {
_Write_fractional_seconds<_Fractional_width>(_Os, _Hms.seconds(), _Hms.subseconds());
}
} else {
const auto _Dp = _CHRONO floor<days>(_Val);
_Write_seconds(_Os, hh_mm_ss{_Val - _Dp});
}
}
template <class _CharT, class _Traits, class _Duration>
void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const _Local_time_format_t<_Duration>& _Val) {
_Write_seconds(_Os, _Val._Time);
}
template <class _CharT, class _Traits, class _Rep, class _Period>
void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const duration<_Rep, _Period>& _Val) {
_Write_seconds(_Os, hh_mm_ss{_Val});
}
template <class _Ty>
_NODISCARD tm _Fill_tm(const _Ty& _Val) {
unsigned int _Day = 0;
unsigned int _Month = 0;
int _Year = 0;
int _Yearday = 0;
int _Weekday = 0;
int _Hours = 0;
int _Minutes = 0;
int _Seconds = 0;
if constexpr (_Is_specialization_v<_Ty, duration>) {
return _Fill_tm(hh_mm_ss{_Val});
} else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) {
return _Fill_tm(_Val._Time);
} else if constexpr (is_same_v<_Ty, day>) {
_Day = static_cast<unsigned int>(_Val);
} else if constexpr (is_same_v<_Ty, month>) {
_Month = static_cast<unsigned int>(_Val);
} else if constexpr (is_same_v<_Ty, year>) {
_Year = static_cast<int>(_Val);
} else if constexpr (is_same_v<_Ty, weekday>) {
_Weekday = static_cast<int>(_Val.c_encoding());
} else if constexpr (_Is_any_of_v<_Ty, weekday_indexed, weekday_last>) {
_Weekday = static_cast<int>(_Val.weekday().c_encoding());
} else if constexpr (is_same_v<_Ty, month_day>) {
_Day = static_cast<unsigned int>(_Val.day());
_Month = static_cast<unsigned int>(_Val.month());
if (_Val.month() == January) {
_Yearday = static_cast<int>(_Day) - 1;
} else if (_Val.month() == February) {
_Yearday = 31 + static_cast<int>(_Day) - 1;
}
} else if constexpr (is_same_v<_Ty, month_day_last>) {
_Month = static_cast<unsigned int>(_Val.month());
_Day = static_cast<unsigned int>(_Last_day_table[(_Month - 1) & 0xF]);
if (_Val.month() == January) {
_Yearday = 30;
}
} else if constexpr (is_same_v<_Ty, month_weekday>) {
_Month = static_cast<unsigned int>(_Val.month());
_Weekday = static_cast<int>(_Val.weekday_indexed().weekday().c_encoding());
} else if constexpr (is_same_v<_Ty, month_weekday_last>) {
_Month = static_cast<unsigned int>(_Val.month());
_Weekday = static_cast<int>(_Val.weekday_last().weekday().c_encoding());
} else if constexpr (is_same_v<_Ty, year_month>) {
_Month = static_cast<unsigned int>(_Val.month());
_Year = static_cast<int>(_Val.year());
} else if constexpr (_Is_any_of_v<_Ty, year_month_day, year_month_day_last>) {
_Day = static_cast<unsigned int>(_Val.day());
_Month = static_cast<unsigned int>(_Val.month());
_Year = static_cast<int>(_Val.year());
if (_Val.ok()) {
const year_month_day& _Ymd = _Val;
_Weekday = _Ymd._Calculate_weekday();
_Yearday = (static_cast<sys_days>(_Val) - static_cast<sys_days>(_Val.year() / January / 1)).count();
}
} else if constexpr (_Is_any_of_v<_Ty, year_month_weekday, year_month_weekday_last>) {
auto _Tm = _Fill_tm(year_month_day{_Val});
_Tm.tm_wday = static_cast<int>(_Val.weekday().c_encoding());
return _Tm;
} else if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) {
_Hours = _Val.hours().count();
_Minutes = _Val.minutes().count();
_Seconds = static_cast<int>(_Val.seconds().count());
} else if constexpr (_Is_any_of_v<_Ty, sys_info, local_info>) {
return {}; // none of the valid conversion specifiers need tm fields
} else if constexpr (_Is_specialization_v<_Ty, time_point>) {
const auto _Dp = _CHRONO floor<days>(_Val);
const year_month_day _Ymd{_Dp};
const hh_mm_ss _Time{_Val - _Dp};
const auto _Hms = _Fill_tm(_Time);
auto _Tm = _Fill_tm(_Ymd);
_Tm.tm_sec = _Hms.tm_sec;
_Tm.tm_min = _Hms.tm_min;
_Tm.tm_hour = _Hms.tm_hour;
return _Tm;
}
tm _Time;
_Time.tm_sec = _Seconds;
_Time.tm_min = _Minutes;
_Time.tm_hour = _Hours;
_Time.tm_mday = static_cast<int>(_Day);
_Time.tm_mon = static_cast<int>(_Month) - 1;
_Time.tm_year = _Year - 1900;
_Time.tm_yday = _Yearday;
_Time.tm_wday = _Weekday;
return _Time;
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const day& _Val) {
return _Os << (_Val.ok() ? _STD format(_STATICALLY_WIDEN(_CharT, "{:%d}"), _Val)
: _STD format(_STATICALLY_WIDEN(_CharT, "{:%d} is not a valid day"), _Val));
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month& _Val) {
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%b}"), _Val)
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} is not a valid month"),
static_cast<unsigned int>(_Val)));
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year& _Val) {
return _Os << (_Val.ok() ? _STD format(_STATICALLY_WIDEN(_CharT, "{:%Y}"), _Val)
: _STD format(_STATICALLY_WIDEN(_CharT, "{:%Y} is not a valid year"), _Val));
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday& _Val) {
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%a}"), _Val)
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} is not a valid weekday"),
_Val.c_encoding()));
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday_indexed& _Val) {
const auto _Idx = _Val.index();
return _Os << (_Idx >= 1 && _Idx <= 5
? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[{}]"), _Val.weekday(), _Idx)
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[{} is not a valid index]"),
_Val.weekday(), _Idx));
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday_last& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[last]"), _Val.weekday());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_day& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{}"), _Val.month(), _Val.day());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_day_last& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/last"), _Val.month());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_weekday& _Val) {
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.month(), _Val.weekday_indexed());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_weekday_last& _Val) {
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.month(), _Val.weekday_last());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}"), _Val.year(), _Val.month());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month_day& _Val) {
return _Os << (_Val.ok() ? _STD format(_STATICALLY_WIDEN(_CharT, "{:%F}"), _Val)
: _STD format(_STATICALLY_WIDEN(_CharT, "{:%F} is not a valid date"), _Val));
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month_day_last& _Val) {
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}"), _Val.year(), _Val.month_day_last());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month_weekday& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}/{:L}"), _Val.year(), _Val.month(),
_Val.weekday_indexed());
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(
basic_ostream<_CharT, _Traits>& _Os, const year_month_weekday_last& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}/{:L}"), _Val.year(), _Val.month(),
_Val.weekday_last());
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const hh_mm_ss<_Duration>& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%T}"), _Val);
}
#pragma warning(push)
#pragma warning(disable : 4365) // 'argument': conversion from 'char' to 'const wchar_t', signed/unsigned mismatch
template <class _CharT>
_NODISCARD decltype(auto) _Widen_string(const string& _Str) {
if constexpr (is_same_v<_CharT, char>) {
return _Str;
} else {
return wstring{_Str.begin(), _Str.end()}; // TRANSITION, should probably use ctype::widen
}
}
#pragma warning(pop)
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const sys_info& _Val) {
return _Os << _STD format(_Os.getloc(),
_STATICALLY_WIDEN(_CharT, "begin: {}, end: {}, offset: {}, save: {}, abbrev: {}"), //
_Val.begin, _Val.end, _Val.offset, _Val.save, _Widen_string<_CharT>(_Val.abbrev));
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const local_info& _Val) {
switch (_Val.result) {
case local_info::unique:
return _Os << _STD format(_Os.getloc(), //
_STATICALLY_WIDEN(_CharT, "result: unique, first: ({})"), //
_Val.first);
case local_info::nonexistent:
return _Os << _STD format(_Os.getloc(),
_STATICALLY_WIDEN(_CharT, "result: nonexistent, first: ({}), second: ({})"), //
_Val.first, _Val.second);
case local_info::ambiguous:
return _Os << _STD format(_Os.getloc(),
_STATICALLY_WIDEN(_CharT, "result: ambiguous, first: ({}), second: ({})"), //
_Val.first, _Val.second);
default:
return _Os << _STD format(_Os.getloc(), //
_STATICALLY_WIDEN(_CharT, "result: {}, first: ({}), second: ({})"), //
_Val.result, _Val.first, _Val.second);
}
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration>
requires (!treat_as_floating_point_v<typename _Duration::rep> && _Duration{1} < days{1})
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const sys_time<_Duration>& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
_EXPORT_STD template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const sys_days& _Val) {
return _Os << year_month_day{_Val};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const utc_time<_Duration>& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const tai_time<_Duration>& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const gps_time<_Duration>& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const file_time<_Duration>& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const local_time<_Duration>& _Val) {
return _Os << sys_time<_Duration>{_Val.time_since_epoch()};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Duration, class _TimeZonePtr>
basic_ostream<_CharT, _Traits>& operator<<(
basic_ostream<_CharT, _Traits>& _Os, const zoned_time<_Duration, _TimeZonePtr>& _Val) {
const auto _Info = _Val.get_info();
return _Os << _Local_time_format_t<_Duration>{_Val.get_local_time(), &_Info.abbrev};
}
template <class _CharT>
_NODISCARD const _CharT* _Fmt_string(const _Chrono_spec<_CharT>& _Spec, _CharT (&_Fmt_str)[4]) {
size_t _Next_idx = 0;
_Fmt_str[_Next_idx++] = _CharT{'%'};
if (_Spec._Modifier != '\0') {
_Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Modifier);
}
_Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Type);
_Fmt_str[_Next_idx] = _CharT{'\0'};
return _Fmt_str;
}
template <class _CharT>
struct _Chrono_formatter {
_Chrono_formatter() = default;
constexpr explicit _Chrono_formatter(const basic_string_view<_CharT> _Time_zone_abbreviation_)
: _Time_zone_abbreviation{_Time_zone_abbreviation_} {}
template <class _Ty>
_NODISCARD constexpr auto _Parse(basic_format_parse_context<_CharT>& _Parse_ctx) {
_Chrono_specs_setter<_CharT, basic_format_parse_context<_CharT>> _Callback{_Specs, _Parse_ctx};
const auto _It =
_Parse_chrono_format_specs(_Parse_ctx._Unchecked_begin(), _Parse_ctx._Unchecked_end(), _Callback);
const auto _Res_iter = _Parse_ctx.begin() + (_It - _Parse_ctx._Unchecked_begin());
if (_It != _Parse_ctx._Unchecked_end() && *_It != '}') {
_Throw_format_error("Missing '}' in format string.");
}
if constexpr (_Is_specialization_v<_Ty, duration>) {
if constexpr (!treat_as_floating_point_v<typename _Ty::rep>) {
if (_Specs._Precision != -1) {
_Throw_format_error("Precision specification invalid for chrono::duration type with "
"integral representation type, see N4950 [time.format]/1.");
}
}
} else {
if (_Specs._Precision != -1) {
_Throw_format_error("Precision specification invalid for non-chrono::duration type, "
"see N4950 [time.format]/1.");
}
}
for (const auto& _Spec : _Specs._Chrono_specs_list) {
if (_Spec._Type != '\0' && !_Is_valid_type<_Ty>(_Spec._Type)) {
_Throw_format_error("Invalid type.");
}
_Check_modifier(_Spec._Type, _Spec._Modifier);
}
return _Res_iter;
}
enum _Allowed_bit : uint8_t { _E_mod = 1, _O_mod = 2, _EO_mod = _E_mod | _O_mod };
struct _Table_entry {
char _Type;
_Allowed_bit _Allowed;
};
static constexpr _Table_entry _Table[] = {
{'c', _E_mod},
{'C', _E_mod},
{'d', _O_mod},
{'e', _O_mod},
{'H', _O_mod},
{'I', _O_mod},
{'m', _O_mod},
{'M', _O_mod},
{'S', _O_mod},
{'u', _O_mod},
{'U', _O_mod},
{'V', _O_mod},
{'w', _O_mod},
{'W', _O_mod},
{'x', _E_mod},
{'X', _E_mod},
{'y', _EO_mod},
{'Y', _E_mod},
{'z', _EO_mod},
};
static constexpr void _Check_modifier(const char _Type, const char _Modifier) {
if (_Modifier == '\0') {
return;
}
const _Allowed_bit _Mod = _Modifier == 'E' ? _E_mod : _O_mod;
#ifdef _M_CEE // TRANSITION, VSO-1664341
constexpr auto _Get_table_entry_type = [](const _Table_entry& _Entry) { return _Entry._Type; };
#else // ^^^ workaround / no workaround vvv
constexpr auto _Get_table_entry_type = &_Table_entry::_Type;
#endif // ^^^ no workaround ^^^
if (auto _It = _RANGES find(_Table, _Type, _Get_table_entry_type); _It != _STD end(_Table)) {
if (_It->_Allowed & _Mod) {
return;
}
}
_Throw_format_error("Incompatible modifier for type");
}
template <class _Ty>
_NODISCARD static constexpr bool _Is_valid_type(const char _Type) noexcept {
if constexpr (_Is_specialization_v<_Ty, duration>) {
return _Type == 'j' || _Type == 'q' || _Type == 'Q' || _Is_valid_type<hh_mm_ss<seconds>>(_Type);
} else if constexpr (is_same_v<_Ty, day>) {
return _Type == 'd' || _Type == 'e';
} else if constexpr (is_same_v<_Ty, month>) {
return _Type == 'b' || _Type == 'B' || _Type == 'h' || _Type == 'm';
} else if constexpr (is_same_v<_Ty, year>) {
return _Type == 'Y' || _Type == 'y' || _Type == 'C';
} else if constexpr (_Is_any_of_v<_Ty, weekday, weekday_indexed, weekday_last>) {
return _Type == 'a' || _Type == 'A' || _Type == 'u' || _Type == 'w';
} else if constexpr (_Is_any_of_v<_Ty, month_day, month_day_last>) {
return _Type == 'j' || _Is_valid_type<month>(_Type) || _Is_valid_type<day>(_Type);
} else if constexpr (_Is_any_of_v<_Ty, month_weekday, month_weekday_last>) {
return _Is_valid_type<month>(_Type) || _Is_valid_type<weekday>(_Type);
} else if constexpr (is_same_v<_Ty, year_month>) {
return _Type == 'g' || _Type == 'G' || _Is_valid_type<year>(_Type) || _Is_valid_type<month>(_Type);
} else if constexpr (_Is_any_of_v<_Ty, year_month_day, year_month_day_last, year_month_weekday,
year_month_weekday_last>) {
return _Type == 'D' || _Type == 'F' || _Type == 'g' || _Type == 'G' || _Type == 'j' || _Type == 'U'
|| _Type == 'V' || _Type == 'W' || _Type == 'x' || _Is_valid_type<year>(_Type)
|| _Is_valid_type<month>(_Type) || _Is_valid_type<day>(_Type) || _Is_valid_type<weekday>(_Type);
} else if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) {
return _Type == 'H' || _Type == 'I' || _Type == 'M' || _Type == 'S' || _Type == 'r' || _Type == 'R'
|| _Type == 'T' || _Type == 'p' || _Type == 'X';
} else if constexpr (_Is_any_of_v<_Ty, sys_info, local_info>) {
return _Type == 'z' || _Type == 'Z';
} else if constexpr (_Is_specialization_v<_Ty, time_point>) {
if constexpr (!is_same_v<typename _Ty::clock, local_t>) {
if (_Type == 'z' || _Type == 'Z') {
return true;
}
}
return _Type == 'c' || _Is_valid_type<year_month_day>(_Type)
|| _Is_valid_type<hh_mm_ss<seconds>>(_Type);
} else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) {
return _Type == 'z' || _Type == 'Z' || _Is_valid_type<decltype(_Ty::_Time)>(_Type);
} else {
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected type
}
}
template <class _FormatContext, class _Ty>
_NODISCARD auto _Write(_FormatContext& _FormatCtx, const _Ty& _Val, const tm& _Time) const {
basic_ostringstream<_CharT> _Stream;
_Stream.imbue(_Specs._Localized ? _FormatCtx.locale() : locale::classic());
if (_Specs._Chrono_specs_list.empty()) {
_Stream << _Val; // N4950 [time.format]/7
} else {
if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) {
if (_Val.is_negative()) {
_Stream << _CharT{'-'};
}
} else if constexpr (_Is_specialization_v<_Ty, duration>) {
if (_Val < _Ty::zero()) {
_Stream << _CharT{'-'};
}
}
for (const auto& _Spec : _Specs._Chrono_specs_list) {
if (_Spec._Lit_char != _CharT{'\0'}) {
_Stream << _Spec._Lit_char;
continue;
}
// We need to manually do certain writes, either because the specification is different from
// put_time or custom logic is needed.
if (_Custom_write(_Stream, _Spec, _Time, _Val)) {
continue;
}
_Validate_specifiers(_Spec, _Val);
#if defined(_MSVC_EXECUTION_CHARACTER_SET) && _MSVC_EXECUTION_CHARACTER_SET == 65001 // TRANSITION, VSO-1468747 (EDG)
if constexpr (is_same_v<_CharT, char>) {
if (_Specs._Localized) {
wostringstream _Wstream;
_Wstream.imbue(_FormatCtx.locale());
wchar_t _Fmt_str[4];
_Chrono_spec<wchar_t> _Wspec{._Modifier = _Spec._Modifier, ._Type = _Spec._Type};
_Wstream << _STD put_time<wchar_t>(&_Time, _Fmt_string(_Wspec, _Fmt_str));
_Stream << _Convert_wide_to_narrow<char_traits<char>>(
__std_code_page::_Utf8, _Wstream.view(), allocator<char>{});
continue;
}
}
#endif // ^^^ no workaround ^^^
_CharT _Fmt_str[4];
_Stream << _STD put_time<_CharT>(&_Time, _Fmt_string(_Spec, _Fmt_str));
}
}
int _Estimated_width = -1;
(void) _Measure_string_prefix(_Stream.view(), _Estimated_width);
auto _Format_specs = _Specs;
if (_Specs._Dynamic_width_index >= 0) {
_Format_specs._Width = _Get_dynamic_specs<_Width_checker>(
_FormatCtx.arg(static_cast<size_t>(_Specs._Dynamic_width_index)));
}
return _Write_aligned(_STD move(_FormatCtx.out()), _Estimated_width, _Format_specs, _Fmt_align::_Left,
[&](auto _Out) { return _Fmt_write(_STD move(_Out), _Stream.view()); });
}
// This echoes the functionality of put_time, but is able to handle invalid dates (when !ok()) since the
// Standard mandates that invalid dates still be formatted properly. For example, put_time isn't able to handle
// a tm_mday of 40, but format("{:%d}", day{40}) should return "40" and operator<< for day prints
// "40 is not a valid day".
template <class _Ty>
bool _Custom_write(
basic_ostream<_CharT>& _Os, const _Chrono_spec<_CharT>& _Spec, const tm& _Time, const _Ty& _Val) const {
if constexpr (is_same_v<_Ty, local_info>) {
if (_Val.result != local_info::unique) {
_Throw_format_error("Cannot print non-unique local_info");
}
}
const auto _Year = _Time.tm_year + 1900;
const auto _Month = _Time.tm_mon + 1;
const bool _Has_modifier = _Spec._Modifier != '\0';
switch (_Spec._Type) {
case 'd': // Print days as a decimal, even if invalid.
case 'e':
// Most months have a proper last day, but February depends on the year.
if constexpr (is_same_v<_Ty, month_day_last>) {
if (_Val.month() == February) {
_Throw_format_error("Cannot print the last day of February without a year");
}
if (!_Val.ok()) {
return false;
}
}
if (_Has_modifier) {
return false;
}
if (_Time.tm_mday < 10) {
_Os << (_Spec._Type == 'd' ? _CharT{'0'} : _CharT{' '});
}
_Os << _Time.tm_mday;
return true;
case 'g':
case 'G':
if constexpr (is_same_v<_Ty, year_month>) {
if (_Val.month() == January || _Val.month() == December) {
_Throw_format_error(
"The ISO week-based year for a year_month of January or December is ambiguous.");
}
if (!_Val.ok()) {
_Throw_format_error("The ISO week-based year for an out-of-bounds year_month is ambiguous.");
}
const char _Gregorian_type = _Spec._Type == 'g' ? 'y' : 'Y';
_CharT _Fmt_str[4];
_Os << _STD put_time(&_Time, _Fmt_string({._Type = _Gregorian_type}, _Fmt_str));
return true;
} else {
return false;
}
case 'r':
// put_time uses _Strftime in order to bypass reference-counting that locale uses. This function
// takes the locale information by pointer, but the pointer (from _Gettnames) returns a copy.
// _Strftime delegates to other functions but eventually (for the C locale) has the %r specifier
// rewritten. It checks for the locale by comparing pointers, which do not compare equal as we have
// a copy of the pointer instead of the original. Therefore, we replace %r for the C locale
// ourselves.
if (_Os.getloc() == locale::classic()) {
_Os << _STD put_time(&_Time, _STATICALLY_WIDEN(_CharT, "%I:%M:%S %p"));
return true;
}
return false;
case 'j':
if constexpr (_Is_specialization_v<_Ty, duration>) {
_Os << _STD abs(_Duration_cast_underflow_to_zero<days>(_Val).count());
return true;
} else {
return false;
}
case 'q':
if constexpr (_Is_specialization_v<_Ty, duration>) {
_Write_unit_suffix<typename _Ty::period>(_Os);
} else {
_STL_INTERNAL_CHECK(false);
}
return true;
case 'Q':
if constexpr (_Is_specialization_v<_Ty, duration>) {
_Os << _STD abs(_Val.count());
} else {
_STL_INTERNAL_CHECK(false);
}
return true;
case 'm': // Print months as a decimal, even if invalid.
if (_Has_modifier) {
return false;
}
if (_Month < 10) {
_Os << _CharT{'0'};
}
_Os << _Month;
return true;
case 'Y': // Print years as a decimal, even if invalid.
if (_Has_modifier) {
return false;
}
if (_Year < 0) {
_Os << _CharT{'-'};
}
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:04}"), _STD abs(_Year));
return true;
case 'y': // Print the two-digit year as a decimal, even if invalid.
if (_Has_modifier) {
return false;
}
_Os << _STD format(
_STATICALLY_WIDEN(_CharT, "{:02}"), _Time_parse_fields::_Decompose_year(_Year).second);
return true;
case 'C': // Print the century as a decimal, even if invalid.
if (_Has_modifier) {
return false;
}
if (_Year < 0) {
_Os << _CharT{'-'};
}
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:02}"),
_STD abs(_Time_parse_fields::_Decompose_year(_Year).first) / 100);
return true;
case 'F': // Print YMD even if invalid.
_Custom_write(_Os, {._Type = 'Y'}, _Time, _Val);
_Os << _CharT{'-'};
_Custom_write(_Os, {._Type = 'm'}, _Time, _Val);
_Os << _CharT{'-'};
_Custom_write(_Os, {._Type = 'd'}, _Time, _Val);
return true;
case 'D': // Print YMD even if invalid.
_Custom_write(_Os, {._Type = 'm'}, _Time, _Val);
_Os << _CharT{'/'};
_Custom_write(_Os, {._Type = 'd'}, _Time, _Val);
_Os << _CharT{'/'};
_Custom_write(_Os, {._Type = 'y'}, _Time, _Val);
return true;
case 'H':
// Print hour even if >= 24.
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:02}"), _Time.tm_hour);
return true;
case 'T':
// Alias for %H:%M:%S but we allow hours > 23 and rewrite %S to display fractions of a second.
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:02}:{:02}:"), _Time.tm_hour, _Time.tm_min);
[[fallthrough]];
case 'S':
if (_Has_modifier) {
return false;
}
_Write_seconds(_Os, _Val);
return true;
case 'Z':
if constexpr (is_same_v<_Ty, sys_info>) {
_Os << _Widen_string<_CharT>(_Val.abbrev);
} else if constexpr (is_same_v<_Ty, local_info>) {
_Os << _Widen_string<_CharT>(_Val.first.abbrev);
} else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) {
if (_Val._Abbrev == nullptr) {
_Throw_format_error("Cannot print local-time-format-t with null abbrev.");
}
_Os << _Widen_string<_CharT>(*_Val._Abbrev);
} else {
_Os << _Time_zone_abbreviation;
}
return true;
case 'z':
{
hh_mm_ss<seconds> _Offset;
if constexpr (is_same_v<_Ty, sys_info>) {
_Offset = hh_mm_ss<seconds>{_Val.offset};
} else if constexpr (is_same_v<_Ty, local_info>) {
_Offset = hh_mm_ss<seconds>{_Val.first.offset};
} else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) {
if (_Val._Offset_sec == nullptr) {
_Throw_format_error("Cannot print local-time-format-t with null offset_sec.");
}
_Offset = hh_mm_ss<seconds>{*_Val._Offset_sec};
} else {
_Offset = hh_mm_ss<seconds>{};
}
const auto _Sign = _Offset.is_negative() ? _CharT{'-'} : _CharT{'+'};
const auto _Separator =
_Has_modifier ? _STATICALLY_WIDEN(_CharT, ":") : _STATICALLY_WIDEN(_CharT, "");
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{}{:02}{}{:02}"), _Sign, _Offset.hours().count(),
_Separator, _Offset.minutes().count());
return true;
}
default:
return false;
}
}
template <class _Ty>
static void _Validate_specifiers(const _Chrono_spec<_CharT>& _Spec, const _Ty& _Val) {
if constexpr (_Is_specialization_v<_Ty, duration> || is_same_v<_Ty, sys_info>
|| _Is_specialization_v<_Ty, time_point> || _Is_specialization_v<_Ty, _Local_time_format_t>
|| _Is_specialization_v<_Ty, hh_mm_ss>) {
return;
}
const auto _Validate = [&] {
constexpr bool _Is_ymd =
_Is_any_of_v<_Ty, year_month_day, year_month_day_last, year_month_weekday, year_month_weekday_last>;
switch (_Spec._Type) {
case 'a':
case 'A':
case 'u':
case 'w':
if constexpr (_Is_any_of_v<_Ty, weekday, weekday_last>) {
return _Val.ok();
} else if constexpr (_Is_any_of_v<_Ty, weekday_indexed, year_month_weekday,
year_month_weekday_last>) {
return _Val.weekday().ok();
} else if constexpr (is_same_v<_Ty, month_weekday>) {
return _Val.weekday_indexed().weekday().ok();
} else if constexpr (is_same_v<_Ty, month_weekday_last>) {
return _Val.weekday_last().ok();
} else if constexpr (_Is_any_of_v<_Ty, year_month_day, year_month_day_last>) {
return _Val.ok();
}
break;
case 'b':
case 'B':
case 'h':
case 'm':
if constexpr (is_same_v<_Ty, month>) {
return _Val.ok();
} else if constexpr (_Is_any_of_v<_Ty, month_day, month_day_last, month_weekday, month_weekday_last,
year_month>
|| _Is_ymd) {
return _Val.month().ok();
}
break;
case 'C':
case 'y':
case 'Y':
if constexpr (is_same_v<_Ty, year>) {
return _Val.ok();
} else if constexpr (is_same_v<_Ty, year_month> || _Is_ymd) {
return _Val.year().ok();
}
break;
case 'd':
case 'e':
if constexpr (_Is_any_of_v<_Ty, day, month_day_last>) {
return _Val.ok();
} else if constexpr (is_same_v<_Ty, month_day>) {
return _Val.day().ok();
} else if constexpr (_Is_ymd) {
const year_month_day& _Ymd{_Val};
return _Ymd.day().ok();
}
break;
case 'D':
case 'F':
if constexpr (_Has_ok<_Ty>) {
return _Val.ok();
}
break;
case 'j':
if constexpr (is_same_v<_Ty, month_day>) {
if (_Val.month() > February) {
_Throw_format_error("The day of year for a month_day past February is ambiguous.");
}
return true;
} else if constexpr (is_same_v<_Ty, month_day_last>) {
if (_Val.month() >= February) {
_Throw_format_error("The day of year for a month_day_last other than January is ambiguous");
}
return true;
} else if constexpr (_Is_ymd) {
return _Val.ok();
}
break;
case 'g':
case 'G':
case 'U':
case 'V':
case 'W':
if constexpr (_Is_ymd) {
return _Val.ok();
}
break;
default:
if constexpr (_Has_ok<_Ty>) {
return _Val.ok();
}
return true;
}
_STL_INTERNAL_CHECK(false);
return false;
};
if (!_Validate()) {
_Throw_format_error("Cannot localize out-of-bounds time point.");
}
}
_Chrono_format_specs<_CharT> _Specs{};
basic_string_view<_CharT> _Time_zone_abbreviation{};
};
} // namespace chrono
template <class _Ty, class _CharT>
struct _Fill_tm_formatter {
constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) {
return _Impl.template _Parse<_Ty>(_Parse_ctx);
}
template <class _FormatContext>
auto format(const _Ty& _Val, _FormatContext& _FormatCtx) const {
return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Val));
}
private:
_CHRONO _Chrono_formatter<_CharT> _Impl;
};
// Per LWG-3997, `_CharT` in library-provided `formatter` specializations is
// constrained to character types supported by `format`.
template <class _Rep, class _Period, _Format_supported_charT _CharT>
struct formatter<_CHRONO duration<_Rep, _Period>, _CharT>
: _Fill_tm_formatter<_CHRONO duration<_Rep, _Period>, _CharT> {};
#if _HAS_CXX23
template <class _Rep, class _Period>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _Period>> =
enable_nonlocking_formatter_optimization<_Rep>;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO day, _CharT> : _Fill_tm_formatter<_CHRONO day, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO day> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO month, _CharT> : _Fill_tm_formatter<_CHRONO month, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO year, _CharT> : _Fill_tm_formatter<_CHRONO year, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO weekday, _CharT> : _Fill_tm_formatter<_CHRONO weekday, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO weekday> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO weekday_indexed, _CharT> : _Fill_tm_formatter<_CHRONO weekday_indexed, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO weekday_indexed> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO weekday_last, _CharT> : _Fill_tm_formatter<_CHRONO weekday_last, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO weekday_last> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO month_day, _CharT> : _Fill_tm_formatter<_CHRONO month_day, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_day> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO month_day_last, _CharT> : _Fill_tm_formatter<_CHRONO month_day_last, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_day_last> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO month_weekday, _CharT> : _Fill_tm_formatter<_CHRONO month_weekday, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_weekday> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO month_weekday_last, _CharT> : _Fill_tm_formatter<_CHRONO month_weekday_last, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_weekday_last> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO year_month, _CharT> : _Fill_tm_formatter<_CHRONO year_month, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO year_month_day, _CharT> : _Fill_tm_formatter<_CHRONO year_month_day, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_day> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO year_month_day_last, _CharT> : _Fill_tm_formatter<_CHRONO year_month_day_last, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_day_last> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO year_month_weekday, _CharT> : _Fill_tm_formatter<_CHRONO year_month_weekday, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_weekday> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO year_month_weekday_last, _CharT>
: _Fill_tm_formatter<_CHRONO year_month_weekday_last, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_weekday_last> = true;
#endif // _HAS_CXX23
template <class _Rep, class _Period, _Format_supported_charT _CharT>
struct formatter<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>, _CharT>
: _Fill_tm_formatter<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>, _CharT> {};
#if _HAS_CXX23
template <class _Rep, class _Period>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO sys_info, _CharT> : _Fill_tm_formatter<_CHRONO sys_info, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO sys_info> = true;
#endif // _HAS_CXX23
template <_Format_supported_charT _CharT>
struct formatter<_CHRONO local_info, _CharT> : _Fill_tm_formatter<_CHRONO local_info, _CharT> {};
#if _HAS_CXX23
template <>
inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO local_info> = true;
#endif // _HAS_CXX23
template <class _Duration, _Format_supported_charT _CharT>
struct formatter<_CHRONO sys_time<_Duration>, _CharT> {
constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) {
return _Impl.template _Parse<_CHRONO sys_time<_Duration>>(_Parse_ctx);
}
template <class _FormatContext>
auto format(const _CHRONO sys_time<_Duration>& _Val, _FormatContext& _FormatCtx) const {
return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Val));
}
private:
_CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "UTC")};
};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO sys_time<_Duration>> = true;
#endif // _HAS_CXX23
template <class _Duration, _Format_supported_charT _CharT>
struct formatter<_CHRONO utc_time<_Duration>, _CharT> {
constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) {
return _Impl.template _Parse<_CHRONO utc_time<_Duration>>(_Parse_ctx);
}
template <class _FormatContext>
auto format(const _CHRONO utc_time<_Duration>& _Val, _FormatContext& _FormatCtx) const {
const auto _Sys = _CHRONO utc_clock::to_sys(_Val);
return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Sys));
}
private:
_CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "UTC")};
};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO utc_time<_Duration>> = true;
#endif // _HAS_CXX23
template <class _Duration, _Format_supported_charT _CharT>
struct formatter<_CHRONO tai_time<_Duration>, _CharT> {
constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) {
return _Impl.template _Parse<_CHRONO tai_time<_Duration>>(_Parse_ctx);
}
template <class _FormatContext>
auto format(const _CHRONO tai_time<_Duration>& _Val, _FormatContext& _FormatCtx) const {
using namespace chrono;
using _Common = common_type_t<_Duration, days>; // slightly optimize by performing conversion at compile time
constexpr _Common _Offset{sys_days{year{1970} / January / 1} - sys_days{year{1958} / January / 1}};
const auto _Sys = sys_time<_Duration>{_Val.time_since_epoch()} - _Offset;
return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Sys));
}
private:
_CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "TAI")};
};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO tai_time<_Duration>> = true;
#endif // _HAS_CXX23
template <class _Duration, _Format_supported_charT _CharT>
struct formatter<_CHRONO gps_time<_Duration>, _CharT> {
constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) {
return _Impl.template _Parse<_CHRONO gps_time<_Duration>>(_Parse_ctx);
}
template <class _FormatContext>
auto format(const _CHRONO gps_time<_Duration>& _Val, _FormatContext& _FormatCtx) const {
using namespace chrono;
using _Common = common_type_t<_Duration, days>; // slightly optimize by performing conversion at compile time
constexpr _Common _Offset{sys_days{year{1980} / January / Sunday[1]} - sys_days{year{1970} / January / 1}};
const auto _Sys = sys_time<_Duration>{_Val.time_since_epoch()} + _Offset;
return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Sys));
}
private:
_CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "GPS")};
};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO gps_time<_Duration>> = true;
#endif // _HAS_CXX23
template <class _Duration, _Format_supported_charT _CharT>
struct formatter<_CHRONO file_time<_Duration>, _CharT> {
constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) {
return _Impl.template _Parse<_CHRONO file_time<_Duration>>(_Parse_ctx);
}
template <class _FormatContext>
auto format(const _CHRONO file_time<_Duration>& _Val, _FormatContext& _FormatCtx) const {
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));
}
private:
_CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "UTC")};
};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO file_time<_Duration>> = true;
#endif // _HAS_CXX23
template <class _Duration, _Format_supported_charT _CharT>
struct formatter<_CHRONO local_time<_Duration>, _CharT> : _Fill_tm_formatter<_CHRONO local_time<_Duration>, _CharT> {};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO local_time<_Duration>> = true;
#endif // _HAS_CXX23
template <class _Duration, _Format_supported_charT _CharT>
struct formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT>
: _Fill_tm_formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT> {};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO _Local_time_format_t<_Duration>> = true;
#endif // _HAS_CXX23
template <class _Duration, class _TimeZonePtr, _Format_supported_charT _CharT>
struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT>
: formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT> {
template <class _FormatContext>
auto format(const _CHRONO zoned_time<_Duration, _TimeZonePtr>& _Val, _FormatContext& _FormatCtx) const {
using _Mybase = formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT>;
const auto _Info = _Val.get_info();
return _Mybase::format({_Val.get_local_time(), &_Info.abbrev, &_Info.offset}, _FormatCtx);
}
};
#if _HAS_CXX23
template <class _Duration>
constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const _CHRONO time_zone*>> = true;
#endif // _HAS_CXX23
namespace chrono {
template <class _Duration>
_NODISCARD string nonexistent_local_time::_Make_string(const local_time<_Duration>& _Tp, const local_info& _Info) {
ostringstream _Os;
_Os << _Tp << " is in a gap between\n"
<< local_seconds{_Info.first.end.time_since_epoch()} + _Info.first.offset << ' ' << _Info.first.abbrev
<< " and\n"
<< local_seconds{_Info.second.begin.time_since_epoch()} + _Info.second.offset << ' ' << _Info.second.abbrev
<< " which are both equivalent to\n"
<< _Info.first.end << " UTC";
return _STD move(_Os).str();
}
template <class _Duration>
_NODISCARD string ambiguous_local_time::_Make_string(const local_time<_Duration>& _Tp, const local_info& _Info) {
ostringstream _Os;
_Os << _Tp << " is ambiguous. It could be\n"
<< _Tp << ' ' << _Info.first.abbrev << " == " << _Tp - _Info.first.offset << " UTC or\n"
<< _Tp << ' ' << _Info.second.abbrev << " == " << _Tp - _Info.second.offset << " UTC";
return _STD move(_Os).str();
}
} // namespace chrono
inline namespace literals {
inline namespace chrono_literals {
_EXPORT_STD _NODISCARD constexpr _CHRONO day operator""d(unsigned long long _Day) noexcept {
return _CHRONO day{static_cast<unsigned int>(_Day)};
}
_EXPORT_STD _NODISCARD constexpr _CHRONO year operator""y(unsigned long long _Year) noexcept {
return _CHRONO year{static_cast<int>(_Year)};
}
} // namespace chrono_literals
} // namespace literals
#endif // _HAS_CXX20
_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _CHRONO_