// xloctime internal header

// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef _XLOCTIME_
#define _XLOCTIME_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#include <ctime>
#include <iterator>
#include <xlocnum>

#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
template <class _InIt, class _Elem>
ios_base::iostate _Getint_v2(_InIt& _First, _InIt& _Last, int _Lo, int _Hi, int& _Val, int& _Digits_read,
    const ctype<_Elem>& _Ctype_fac) { // get integer in range [_Lo, _Hi] from [_First, _Last)
    _STL_INTERNAL_CHECK(0 <= _Hi && _Hi <= 9999);
    const int _Hi_digits = (_Hi <= 9 ? 1 : _Hi <= 99 ? 2 : _Hi <= 999 ? 3 : 4);
    char _Ac[_Max_int_dig];
    char* _Ep;
    char* _Ptr = _Ac;
    char _Ch;

    _Digits_read = 0;

    while (_First != _Last && _Digits_read < _Hi_digits && _Ctype_fac.is(ctype_base::space, *_First)) {
        ++_First;
        ++_Digits_read;
    }

    if (_First != _Last && _Digits_read < _Hi_digits) {
        if ((_Ch = _Ctype_fac.narrow(*_First)) == '+') { // copy plus sign
            *_Ptr++ = '+';
            ++_First;
        } else if (_Ch == '-') { // copy minus sign
            *_Ptr++ = '-';
            ++_First;
        }
    }

    for (; _First != _Last && _Digits_read < _Hi_digits && _Ctype_fac.narrow(*_First) == '0'; ++_First) {
        ++_Digits_read; // strip leading zeros
    }

    if (_Digits_read > 0) {
        *_Ptr++ = '0'; // replace one or more with single zero
    }

    for (char* const _Pe = &_Ac[_Max_int_dig - 1];
        _First != _Last && '0' <= (_Ch = _Ctype_fac.narrow(*_First)) && _Ch <= '9' && _Digits_read < _Hi_digits;
        ++_Digits_read, (void) ++_First) { // copy digits
        *_Ptr = _Ch;
        if (_Ptr < _Pe) {
            ++_Ptr; // drop trailing digits if already too large
        }
    }

    if (_Digits_read == 0) {
        _Ptr = _Ac;
    }

    *_Ptr                    = '\0';
    int _Errno               = 0;
    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 || _Ans < _Lo || _Hi < _Ans) {
        _State |= ios_base::failbit; // bad conversion
    } else {
        _Val = _Ans; // store valid result
    }

    return _State;
}

_EXPORT_STD extern "C++" struct _CRTIMP2_PURE_IMPORT time_base // base class for time_get
    : locale::facet // TRANSITION, ABI, shouldn't be derived from locale::facet
{
    enum dateorder { // constants for different orders of date components
        no_order,
        dmy,
        mdy,
        ymd,
        ydm
    };

    __CLR_OR_THIS_CALL time_base(size_t _Refs = 0) noexcept // strengthened
        : locale::facet(_Refs) {}

    __CLR_OR_THIS_CALL ~time_base() noexcept override {}
};

_EXPORT_STD extern "C++" template <class _Elem, class _InIt = istreambuf_iterator<_Elem, char_traits<_Elem>>>
class time_get : public time_base { // facet for converting text to encoded times
private:
    friend _Tidy_guard<time_get>;

public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t>, _FACET_SPECIALIZATION_MESSAGE);

    using char_type = _Elem;
    using iter_type = _InIt;
    using _Ctype    = ctype<_Elem>;

    __PURE_APPDOMAIN_GLOBAL static locale::id id; // unique facet id

    dateorder __CLR_OR_THIS_CALL date_order() const {
        return do_date_order();
    }

    _InIt __CLR_OR_THIS_CALL get_time(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        tm* _Pt) const { // get time of day from [_First, _Last) into _Pt
        return do_get_time(_First, _Last, _Iosbase, _State, _Pt);
    }

    _InIt __CLR_OR_THIS_CALL get_date(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        tm* _Pt) const { // get date from [_First, _Last) into _Pt
        return do_get_date(_First, _Last, _Iosbase, _State, _Pt);
    }

    _InIt __CLR_OR_THIS_CALL get_weekday(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        tm* _Pt) const { // get weekday from [_First, _Last) into _Pt
        return do_get_weekday(_First, _Last, _Iosbase, _State, _Pt);
    }

    _InIt __CLR_OR_THIS_CALL get_monthname(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        tm* _Pt) const { // get month from [_First, _Last) into _Pt
        return do_get_monthname(_First, _Last, _Iosbase, _State, _Pt);
    }

    _InIt __CLR_OR_THIS_CALL get_year(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        tm* _Pt) const { // get year from [_First, _Last) into _Pt
        return do_get_year(_First, _Last, _Iosbase, _State, _Pt);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State, tm* _Pt,
        char _Specifier, char _Modifier = '\0') const { // get formatted time for _Specifier/_Modifier
        return do_get(_First, _Last, _Iosbase, _State, _Pt, _Specifier, _Modifier);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State, tm* _Pt,
        const _Elem* _Fmtfirst, const _Elem* _Fmtlast) const { // get formatted time for format string
        _Adl_verify_range(_Fmtfirst, _Fmtlast);
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        _State = ios_base::goodbit;

        for (; _Fmtfirst != _Fmtlast; ++_Fmtfirst) {
            if (_State != ios_base::goodbit) {
                // N4950 [locale.time.get.members]/8.2
                // _State is fail, eof, or bad. Do not proceed to the next fields. Return with current _State.
                break;
            } else if (_First == _Last) {
                // N4950 [locale.time.get.members]/8.3
                _State = ios_base::eofbit | ios_base::failbit;
                break;
            } else if (_Ctype_fac.narrow(*_Fmtfirst) != '%') { // match literal element
                if (_Ctype_fac.is(_Ctype::space, *_Fmtfirst)) {
                    while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) {
                        ++_First;
                    }
                } else if (_Ctype_fac.tolower(*_First) != _Ctype_fac.tolower(*_Fmtfirst)) { // bad literal match
                    _State |= ios_base::failbit;
                    break;
                } else {
                    ++_First;
                }
            } else if (++_Fmtfirst == _Fmtlast) {
                // N4950 [locale.time.get.members]/8.4: "If the number of elements in the range [fmt, fmtend) is not
                // sufficient to unambiguously determine whether the conversion specification is complete and valid,
                // the function evaluates err = ios_base::failbit."
                _State = ios_base::failbit;
                break;
            } else { // get specifier after %
                char _Specifier = _Ctype_fac.narrow(*_Fmtfirst);
                char _Modifier  = '\0';

                if (_Specifier == 'E' || _Specifier == 'O' || _Specifier == 'Q' || _Specifier == '#') {
                    if (++_Fmtfirst == _Fmtlast) { // no specifier
                        _State = ios_base::failbit;
                        break;
                    } else { // save both qualifier and specifier
                        _Modifier  = _Specifier;
                        _Specifier = _Ctype_fac.narrow(*_Fmtfirst);
                    }
                }

                _First = do_get(_First, _Last, _Iosbase, _State, _Pt, _Specifier, _Modifier); // convert a single field
            }
        }

        return _First;
    }

    explicit __CLR_OR_THIS_CALL time_get(size_t _Refs = 0) : time_base(_Refs) { // construct from current locale
        _BEGIN_LOCINFO(_Lobj)
        _Init(_Lobj);
        _END_LOCINFO()
    }

    __CLR_OR_THIS_CALL time_get(const _Locinfo& _Lobj, size_t _Refs = 0) : time_base(_Refs) {
        _Init(_Lobj);
    }

    static size_t __CLRCALL_OR_CDECL _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) {
        // return locale category mask and construct standard facet
        if (_Ppf && !*_Ppf) {
            *_Ppf = new time_get<_Elem, _InIt>(_Locinfo(_Ploc->_C_str()));
        }

        return _X_TIME;
    }

