зеркало из https://github.com/microsoft/STL.git
1261 строка
48 KiB
C++
1261 строка
48 KiB
C++
// 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 %[E0Q#] 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 %[E0Q#] 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 %[E0Q#] 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_
|