protected:
    __CLR_OR_THIS_CALL ~time_get() noexcept override {
        _Tidy();
    }

    __CLR_OR_THIS_CALL time_get(const char* _Locname, size_t _Refs = 0) : time_base(_Refs) {
        _BEGIN_LOCINFO(_Lobj(_Locname))
        _Init(_Lobj);
        _END_LOCINFO()
    }

    template <class _Elem2>
    void __CLR_OR_THIS_CALL _Getvals(_Elem2, const _Locinfo& _Lobj) { // get values
        _Cvt = _Lobj._Getcvt();

        if constexpr (is_same_v<_Elem2, wchar_t>) {
            _Days = reinterpret_cast<const _Elem*>(_Maklocwcs(reinterpret_cast<const wchar_t*>(_Lobj._W_Getdays())));
            _Months =
                reinterpret_cast<const _Elem*>(_Maklocwcs(reinterpret_cast<const wchar_t*>(_Lobj._W_Getmonths())));
            _Ampm = reinterpret_cast<const _Elem*>(_Maklocwcs(L":AM:am:PM:pm"));
        } else {
            _Days   = _Maklocstr(_Lobj._Getdays(), static_cast<_Elem*>(nullptr), _Cvt);
            _Months = _Maklocstr(_Lobj._Getmonths(), static_cast<_Elem*>(nullptr), _Cvt);
            _Ampm   = _Maklocstr(":AM:am:PM:pm", static_cast<_Elem*>(nullptr), _Cvt);
        }
    }

    void __CLR_OR_THIS_CALL _Init(const _Locinfo& _Lobj) { // initialize from _Lobj
        _Days   = nullptr;
        _Months = nullptr;
        _Ampm   = nullptr;

        _Tidy_guard<time_get> _Guard{this};
        _Getvals(_Elem{}, _Lobj);
        _Dateorder     = static_cast<dateorder>(_Lobj._Getdateorder());
        _Guard._Target = nullptr;
    }

    virtual dateorder __CLR_OR_THIS_CALL do_date_order() const {
        return _Dateorder;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get_time(_InIt _First, _InIt _Last, ios_base& _Iosbase,
        ios_base::iostate& _State, tm* _Pt) const { // get time of day from [_First, _Last) into _Pt
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        _State |= _Getint(_First, _Last, 0, 23, _Pt->tm_hour, _Ctype_fac);

        if (_State != ios_base::goodbit || _Ctype_fac.narrow(*_First) != ':') {
            _State |= ios_base::failbit; // hour field is bad
        } else {
            _State |= _Getint(++_First, _Last, 0, 59, _Pt->tm_min, _Ctype_fac);
        }

        if (_State != ios_base::goodbit || _Ctype_fac.narrow(*_First) != ':') {
            _State |= ios_base::failbit; // min field is bad
        } else {
            _State |= _Getint(++_First, _Last, 0, 60, _Pt->tm_sec, _Ctype_fac);
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get_date(_InIt _First, _InIt _Last, ios_base& _Iosbase,
        ios_base::iostate& _State, tm* _Pt) const { // get date from [_First, _Last) into _Pt
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        dateorder _Dorder = date_order();
        if (_Dorder == no_order) {
            _Dorder = mdy;
        }

        if (_First != _Last) {
            if (!_Ctype_fac.is(_Ctype::digit, *_First)) { // begins with month name, assume mdy
                _First  = get_monthname(_First, _Last, _Iosbase, _State, _Pt);
                _Dorder = mdy;
            } else if (_Dorder == mdy) { // get month number
                _State |= _Getint(_First, _Last, 1, 12, _Pt->tm_mon, _Ctype_fac);
                --_Pt->tm_mon;
            } else if (_Dorder == dmy) {
                _State |= _Getint(_First, _Last, 1, 31, _Pt->tm_mday, _Ctype_fac);
            } else { // ymd or ydm
                _First = get_year(_First, _Last, _Iosbase, _State, _Pt);
            }
        }

        while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) {
            ++_First; // skip spaces
        }

        if (_First != _Last) { // skip [:,/]
            char _Ch = _Ctype_fac.narrow(*_First);
            if (_Ch == ':' || _Ch == ',' || _Ch == '/') {
                ++_First;
            }
        }

        while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) {
            ++_First; // skip spaces
        }

        if (_First != _Last) {
            if (!_Ctype_fac.is(_Ctype::digit, *_First)) {
                if (_Dorder == mdy) {
                    _State |= ios_base::failbit; // error, month already seen
                } else { // month name is second, like it or not
                    _First = get_monthname(_First, _Last, _Iosbase, _State, _Pt);
                    if (_Dorder == ydm) {
                        _Dorder = ymd;
                    }
                }
            } else if (_Dorder == dmy || _Dorder == ymd) { // get month number
                _State |= _Getint(_First, _Last, 1, 12, _Pt->tm_mon, _Ctype_fac);
                --_Pt->tm_mon;
            } else {
                _State |= _Getint(_First, _Last, 1, 31, _Pt->tm_mday, _Ctype_fac);
            }
        }

        while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) {
            ++_First; // skip spaces
        }

        if (_First != _Last) { // skip [:,/]
            char _Ch = _Ctype_fac.narrow(*_First);
            if (_Ch == ':' || _Ch == ',' || _Ch == '/') {
                ++_First;
            }
        }

        while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) {
            ++_First; // skip spaces
        }

        if (_First == _Last) {
            _State |= ios_base::failbit; // error, missing component(s)
        } else if (!_Ctype_fac.is(_Ctype::digit, *_First)) {
            if (_Dorder != ydm) {
                _State |= ios_base::failbit; // error, month out of place
            } else {
                _First = get_monthname(_First, _Last, _Iosbase, _State, _Pt);
            }
        } else if (_Dorder == ydm) { // get month number
            _State |= _Getint(_First, _Last, 1, 12, _Pt->tm_mon, _Ctype_fac);
            --_Pt->tm_mon;
        } else if (_Dorder == ymd) {
            _State |= _Getint(_First, _Last, 1, 31, _Pt->tm_mday, _Ctype_fac);
        } else { // mdy or dmy
            _First = get_year(_First, _Last, _Iosbase, _State, _Pt);
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get_weekday(_InIt _First, _InIt _Last, ios_base&, ios_base::iostate& _State,
        tm* _Pt) const { // get weekday from [_First, _Last) into _Pt
        int _Num = _Getloctxt(_First, _Last, 0, _Days, _Case_sensitive::_Nope);
        if (_Num < 0) {
            _State |= ios_base::failbit;
        } else {
            _Pt->tm_wday = _Num >> 1; // set wday
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get_monthname(_InIt _First, _InIt _Last, ios_base&, ios_base::iostate& _State,
        tm* _Pt) const { // get month from [_First, _Last) into _Pt
        int _Num = _Getloctxt(_First, _Last, 0, _Months, _Case_sensitive::_Nope);

        if (_Num < 0) {
            _State |= ios_base::failbit;
        } else {
            _Pt->tm_mon = _Num >> 1; // set mon
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get_year(_InIt _First, _InIt _Last, ios_base& _Iosbase,
        ios_base::iostate& _State, tm* _Pt) const { // get year from [_First, _Last) into _Pt
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        int _Ans = 0;
        int _Digits_read;
        ios_base::iostate _Res = _Getint_v2(_First, _Last, 0, 9999, _Ans, _Digits_read, _Ctype_fac);

        _State |= _Res; // pass on eofbit and failbit
        if (!(_Res & ios_base::failbit)) {
            if (_Digits_read <= 2) {
                if (_Ans < 69) {
                    _Pt->tm_year = _Ans + 100; // [00, 68] parsed as [2000, 2068]
                } else if (_Ans < 100) {
                    _Pt->tm_year = _Ans; // [69, 99] parsed as [1969, 1999]
                }
            } else {
                _Pt->tm_year = _Ans - 1900; // parsed literally
            }
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        tm* _Pt, char _Specifier, char = 0) const { // get formatted time for _Specifier (_Modifier ignored)
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());
        int _Ans                 = 0;

        _State = ios_base::goodbit;

        switch (_Specifier) { // process format specifier
        case 'a':
        case 'A':
            _First = get_weekday(_First, _Last, _Iosbase, _State, _Pt);
            break;

        case 'b':
        case 'B':
        case 'h':
            _First = get_monthname(_First, _Last, _Iosbase, _State, _Pt);
            break;

        case 'c':
            _First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%b %d %H : %M : %S %Y");
            break;

        case 'C':
            _State |= _Getint(_First, _Last, 0, 99, _Ans, _Ctype_fac);
            if (!(_State & ios_base::failbit)) {
                _Pt->tm_year = _Ans * 100 - 1900; // convert to century
            }

            break;

        case 'd':
        case 'e':
            _State |= _Getint(_First, _Last, 1, 31, _Pt->tm_mday, _Ctype_fac);
            break;

        case 'D':
            _First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%m / %d / %y");
            break;

        case 'H':
            _State |= _Getint(_First, _Last, 0, 23, _Pt->tm_hour, _Ctype_fac);
            break;

        case 'I':
            _State |= _Getint(_First, _Last, 1, 12, _Ans, _Ctype_fac);
            if (!(_State & ios_base::failbit)) {
                _Pt->tm_hour = _Ans == 12 ? 0 : _Ans;
            }

            break;

        case 'j':
            _State |= _Getint(_First, _Last, 1, 366, _Pt->tm_yday, _Ctype_fac);
            break;

        case 'm':
            _State |= _Getint(_First, _Last, 1, 12, _Ans, _Ctype_fac);
            if (!(_State & ios_base::failbit)) {
                _Pt->tm_mon = _Ans - 1;
            }

            break;

        case 'M':
            _State |= _Getint(_First, _Last, 0, 59, _Pt->tm_min, _Ctype_fac);
            break;

        case 'n':
        case 't':
            _First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, " ");
            break;

        case 'p':
            _Ans = _Getloctxt(_First, _Last, 0, ":AM:am:PM:pm", _Case_sensitive::_Nope);
            if (_Ans < 0) {
                _State |= ios_base::failbit;
            } else if (1 < _Ans) {
                _Pt->tm_hour += 12;
            }

            break;

        case 'r':
            _First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%I : %M : %S %p");
            break;

        case 'R':
            _First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%H : %M");
            break;

        case 'S':
            _State |= _Getint(_First, _Last, 0, 60, _Pt->tm_sec, _Ctype_fac);
            break;

        case 'T':
        case 'X':
            _First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%H : %M : %S");
            break;

        case 'U':
            _State |= _Getint(_First, _Last, 0, 53, _Pt->tm_yday, _Ctype_fac);
            break;

        case 'w':
            _State |= _Getint(_First, _Last, 0, 6, _Pt->tm_wday, _Ctype_fac);
            break;

        case 'W':
            _State |= _Getint(_First, _Last, 0, 53, _Pt->tm_yday, _Ctype_fac);
            break;

        case 'x':
            _First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%d / %m / %y");
            break;

        case 'y':
            _State |= _Getint(_First, _Last, 0, 99, _Ans, _Ctype_fac);
            if (!(_State & ios_base::failbit)) {
                _Pt->tm_year = _Ans < 69 ? _Ans + 100 : _Ans;
            }

            break;

        case 'Y':
            _State |= _Getint(_First, _Last, 0, 9999, _Ans, _Ctype_fac);
            if (!(_State & ios_base::failbit)) {
                _Pt->tm_year = _Ans - 1900;
            }
            break;

        default:
            _State |= ios_base::failbit; // unknown specifier
            break;
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    _InIt __CLR_OR_THIS_CALL _Getfmt(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State, tm* _Pt,
        const char* _Fmtfirst) const { // get formatted time for format NTBS
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        for (; *_Fmtfirst != '\0'; ++_Fmtfirst) {
            if (_First == _Last) {
                _State |= ios_base::failbit;
                break;
            } else if (*_Fmtfirst == '%') {
                _First = do_get(_First, _Last, _Iosbase, _State, _Pt,
                    *++_Fmtfirst); // convert a single field
            } else if (*_Fmtfirst == ' ') {
                while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) {
                    ++_First;
                }
            } else if (_Ctype_fac.narrow(*_First) != *_Fmtfirst) { // bad literal match
                _State |= ios_base::failbit;
                break;
            } else {
                ++_First;
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

private:
    ios_base::iostate __CLRCALL_OR_CDECL _Getint(_InIt& _First, _InIt& _Last, int _Lo, int _Hi, int& _Val,
        const _Ctype& _Ctype_fac) const { // get integer in range [_Lo, _Hi] from [_First, _Last)
        // TRANSITION, ABI
        int _Digits_read;
        return _Getint_v2(_First, _Last, _Lo, _Hi, _Val, _Digits_read, _Ctype_fac);
    }

    void __CLR_OR_THIS_CALL _Tidy() noexcept { // free all storage
        _CSTD free(const_cast<_Elem*>(_Days));
        _CSTD free(const_cast<_Elem*>(_Months));
        _CSTD free(const_cast<_Elem*>(_Ampm));
    }

    const _Elem* _Days; // ":Sun:Sunday:Mon:Monday..." for example
    const _Elem* _Months; // "Jan:January:Feb:February..." for example
    const _Elem* _Ampm; // ":AM:am:PM:pm"
    dateorder _Dateorder;
    _Locinfo::_Cvtvec _Cvt; // conversion information
};

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdllimport-static-field-def"
#endif // defined(__clang__)

template <class _Elem, class _InIt>
__PURE_APPDOMAIN_GLOBAL locale::id time_get<_Elem, _InIt>::id;

#ifdef __clang__
#pragma clang diagnostic pop
#endif // defined(__clang__)

_EXPORT_STD template <class _Elem, class _InIt = istreambuf_iterator<_Elem, char_traits<_Elem>>>
class time_get_byname : public time_get<_Elem, _InIt> { // time_get for named locale
public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t>, _FACET_SPECIALIZATION_MESSAGE);

    explicit time_get_byname(const char* _Locname, size_t _Refs = 0)
        : time_get<_Elem, _InIt>(_Locname, _Refs) {} // construct for named locale

    explicit time_get_byname(const string& _Str, size_t _Refs = 0)
        : time_get<_Elem, _InIt>(_Locinfo(_Str.c_str()), _Refs) {} // construct for named locale

protected:
    __CLR_OR_THIS_CALL ~time_get_byname() noexcept override {}
};

// C23 7.29.3.5 "The strftime function"/3
_INLINE_VAR constexpr char _Valid_strftime_specifiers[] = {'a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'F', 'g', 'G',
    'h', 'H', 'I', 'j', 'm', 'M', 'n', 'p', 'r', 'R', 'S', 't', 'T', 'u', 'U', 'V', 'w', 'W', 'x', 'X', 'y', 'Y', 'z',
    'Z'};

_NODISCARD constexpr bool _Is_valid_strftime_specifier(const char _Specifier) {
    for (const auto& _Valid_specifier : _Valid_strftime_specifiers) {
        if (_Specifier == _Valid_specifier) {
            return true;
        }
    }

    return false;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_sec(const tm* const _Pt) noexcept {
    // seconds after the minute - [0, 60] including leap second
    return _Pt->tm_sec >= 0 && _Pt->tm_sec <= 60;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_min(const tm* const _Pt) noexcept {
    // minutes after the hour - [0, 59]
    return _Pt->tm_min >= 0 && _Pt->tm_min <= 59;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_hour(const tm* const _Pt) noexcept {
    // hours since midnight - [0, 23]
    return _Pt->tm_hour >= 0 && _Pt->tm_hour <= 23;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_mday(const tm* const _Pt) noexcept {
    // day of the month - [1, 31]
    return _Pt->tm_mday >= 1 && _Pt->tm_mday <= 31;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_mon(const tm* const _Pt) noexcept {
    // months since January - [0, 11]
    return _Pt->tm_mon >= 0 && _Pt->tm_mon <= 11;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_year(const tm* const _Pt) noexcept {
    // years since 1900 - UCRT max range is up until 8099
    return _Pt->tm_year >= -1900 && _Pt->tm_year <= 8099;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_wday(const tm* const _Pt) noexcept {
    // days since Sunday - [0, 6]
    return _Pt->tm_wday >= 0 && _Pt->tm_wday <= 6;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_yday(const tm* const _Pt) noexcept {
    // days since January 1 - [0, 365]
    return _Pt->tm_yday >= 0 && _Pt->tm_yday <= 365;
}

_NODISCARD constexpr bool _Is_valid_strftime_tm_data(const char _Specifier, const tm* const _Pt) noexcept {
    if (!_Pt) {
        return false;
    }

    switch (_Specifier) {
    case 'S':
        return _Is_valid_strftime_tm_sec(_Pt);
    case 'M':
        return _Is_valid_strftime_tm_min(_Pt);
    case 'H':
    case 'I':
    case 'p':
        return _Is_valid_strftime_tm_hour(_Pt);
    case 'd':
    case 'e':
        return _Is_valid_strftime_tm_mday(_Pt);
    case 'b':
    case 'B':
    case 'm':
    case 'h':
        return _Is_valid_strftime_tm_mon(_Pt);
    case 'C':
    case 'y':
    case 'Y':
        return _Is_valid_strftime_tm_year(_Pt);
    case 'j':
        return _Is_valid_strftime_tm_yday(_Pt);
    case 'a':
    case 'A':
    case 'u':
    case 'w':
        return _Is_valid_strftime_tm_wday(_Pt);
    case 'U': // C23 7.29.3.5 "The strftime function"/3 says that %U and %W depend on tm_year,
    case 'W': // but the UCRT neither uses nor validates it.
        return _Is_valid_strftime_tm_wday(_Pt) && _Is_valid_strftime_tm_yday(_Pt);
    case 'R':
        return _Is_valid_strftime_tm_hour(_Pt) && _Is_valid_strftime_tm_min(_Pt);
    case 'D':
    case 'x':
    case 'F':
        return _Is_valid_strftime_tm_year(_Pt) && _Is_valid_strftime_tm_mon(_Pt) && _Is_valid_strftime_tm_mday(_Pt);
    case 'g':
    case 'G':
        return _Is_valid_strftime_tm_year(_Pt) && _Is_valid_strftime_tm_wday(_Pt) && _Is_valid_strftime_tm_yday(_Pt);
    case 'r':
    case 'X':
    case 'T':
        return _Is_valid_strftime_tm_hour(_Pt) && _Is_valid_strftime_tm_min(_Pt) && _Is_valid_strftime_tm_sec(_Pt);
    case 'c':
        return _Is_valid_strftime_tm_wday(_Pt) && _Is_valid_strftime_tm_mon(_Pt) && _Is_valid_strftime_tm_mday(_Pt)
            && _Is_valid_strftime_tm_hour(_Pt) && _Is_valid_strftime_tm_min(_Pt) && _Is_valid_strftime_tm_sec(_Pt)
            && _Is_valid_strftime_tm_year(_Pt);
    case 'V': // C23 7.29.3.5 "The strftime function"/3 says that %V depends on tm_year, tm_wday, and tm_yday.
              // The UCRT uses them without validating them.
        return true;
    case 'z': // C23 7.29.3.5 "The strftime function"/3 says that %z and %Z depend on tm_isdst.
    case 'Z': // The UCRT treats it as a boolean value, so there's no need for validation.
        return true;
    case 'n': // newline
    case 't': // tab
        return true;
    default:
        // We should have handled %% and called _Is_valid_strftime_specifier() before calling this function.
        _STL_INTERNAL_CHECK(false);
        return false;
    }
}

_EXPORT_STD extern "C++" template <class _Elem, class _OutIt = ostreambuf_iterator<_Elem, char_traits<_Elem>>>
class time_put : public locale::facet { // facet for converting encoded times to text
public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t>, _FACET_SPECIALIZATION_MESSAGE);

    using char_type = _Elem;
    using iter_type = _OutIt;
    using _Ctype    = ctype<_Elem>;

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const tm* _Pt, const _Elem* _Fmtfirst,
        const _Elem* _Fmtlast) const { // put formatted time from _Pt to _Dest for [_Fmtfirst, _Fmtlast)
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        for (; _Fmtfirst != _Fmtlast; ++_Fmtfirst) {
            if (_Ctype_fac.narrow(*_Fmtfirst) != '%') {
                *_Dest++ = *_Fmtfirst; // copy literal element
            } else if (++_Fmtfirst == _Fmtlast) { // treat trailing % as %%
                *_Dest++ = _Fmtfirst[-1];
                break;
            } else { // get specifier after %
                char _Specifier = _Ctype_fac.narrow(*_Fmtfirst);
                char _Modifier  = '\0';
                _Elem _Percent  = _Fmtfirst[-1];

                if (_Specifier == 'E' || _Specifier == 'O' || _Specifier == 'Q' || _Specifier == '#') {
                    if (++_Fmtfirst == _Fmtlast) { // no specifier, copy %[EOQ#] as literal elements
                        *_Dest++ = _Percent;
                        *_Dest++ = _Specifier;
                        break;
                    }

                    // save both qualifier and specifier
                    _Modifier  = _Specifier;
                    _Specifier = _Ctype_fac.narrow(*_Fmtfirst);
                }

                if (_Specifier == '%' && _Modifier == '\0') {
                    // if the specifier is percent and no modifier is set, just append it
                    *_Dest++ = _Percent;
                } else if (!_Is_valid_strftime_specifier(_Specifier)) {
                    // no valid specifier, directly copy as literal elements
                    *_Dest++ = _Percent;
                    if (_Modifier != '\0') {
                        *_Dest++ = _Modifier;
                    }
                    *_Dest++ = _Specifier;
                } else {
                    if (_Is_valid_strftime_tm_data(_Specifier, _Pt)) {
                        _Dest = do_put(_Dest, _Iosbase, _Fill, _Pt, _Specifier, _Modifier); // convert a single field
                    } else {
                        *_Dest++ = _Elem('?');
                    }
                }
            }
        }

        return _Dest;
    }

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const tm* _Pt, char _Specifier,
        char _Modifier = '\0') const { // put formatted time from _Pt to _Dest for _Specifier/_Modifier
        return do_put(_Dest, _Iosbase, _Fill, _Pt, _Specifier, _Modifier);
    }

    __PURE_APPDOMAIN_GLOBAL static locale::id id; // unique facet id

    explicit __CLR_OR_THIS_CALL time_put(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale
        _BEGIN_LOCINFO(_Lobj)
        _Init(_Lobj);
        _END_LOCINFO()
    }

    __CLR_OR_THIS_CALL time_put(const _Locinfo& _Lobj, size_t _Refs = 0) : locale::facet(_Refs) {
        _Init(_Lobj);
    }

    static size_t __CLRCALL_OR_CDECL _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) {
        // return locale category mask and construct standard facet
        if (_Ppf && !*_Ppf) {
            *_Ppf = new time_put<_Elem, _OutIt>(_Locinfo(_Ploc->_C_str()));
        }

        return _X_TIME;
    }

protected:
    __CLR_OR_THIS_CALL ~time_put() noexcept override {}

    void __CLR_OR_THIS_CALL _Init(const _Locinfo& _Lobj) { // initialize from _Lobj
        _Tnames = _Lobj._Gettnames();
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(_OutIt _Dest, ios_base& _Iosbase, _Elem, const tm* _Pt, char _Specifier,
        char _Modifier = '\0') const { // put formatted time from _Pt to _Dest for [_Fmtfirst, _Fmtlast)
        char _Fmt[5] = "!%x\0"; // '!' for nonzero count, null for modifier
        size_t _Count;
        size_t _Num;
        string _Str;

        if (_Modifier == '\0') {
            _Fmt[2] = _Specifier;
        } else { // add both modifier and specifier
            _Fmt[2] = _Modifier;
            _Fmt[3] = _Specifier;
        }

        int& _Errno_ref      = errno; // Nonzero cost, pay it once
        const int _Old_errno = _Errno_ref;

        for (_Num = 16;; _Num *= 2) { // convert into ever larger string buffer until success
            _Str.append(_Num, '\0');
            _Count = _Strftime(&_Str[0], _Str.size(), _Fmt, _Pt, _Tnames._Getptr());
            if (0 < _Count) {
                break;
            } else if (_Errno_ref == EINVAL) {
                _Iosbase.setstate(ios_base::badbit);
                return _Dest;
            }
        }

        _Errno_ref = _Old_errno;
        return _STD copy(&_Str[1], &_Str[_Count], _Dest);
    }

private:
    _Locinfo::_Timevec _Tnames; // locale-specific stuff for _Strftime
};

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdllimport-static-field-def"
#endif // defined(__clang__)

template <class _Elem, class _OutIt>
__PURE_APPDOMAIN_GLOBAL locale::id time_put<_Elem, _OutIt>::id;

#ifdef __clang__
#pragma clang diagnostic pop
#endif // defined(__clang__)

extern "C++" template <class _OutIt>
class time_put<wchar_t, _OutIt> : public locale::facet { // facet for converting encoded times to wchar_t text
public:
    using _Elem     = wchar_t;
    using char_type = _Elem;
    using iter_type = _OutIt;
    using _Ctype    = ctype<_Elem>;

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const tm* _Pt, const _Elem* _Fmtfirst,
        const _Elem* _Fmtlast) const { // put formatted time from _Pt to _Dest for [_Fmtfirst, _Fmtlast)
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        for (; _Fmtfirst != _Fmtlast; ++_Fmtfirst) {
            if (_Ctype_fac.narrow(*_Fmtfirst) != '%') {
                *_Dest++ = *_Fmtfirst; // copy literal element
            } else if (++_Fmtfirst == _Fmtlast) { // treat trailing % as %%
                *_Dest++ = _Fmtfirst[-1];
                break;
            } else { // get specifier after %
                _Elem _Raw      = *_Fmtfirst;
                char _Specifier = _Ctype_fac.narrow(_Raw);
                char _Modifier  = '\0';
                _Elem _Percent  = _Fmtfirst[-1];

                if (_Specifier == 'E' || _Specifier == 'O' || _Specifier == 'Q' || _Specifier == '#') {
                    if (++_Fmtfirst == _Fmtlast) { // no specifier, copy %[EOQ#] as literal elements
                        *_Dest++ = _Percent;
                        *_Dest++ = _Raw;
                        break;
                    }

                    // save both qualifier and specifier
                    _Modifier  = _Specifier;
                    _Specifier = _Ctype_fac.narrow(*_Fmtfirst);
                }

                if (_Specifier == '%' && _Modifier == '\0') {
                    // if the specifier is percent and no modifier is set, just append it
                    *_Dest++ = _Percent;
                } else if (!_Is_valid_strftime_specifier(_Specifier)) {
                    // no valid specifier, directly copy as literal elements
                    *_Dest++ = _Percent;
                    if (_Modifier != '\0') {
                        *_Dest++ = _Raw;
                    }
                    *_Dest++ = *_Fmtfirst;
                } else {
                    if (_Is_valid_strftime_tm_data(_Specifier, _Pt)) {
                        _Dest = do_put(_Dest, _Iosbase, _Fill, _Pt, _Specifier, _Modifier); // convert a single field
                    } else {
                        *_Dest++ = _Elem('?');
                    }
                }
            }
        }

        return _Dest;
    }

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const tm* _Pt, char _Specifier,
        char _Modifier = '\0') const { // put formatted time from _Pt to _Dest for _Specifier/_Modifier
        return do_put(_Dest, _Iosbase, _Fill, _Pt, _Specifier, _Modifier);
    }

    __PURE_APPDOMAIN_GLOBAL static locale::id id; // unique facet id

    explicit __CLR_OR_THIS_CALL time_put(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale
        _BEGIN_LOCINFO(_Lobj)
        _Init(_Lobj);
        _END_LOCINFO()
    }

    __CLR_OR_THIS_CALL time_put(const _Locinfo& _Lobj, size_t _Refs = 0) : locale::facet(_Refs) {
        _Init(_Lobj);
    }

    static size_t __CLRCALL_OR_CDECL _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) {
        // return locale category mask and construct standard facet
        if (_Ppf && !*_Ppf) {
            *_Ppf = new time_put<_Elem, _OutIt>(_Locinfo(_Ploc->_C_str()));
        }

        return _X_TIME;
    }

protected:
    __CLR_OR_THIS_CALL ~time_put() noexcept override {}

    __CLR_OR_THIS_CALL time_put(const char* _Locname, size_t _Refs = 0) : locale::facet(_Refs) {
        _BEGIN_LOCINFO(_Lobj(_Locname))
        _Init(_Lobj);
        _END_LOCINFO()
    }

    void __CLR_OR_THIS_CALL _Init(const _Locinfo& _Lobj) { // initialize from _Lobj
        _Tnames = _Lobj._W_Gettnames();
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(_OutIt _Dest, ios_base& _Iosbase, _Elem, const tm* _Pt, char _Specifier,
        char _Modifier = '\0') const { // put formatted time from _Pt to _Dest for [_Fmtfirst, _Fmtlast)
        wchar_t _Fmt[5] = L"!%x\0"; // ! for nonzero count, null for modifier
        size_t _Count;
        size_t _Num;
        wstring _Str;

        if (_Modifier == '\0') {
            _Fmt[2] = static_cast<_Elem>(_Specifier); // conversion rule unspecified
        } else { // add both modifier and specifier
            _Fmt[2] = static_cast<_Elem>(_Modifier);
            _Fmt[3] = static_cast<_Elem>(_Specifier);
        }

        int& _Errno_ref      = errno; // Nonzero cost, pay it once
        const int _Old_errno = _Errno_ref;

        for (_Num = 16;; _Num *= 2) { // convert into ever larger string buffer until success
            _Str.append(_Num, '\0');
            _Count = _Wcsftime(&_Str[0], _Str.size(), _Fmt, _Pt, _Tnames._Getptr());
            if (0 < _Count) {
                break;
            } else if (_Errno_ref == EINVAL) {
                _Iosbase.setstate(ios_base::badbit);
                return _Dest;
            }
        }

        _Errno_ref = _Old_errno;
        return _STD copy(&_Str[1], &_Str[_Count], _Dest);
    }

private:
    _Locinfo::_Timevec _Tnames; // locale-specific stuff for _Wcsftime
};

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdllimport-static-field-def"
#endif // defined(__clang__)

template <class _OutIt>
__PURE_APPDOMAIN_GLOBAL locale::id time_put<wchar_t, _OutIt>::id;

#ifdef __clang__
#pragma clang diagnostic pop
#endif // defined(__clang__)

#if defined(_CRTBLD)

extern "C++" template <class _OutIt>
class time_put<unsigned short, _OutIt>
    : public locale::facet { // facet for converting encoded times to unsigned short text
public:
    using _Elem     = unsigned short;
    using char_type = _Elem;
    using iter_type = _OutIt;
    using _Ctype    = ctype<_Elem>;

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const tm* _Pt, const _Elem* _Fmtfirst,
        const _Elem* _Fmtlast) const { // put formatted time from _Pt to _Dest for [_Fmtfirst, _Fmtlast)
        const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());

        for (; _Fmtfirst != _Fmtlast; ++_Fmtfirst) {
            if (_Ctype_fac.narrow(*_Fmtfirst) != '%') {
                *_Dest++ = *_Fmtfirst; // copy literal element
            } else if (++_Fmtfirst == _Fmtlast) { // treat trailing % as %%
                *_Dest++ = _Fmtfirst[-1];
                break;
            } else { // get specifier after %
                _Elem _Raw      = *_Fmtfirst;
                char _Specifier = _Ctype_fac.narrow(_Raw);
                char _Modifier  = '\0';
                _Elem _Percent  = _Fmtfirst[-1];

                if (_Specifier == 'E' || _Specifier == 'O' || _Specifier == 'Q' || _Specifier == '#') {
                    if (++_Fmtfirst == _Fmtlast) { // no specifier, copy %[EOQ#] as literal elements
                        *_Dest++ = _Percent;
                        *_Dest++ = _Raw;
                        break;
                    }

                    // save both qualifier and specifier
                    _Modifier  = _Specifier;
                    _Specifier = _Ctype_fac.narrow(*_Fmtfirst);
                }

                if (_Specifier == '%' && _Modifier == '\0') {
                    // if the specifier is percent and no modifier is set, just append it
                    *_Dest++ = _Percent;
                } else if (!_Is_valid_strftime_specifier(_Specifier)) {
                    // no valid specifier, directly copy as literal elements
                    *_Dest++ = _Percent;
                    if (_Modifier != '\0') {
                        *_Dest++ = _Raw;
                    }
                    *_Dest++ = *_Fmtfirst;
                } else {
                    if (_Is_valid_strftime_tm_data(_Specifier, _Pt)) {
                        _Dest = do_put(_Dest, _Iosbase, _Fill, _Pt, _Specifier, _Modifier); // convert a single field
                    } else {
                        *_Dest++ = _Elem('?');
                    }
                }
            }
        }

        return _Dest;
    }

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const tm* _Pt, char _Specifier,
        char _Modifier = '\0') const { // put formatted time from _Pt to _Dest for _Specifier/_Modifier
        return do_put(_Dest, _Iosbase, _Fill, _Pt, _Specifier, _Modifier);
    }

    __PURE_APPDOMAIN_GLOBAL static locale::id id; // unique facet id

    explicit __CLR_OR_THIS_CALL time_put(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale
        _BEGIN_LOCINFO(_Lobj)
        _Init(_Lobj);
        _END_LOCINFO()
    }

    __CLR_OR_THIS_CALL time_put(const _Locinfo& _Lobj, size_t _Refs = 0) : locale::facet(_Refs) {
        _Init(_Lobj);
    }

    static size_t __CLRCALL_OR_CDECL _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) {
        // return locale category mask and construct standard facet
        if (_Ppf && !*_Ppf) {
            *_Ppf = new time_put<_Elem, _OutIt>(_Locinfo(_Ploc->_C_str()));
        }

        return _X_TIME;
    }

protected:
    __CLR_OR_THIS_CALL ~time_put() noexcept override {}

    __CLR_OR_THIS_CALL time_put(const char* _Locname, size_t _Refs = 0) : locale::facet(_Refs) {
        _BEGIN_LOCINFO(_Lobj(_Locname))
        _Init(_Lobj);
        _END_LOCINFO()
    }

    void __CLR_OR_THIS_CALL _Init(const _Locinfo& _Lobj) { // initialize from _Lobj
        _Tnames = _Lobj._W_Gettnames();
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(_OutIt _Dest, ios_base& _Iosbase, _Elem, const tm* _Pt, char _Specifier,
        char _Modifier = '\0') const { // put formatted time from _Pt to _Dest for [_Fmtfirst, _Fmtlast)
        wchar_t _Fmt[5] = L"!%x\0"; // ! for nonzero count, null for modifier
        size_t _Count;
        size_t _Num;
        wstring _Str;

        if (_Modifier == '\0') {
            _Fmt[2] = static_cast<_Elem>(_Specifier); // conversion rule unspecified
        } else { // add both modifier and specifier
            _Fmt[2] = static_cast<_Elem>(_Modifier);
            _Fmt[3] = static_cast<_Elem>(_Specifier);
        }

        int& _Errno_ref      = errno; // Nonzero cost, pay it once
        const int _Old_errno = _Errno_ref;

        for (_Num = 16;; _Num *= 2) { // convert into ever larger string buffer until success
            _Str.append(_Num, '\0');
            _Count = _Wcsftime(&_Str[0], _Str.size(), _Fmt, _Pt, _Tnames._Getptr());
            if (0 < _Count) {
                break;
            } else if (_Errno_ref == EINVAL) {
                _Iosbase.setstate(ios_base::badbit);
                return _Dest;
            }
        }

        _Errno_ref = _Old_errno;
        return _STD copy(&_Str[1], &_Str[_Count], _Dest);
    }

private:
    _Locinfo::_Timevec _Tnames; // locale-specific stuff for _Wcsftime
};

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdllimport-static-field-def"
#endif // defined(__clang__)

template <class _OutIt>
__PURE_APPDOMAIN_GLOBAL locale::id time_put<unsigned short, _OutIt>::id;

#ifdef __clang__
#pragma clang diagnostic pop
#endif // defined(__clang__)
#endif // defined(_CRTBLD)

_EXPORT_STD template <class _Elem, class _OutIt = ostreambuf_iterator<_Elem, char_traits<_Elem>>>
class time_put_byname : public time_put<_Elem, _OutIt> { // time_put for named locale
public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t>, _FACET_SPECIALIZATION_MESSAGE);

    explicit time_put_byname(const char* _Locname, size_t _Refs = 0)
        : time_put<_Elem, _OutIt>(_Locname, _Refs) {} // construct for named locale

    explicit time_put_byname(const string& _Str, size_t _Refs = 0)
        : time_put<_Elem, _OutIt>(_Str.c_str(), _Refs) {} // construct for named locale

protected:
    __CLR_OR_THIS_CALL ~time_put_byname() noexcept override {}
};

#if defined(_DLL_CPPLIB)

#if !defined(_CRTBLD) || defined(__FORCE_INSTANCE)
template class _CRTIMP2_PURE_IMPORT time_get<char, istreambuf_iterator<char, char_traits<char>>>;
template class _CRTIMP2_PURE_IMPORT time_put<char, ostreambuf_iterator<char, char_traits<char>>>;
template _CRTIMP2_PURE void __CLR_OR_THIS_CALL time_get<char, istreambuf_iterator<char, char_traits<char>>>::_Getvals(
    wchar_t, const _Locinfo&);

template class _CRTIMP2_PURE_IMPORT time_get<wchar_t, istreambuf_iterator<wchar_t, char_traits<wchar_t>>>;
template class _CRTIMP2_PURE_IMPORT time_put<wchar_t, ostreambuf_iterator<wchar_t, char_traits<wchar_t>>>;
template _CRTIMP2_PURE void __CLR_OR_THIS_CALL
    time_get<wchar_t, istreambuf_iterator<wchar_t, char_traits<wchar_t>>>::_Getvals(wchar_t, const _Locinfo&);
#endif // !defined(_CRTBLD) || defined(__FORCE_INSTANCE)

#ifdef __FORCE_INSTANCE
template class _CRTIMP2_PURE_IMPORT
    time_get<unsigned short, istreambuf_iterator<unsigned short, char_traits<unsigned short>>>;
template class _CRTIMP2_PURE_IMPORT
    time_put<unsigned short, ostreambuf_iterator<unsigned short, char_traits<unsigned short>>>;
template _CRTIMP2_PURE void __CLR_OR_THIS_CALL
    time_get<unsigned short, istreambuf_iterator<unsigned short, char_traits<unsigned short>>>::_Getvals(
        wchar_t, const _Locinfo&);
#endif // defined(__FORCE_INSTANCE)
#endif // defined(_DLL_CPPLIB)
_STD_END

#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _XLOCTIME_
