aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2023-07-21 17:19:08 +0100
committerJonathan Wakely <jwakely@redhat.com>2023-08-11 19:58:06 +0100
commitce6c4d3b4d336493cab128795467d832cb04c9ee (patch)
treefa0234779601b85d60d3003ecb8fd911f3b7fd46 /libstdc++-v3
parentf93a612fc4567652b75ffc916d31a446378e6613 (diff)
downloadgcc-ce6c4d3b4d336493cab128795467d832cb04c9ee.zip
gcc-ce6c4d3b4d336493cab128795467d832cb04c9ee.tar.gz
gcc-ce6c4d3b4d336493cab128795467d832cb04c9ee.tar.bz2
libstdc++: Implement C++20 std::chrono::parse [PR104167]
This adds the missing C++20 features to <chrono>. I've implemented my proposed resolutions to LWG issues 3960, 3961, and 3962. There are some unimplemented flags such as %OI which I think are not implementable in general. It might be possible to use na_llanginfo with ALT_DIGITS, but that isn't available on all targets. I intend to file another LWG issue about that. libstdc++-v3/ChangeLog: PR libstdc++/104167 * include/bits/chrono_io.h (operator|=, operator|): Add noexcept to _ChronoParts operators. (from_stream, parse): Define new functions. (__detail::_Parse, __detail::_Parser): New class templates. * include/std/chrono (__cpp_lib_chrono): Define to 201907L for C++20. * include/std/version (__cpp_lib_chrono): Likewise. * testsuite/20_util/duration/arithmetic/constexpr_c++17.cc: Adjust expected value of feature test macro. * testsuite/20_util/duration/io.cc: Test parsing. * testsuite/std/time/clock/file/io.cc: Likewise. * testsuite/std/time/clock/gps/io.cc: Likewise. * testsuite/std/time/clock/system/io.cc: Likewise. * testsuite/std/time/clock/tai/io.cc: Likewise. * testsuite/std/time/clock/utc/io.cc: Likewise. * testsuite/std/time/day/io.cc: Likewise. * testsuite/std/time/month/io.cc: Likewise. * testsuite/std/time/month_day/io.cc: Likewise. * testsuite/std/time/weekday/io.cc: Likewise. * testsuite/std/time/year/io.cc: Likewise. * testsuite/std/time/year_month/io.cc: Likewise. * testsuite/std/time/year_month_day/io.cc: Likewise. * testsuite/std/time/syn_c++20.cc: Check value of macro and for the existence of parse and from_stream in namespace chrono. * testsuite/std/time/clock/local/io.cc: New test. * testsuite/std/time/parse.cc: New test.
Diffstat (limited to 'libstdc++-v3')
-rw-r--r--libstdc++-v3/include/bits/chrono_io.h1691
-rw-r--r--libstdc++-v3/include/std/chrono5
-rw-r--r--libstdc++-v3/include/std/version4
-rw-r--r--libstdc++-v3/testsuite/20_util/duration/arithmetic/constexpr_c++17.cc2
-rw-r--r--libstdc++-v3/testsuite/20_util/duration/io.cc102
-rw-r--r--libstdc++-v3/testsuite/std/time/clock/file/io.cc18
-rw-r--r--libstdc++-v3/testsuite/std/time/clock/gps/io.cc22
-rw-r--r--libstdc++-v3/testsuite/std/time/clock/local/io.cc42
-rw-r--r--libstdc++-v3/testsuite/std/time/clock/system/io.cc73
-rw-r--r--libstdc++-v3/testsuite/std/time/clock/tai/io.cc22
-rw-r--r--libstdc++-v3/testsuite/std/time/clock/utc/io.cc31
-rw-r--r--libstdc++-v3/testsuite/std/time/day/io.cc60
-rw-r--r--libstdc++-v3/testsuite/std/time/month/io.cc122
-rw-r--r--libstdc++-v3/testsuite/std/time/month_day/io.cc79
-rw-r--r--libstdc++-v3/testsuite/std/time/parse.cc309
-rw-r--r--libstdc++-v3/testsuite/std/time/syn_c++20.cc9
-rw-r--r--libstdc++-v3/testsuite/std/time/weekday/io.cc78
-rw-r--r--libstdc++-v3/testsuite/std/time/year/io.cc74
-rw-r--r--libstdc++-v3/testsuite/std/time/year_month/io.cc50
-rw-r--r--libstdc++-v3/testsuite/std/time/year_month_day/io.cc65
20 files changed, 2816 insertions, 42 deletions
diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index c953013..84791d4 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -235,9 +235,13 @@ namespace __format
};
constexpr _ChronoParts
- operator|(_ChronoParts __x, _ChronoParts __y)
+ operator|(_ChronoParts __x, _ChronoParts __y) noexcept
{ return static_cast<_ChronoParts>((int)__x | (int)__y); }
+ constexpr _ChronoParts&
+ operator|=(_ChronoParts& __x, _ChronoParts __y) noexcept
+ { return __x = __x | __y; }
+
// TODO rename this to chrono::__formatter? or chrono::__detail::__formatter?
template<typename _CharT>
struct __formatter_chrono
@@ -2136,18 +2140,150 @@ namespace chrono
/// @addtogroup chrono
/// @{
- // TODO: from_stream for duration
-#if 0
+/// @cond undocumented
+namespace __detail
+{
+ template<typename _Duration = seconds>
+ struct _Parser
+ {
+ static_assert(is_same_v<common_type_t<_Duration, seconds>, _Duration>);
+
+ explicit
+ _Parser(__format::_ChronoParts __need) : _M_need(__need) { }
+
+ _Parser(_Parser&&) = delete;
+ void operator=(_Parser&&) = delete;
+
+ _Duration _M_time{}; // since midnight
+ sys_days _M_sys_days{};
+ year_month_day _M_ymd{};
+ weekday _M_wd{};
+ __format::_ChronoParts _M_need;
+
+ template<typename _CharT, typename _Traits, typename _Alloc>
+ basic_istream<_CharT, _Traits>&
+ operator()(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr);
+
+ private:
+ // Read an unsigned integer from the stream and return it.
+ // Extract no more than __n digits. Set failbit if an integer isn't read.
+ template<typename _CharT, typename _Traits>
+ static int_least32_t
+ _S_read_unsigned(basic_istream<_CharT, _Traits>& __is,
+ ios_base::iostate& __err, int __n)
+ {
+ int_least32_t __val = _S_try_read_digit(__is, __err);
+ if (__val == -1) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ int __n1 = (std::min)(__n, 9);
+ // Cannot overflow __val unless we read more than 9 digits
+ for (int __i = 1; __i < __n1; ++__i)
+ if (auto __dig = _S_try_read_digit(__is, __err); __dig != -1)
+ {
+ __val *= 10;
+ __val += __dig;
+ }
+
+ while (__n1++ < __n) [[unlikely]]
+ if (auto __dig = _S_try_read_digit(__is, __err); __dig != -1)
+ {
+ if (__builtin_mul_overflow(__val, 10, &__val)
+ || __builtin_add_overflow(__val, __dig, &__val))
+ {
+ __err |= ios_base::failbit;
+ return -1;
+ }
+ }
+ }
+ return __val;
+ }
+
+ // Read an unsigned integer from the stream and return it.
+ // Extract no more than __n digits. Set failbit if an integer isn't read.
+ template<typename _CharT, typename _Traits>
+ static int_least32_t
+ _S_read_signed(basic_istream<_CharT, _Traits>& __is,
+ ios_base::iostate& __err, int __n)
+ {
+ auto __sign = __is.peek();
+ if (__sign == '-' || __sign == '+')
+ (void) __is.get();
+ int_least32_t __val = _S_read_unsigned(__is, __err, __n);
+ if (__err & ios_base::failbit)
+ {
+ if (__sign == '-') [[unlikely]]
+ __val *= -1;
+ }
+ return __val;
+ }
+
+ // Read a digit from the stream and return it, or return -1.
+ // If no digit is read eofbit will be set (but not failbit).
+ template<typename _CharT, typename _Traits>
+ static int_least32_t
+ _S_try_read_digit(basic_istream<_CharT, _Traits>& __is,
+ ios_base::iostate& __err)
+ {
+ int_least32_t __val = -1;
+ auto __i = __is.peek();
+ if (!_Traits::eq_int_type(__i, _Traits::eof())) [[likely]]
+ {
+ _CharT __c = _Traits::to_char_type(__i);
+ if (_CharT('0') <= __c && __c <= _CharT('9')) [[likely]]
+ {
+ (void) __is.get();
+ __val = __c - _CharT('0');
+ }
+ }
+ else
+ __err |= ios_base::eofbit;
+ return __val;
+ }
+
+ // Read the specified character and return true.
+ // If the character is not found, set failbit and return false.
+ template<typename _CharT, typename _Traits>
+ static bool
+ _S_read_chr(basic_istream<_CharT, _Traits>& __is,
+ ios_base::iostate& __err, _CharT __c)
+ {
+ auto __i = __is.peek();
+ if (_Traits::eq_int_type(__i, _Traits::eof()))
+ __err |= ios_base::eofbit;
+ else if (_Traits::to_char_type(__i) == __c) [[likely]]
+ {
+ (void) __is.get();
+ return true;
+ }
+ __err |= ios_base::failbit;
+ return false;
+ }
+ };
+
+ template<typename _Duration>
+ using _Parser_t = _Parser<common_type_t<_Duration, seconds>>;
+
+} // namespace __detail
+/// ~endcond
+
template<typename _CharT, typename _Traits, typename _Rep, typename _Period,
typename _Alloc = allocator<_CharT>>
- basic_istream<_CharT, _Traits>&
+ inline basic_istream<_CharT, _Traits>&
from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
duration<_Rep, _Period>& __d,
basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
minutes* __offset = nullptr)
{
+ auto __need = __format::_ChronoParts::_TimeOfDay;
+ __detail::_Parser_t<duration<_Rep, _Period>> __p(__need);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __d = chrono::duration_cast<duration<_Rep, _Period>>(__p._M_time);
+ return __is;
}
-#endif
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2163,7 +2299,19 @@ namespace chrono
return __os;
}
- // TODO from_stream for day
+ template<typename _CharT, typename _Traits,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ day& __d,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ __detail::_Parser<> __p(__format::_ChronoParts::_Day);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __d = __p._M_ymd.day();
+ return __is;
+ }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2182,7 +2330,19 @@ namespace chrono
return __os;
}
- // TODO from_stream for month
+ template<typename _CharT, typename _Traits,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ month& __m,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ __detail::_Parser<> __p(__format::_ChronoParts::_Month);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __m = __p._M_ymd.month();
+ return __is;
+ }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2203,7 +2363,19 @@ namespace chrono
return __os;
}
- // TODO from_stream for year
+ template<typename _CharT, typename _Traits,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ year& __y,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ __detail::_Parser<> __p(__format::_ChronoParts::_Year);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __y = __p._M_ymd.year();
+ return __is;
+ }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2222,7 +2394,19 @@ namespace chrono
return __os;
}
- // TODO from_stream for weekday
+ template<typename _CharT, typename _Traits,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ weekday& __wd,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ __detail::_Parser<> __p(__format::_ChronoParts::_Weekday);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __wd = __p._M_wd;
+ return __is;
+ }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2279,7 +2463,21 @@ namespace chrono
return __os;
}
- // TODO from_stream for month_day
+ template<typename _CharT, typename _Traits,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ month_day& __md,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ using __format::_ChronoParts;
+ auto __need = _ChronoParts::_Month | _ChronoParts::_Day;
+ __detail::_Parser<> __p(__need);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __md = month_day(__p._M_ymd.month(), __p._M_ymd.day());
+ return __is;
+ }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2351,7 +2549,21 @@ namespace chrono
return __os;
}
- // TODO from_stream for year_month
+ template<typename _CharT, typename _Traits,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ year_month& __ym,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ using __format::_ChronoParts;
+ auto __need = _ChronoParts::_Year | _ChronoParts::_Month;
+ __detail::_Parser<> __p(__need);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __ym = year_month(__p._M_ymd.year(), __p._M_ymd.month());
+ return __is;
+ }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2367,7 +2579,22 @@ namespace chrono
return __os;
}
- // TODO from_stream for year_month_day
+ template<typename _CharT, typename _Traits,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ year_month_day& __ymd,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ using __format::_ChronoParts;
+ auto __need = _ChronoParts::_Year | _ChronoParts::_Month
+ | _ChronoParts::_Day;
+ __detail::_Parser<> __p(__need);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ __ymd = __p._M_ymd;
+ return __is;
+ }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
@@ -2498,7 +2725,28 @@ namespace chrono
return __os;
}
- // TODO: from_stream for sys_time
+ template<typename _CharT, typename _Traits, typename _Duration,
+ typename _Alloc = allocator<_CharT>>
+ basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ sys_time<_Duration>& __tp,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ minutes __off{};
+ if (!__offset)
+ __offset = &__off;
+ using __format::_ChronoParts;
+ auto __need = _ChronoParts::_Year | _ChronoParts::_Month
+ | _ChronoParts::_Day | _ChronoParts::_TimeOfDay;
+ __detail::_Parser_t<_Duration> __p(__need);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ {
+ auto __st = __p._M_sys_days + __p._M_time - *__offset;
+ __tp = chrono::time_point_cast<_Duration>(__st);
+ }
+ return __is;
+ }
template<typename _CharT, typename _Traits, typename _Duration>
inline basic_ostream<_CharT, _Traits>&
@@ -2509,7 +2757,19 @@ namespace chrono
return __os;
}
- // TODO: from_stream for utc_time
+ template<typename _CharT, typename _Traits, typename _Duration,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ utc_time<_Duration>& __tp,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ sys_time<_Duration> __st;
+ if (chrono::from_stream(__is, __fmt, __st, __abbrev, __offset))
+ __tp = utc_clock::from_sys(__st);
+ return __is;
+ }
template<typename _CharT, typename _Traits, typename _Duration>
inline basic_ostream<_CharT, _Traits>&
@@ -2520,7 +2780,19 @@ namespace chrono
return __os;
}
- // TODO: from_stream for tai_time
+ template<typename _CharT, typename _Traits, typename _Duration,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ tai_time<_Duration>& __tp,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ utc_time<_Duration> __ut;
+ if (chrono::from_stream(__is, __fmt, __ut, __abbrev, __offset))
+ __tp = tai_clock::from_utc(__ut);
+ return __is;
+ }
template<typename _CharT, typename _Traits, typename _Duration>
inline basic_ostream<_CharT, _Traits>&
@@ -2531,8 +2803,19 @@ namespace chrono
return __os;
}
- // TODO: from_stream for gps_time
-
+ template<typename _CharT, typename _Traits, typename _Duration,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ gps_time<_Duration>& __tp,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ utc_time<_Duration> __ut;
+ if (chrono::from_stream(__is, __fmt, __ut, __abbrev, __offset))
+ __tp = gps_clock::from_utc(__ut);
+ return __is;
+ }
template<typename _CharT, typename _Traits, typename _Duration>
inline basic_ostream<_CharT, _Traits>&
@@ -2543,7 +2826,19 @@ namespace chrono
return __os;
}
- // TODO: from_stream for file_time
+ template<typename _CharT, typename _Traits, typename _Duration,
+ typename _Alloc = allocator<_CharT>>
+ inline basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ file_time<_Duration>& __tp,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ sys_time<_Duration> __st;
+ if (chrono::from_stream(__is, __fmt, __st, __abbrev, __offset))
+ __tp = file_clock::from_sys(__st);
+ return __is;
+ }
template<typename _CharT, typename _Traits, typename _Duration>
inline basic_ostream<_CharT, _Traits>&
@@ -2554,7 +2849,1365 @@ namespace chrono
return __os;
}
- // TODO: from_stream for local_time
+ template<typename _CharT, typename _Traits, typename _Duration,
+ typename _Alloc = allocator<_CharT>>
+ basic_istream<_CharT, _Traits>&
+ from_stream(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ local_time<_Duration>& __tp,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ {
+ using __format::_ChronoParts;
+ auto __need = _ChronoParts::_Year | _ChronoParts::_Month
+ | _ChronoParts::_Day | _ChronoParts::_TimeOfDay;
+ __detail::_Parser_t<_Duration> __p(__need);
+ if (__p(__is, __fmt, __abbrev, __offset))
+ {
+ days __d = __p._M_sys_days.time_since_epoch();
+ auto __t = local_days(__d) + __p._M_time; // ignore offset
+ __tp = chrono::time_point_cast<_Duration>(__t);
+ }
+ return __is;
+ }
+
+ // [time.parse] parsing
+
+namespace __detail
+{
+ template<typename _Parsable, typename _CharT,
+ typename _Traits = std::char_traits<_CharT>,
+ typename... _OptArgs>
+ concept __parsable = requires (basic_istream<_CharT, _Traits>& __is,
+ const _CharT* __fmt, _Parsable& __tp,
+ _OptArgs*... __args)
+ { from_stream(__is, __fmt, __tp, __args...); };
+
+ template<typename _Parsable, typename _CharT,
+ typename _Traits = char_traits<_CharT>,
+ typename _Alloc = allocator<_CharT>>
+ struct _Parse
+ {
+ private:
+ using __string_type = basic_string<_CharT, _Traits, _Alloc>;
+
+ public:
+ _Parse(const _CharT* __fmt, _Parsable& __tp,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev = nullptr,
+ minutes* __offset = nullptr)
+ : _M_fmt(__fmt), _M_tp(std::__addressof(__tp)),
+ _M_abbrev(__abbrev), _M_offset(__offset)
+ { }
+
+ _Parse(_Parse&&) = delete;
+ _Parse& operator=(_Parse&&) = delete;
+
+ private:
+ using __stream_type = basic_istream<_CharT, _Traits>;
+
+ const _CharT* const _M_fmt;
+ _Parsable* const _M_tp;
+ __string_type* const _M_abbrev;
+ minutes* const _M_offset;
+
+ friend __stream_type&
+ operator>>(__stream_type& __is, _Parse&& __p)
+ {
+ if (__p._M_offset)
+ from_stream(__is, __p._M_fmt, *__p._M_tp, __p._M_abbrev,
+ __p._M_offset);
+ else if (__p._M_abbrev)
+ from_stream(__is, __p._M_fmt, *__p._M_tp, __p._M_abbrev);
+ else
+ from_stream(__is, __p._M_fmt, *__p._M_tp);
+ return __is;
+ }
+
+ friend void operator>>(__stream_type&, _Parse&) = delete;
+ friend void operator>>(__stream_type&, const _Parse&) = delete;
+ };
+} // namespace __detail
+
+ template<typename _CharT, __detail::__parsable<_CharT> _Parsable>
+ [[nodiscard, __gnu__::__access__(__read_only__, 1)]]
+ inline auto
+ parse(const _CharT* __fmt, _Parsable& __tp)
+ { return __detail::_Parse<_Parsable, _CharT>(__fmt, __tp); }
+
+ template<typename _CharT, typename _Traits, typename _Alloc,
+ __detail::__parsable<_CharT, _Traits> _Parsable>
+ [[nodiscard]]
+ inline auto
+ parse(const basic_string<_CharT, _Traits, _Alloc>& __fmt, _Parsable& __tp)
+ {
+ return __detail::_Parse<_Parsable, _CharT, _Traits>(__fmt.c_str(), __tp);
+ }
+
+ template<typename _CharT, typename _Traits, typename _Alloc,
+ typename _StrT = basic_string<_CharT, _Traits, _Alloc>,
+ __detail::__parsable<_CharT, _Traits, _StrT> _Parsable>
+ [[nodiscard, __gnu__::__access__(__read_only__, 1)]]
+ inline auto
+ parse(const _CharT* __fmt, _Parsable& __tp,
+ basic_string<_CharT, _Traits, _Alloc>& __abbrev)
+ {
+ auto __pa = std::__addressof(__abbrev);
+ return __detail::_Parse<_Parsable, _CharT, _Traits, _Alloc>(__fmt, __tp,
+ __pa);
+ }
+
+ template<typename _CharT, typename _Traits, typename _Alloc,
+ typename _StrT = basic_string<_CharT, _Traits, _Alloc>,
+ __detail::__parsable<_CharT, _Traits, _StrT> _Parsable>
+ [[nodiscard]]
+ inline auto
+ parse(const basic_string<_CharT, _Traits, _Alloc>& __fmt, _Parsable& __tp,
+ basic_string<_CharT, _Traits, _Alloc>& __abbrev)
+ {
+ auto __pa = std::__addressof(__abbrev);
+ return __detail::_Parse<_Parsable, _CharT, _Traits, _Alloc>(__fmt.c_str(),
+ __tp, __pa);
+ }
+
+ template<typename _CharT, typename _Traits = char_traits<_CharT>,
+ typename _StrT = basic_string<_CharT, _Traits>,
+ __detail::__parsable<_CharT, _Traits, _StrT, minutes> _Parsable>
+ [[nodiscard, __gnu__::__access__(__read_only__, 1)]]
+ inline auto
+ parse(const _CharT* __fmt, _Parsable& __tp, minutes& __offset)
+ {
+ return __detail::_Parse<_Parsable, _CharT>(__fmt, __tp, nullptr,
+ &__offset);
+ }
+
+ template<typename _CharT, typename _Traits, typename _Alloc,
+ typename _StrT = basic_string<_CharT, _Traits>,
+ __detail::__parsable<_CharT, _Traits, _StrT, minutes> _Parsable>
+ [[nodiscard]]
+ inline auto
+ parse(const basic_string<_CharT, _Traits, _Alloc>& __fmt, _Parsable& __tp,
+ minutes& __offset)
+ {
+ return __detail::_Parse<_Parsable, _CharT, _Traits, _Alloc>(__fmt.c_str(),
+ __tp, nullptr,
+ &__offset);
+ }
+
+ template<typename _CharT, typename _Traits, typename _Alloc,
+ typename _StrT = basic_string<_CharT, _Traits, _Alloc>,
+ __detail::__parsable<_CharT, _Traits, _StrT, minutes> _Parsable>
+ [[nodiscard, __gnu__::__access__(__read_only__, 1)]]
+ inline auto
+ parse(const _CharT* __fmt, _Parsable& __tp,
+ basic_string<_CharT, _Traits, _Alloc>& __abbrev, minutes& __offset)
+ {
+ auto __pa = std::__addressof(__abbrev);
+ return __detail::_Parse<_Parsable, _CharT, _Traits, _Alloc>(__fmt, __tp,
+ __pa,
+ &__offset);
+ }
+
+ template<typename _CharT, typename _Traits, typename _Alloc,
+ typename _StrT = basic_string<_CharT, _Traits, _Alloc>,
+ __detail::__parsable<_CharT, _Traits, _StrT, minutes> _Parsable>
+ [[nodiscard]]
+ inline auto
+ parse(const basic_string<_CharT, _Traits, _Alloc>& __fmt, _Parsable& __tp,
+ basic_string<_CharT, _Traits, _Alloc>& __abbrev, minutes& __offset)
+ {
+ auto __pa = std::__addressof(__abbrev);
+ return __detail::_Parse<_Parsable, _CharT, _Traits, _Alloc>(__fmt.c_str(),
+ __tp, __pa,
+ &__offset);
+ }
+
+ /// @cond undocumented
+ template<typename _Duration>
+ template<typename _CharT, typename _Traits, typename _Alloc>
+ basic_istream<_CharT, _Traits>&
+ __detail::_Parser<_Duration>::
+ operator()(basic_istream<_CharT, _Traits>& __is, const _CharT* __fmt,
+ basic_string<_CharT, _Traits, _Alloc>* __abbrev,
+ minutes* __offset)
+ {
+ using sentry = typename basic_istream<_CharT, _Traits>::sentry;
+ ios_base::iostate __err = ios_base::goodbit;
+ if (sentry __cerb(__is, true); __cerb)
+ {
+ locale __loc = __is.getloc();
+ auto& __tmget = std::use_facet<std::time_get<_CharT>>(__loc);
+ auto& __tmpunct = std::use_facet<std::__timepunct<_CharT>>(__loc);
+
+ // RAII type to save and restore stream state.
+ struct _Stream_state
+ {
+ explicit
+ _Stream_state(basic_istream<_CharT, _Traits>& __i)
+ : _M_is(__i),
+ _M_flags(__i.flags(ios_base::skipws | ios_base::dec)),
+ _M_w(__i.width(0))
+ { }
+
+ ~_Stream_state()
+ {
+ _M_is.flags(_M_flags);
+ _M_is.width(_M_w);
+ }
+
+ _Stream_state(_Stream_state&&) = delete;
+
+ basic_istream<_CharT, _Traits>& _M_is;
+ ios_base::fmtflags _M_flags;
+ streamsize _M_w;
+ };
+
+ auto __is_failed = [](ios_base::iostate __e) {
+ return static_cast<bool>(__e & ios_base::failbit);
+ };
+
+ // Read an unsigned integer from the stream and return it.
+ // Extract no more than __n digits. Set __err on error.
+ auto __read_unsigned = [&] (int __n) {
+ return _S_read_unsigned(__is, __err, __n);
+ };
+
+ // Read a signed integer from the stream and return it.
+ // Extract no more than __n digits. Set __err on error.
+ auto __read_signed = [&] (int __n) {
+ return _S_read_signed(__is, __err, __n);
+ };
+
+ // Read an expected character from the stream.
+ auto __read_chr = [&__is, &__err] (_CharT __c) {
+ return _S_read_chr(__is, __err, __c);
+ };
+
+ using __format::_ChronoParts;
+ _ChronoParts __parts{};
+
+ const year __bad_y = --year::min(); // SHRT_MIN
+ const month __bad_mon(255);
+ const day __bad_day(255);
+ const weekday __bad_wday(255);
+ const hours __bad_h(-1);
+ const minutes __bad_min(-9999);
+ const seconds __bad_sec(-1);
+
+ year __y = __bad_y, __yy = __bad_y; // %Y, %yy
+ year __iso_y = __bad_y, __iso_yy = __bad_y; // %G, %g
+ month __m = __bad_mon; // %m
+ day __d = __bad_day; // %d
+ weekday __wday = __bad_wday; // %a %A %u %w
+ hours __h = __bad_h, __h12 = __bad_h; // %H, %I
+ minutes __min = __bad_min; // %M
+ _Duration __s = __bad_sec; // %S
+ int __ampm = 0; // %p
+ int __iso_wk = -1, __sunday_wk = -1, __monday_wk = -1; // %V, %U, %W
+ int __century = -1; // %C
+ int __dayofyear = -1; // %j (for non-duration)
+
+ minutes __tz_offset = __bad_min;
+ basic_string<_CharT, _Traits> __tz_abbr;
+
+ // bool __is_neg = false; // TODO: how is this handled for parsing?
+
+ _CharT __mod{}; // One of 'E' or 'O' or nul.
+ unsigned __num = 0; // Non-zero for N modifier.
+ bool __is_flag = false; // True if we're processing a % flag.
+
+ // If an out-of-range value is extracted (e.g. 61min for %M),
+ // do not set failbit immediately because we might not need it
+ // (e.g. parsing chrono::year doesn't care about invalid %M values).
+ // Instead set the variable back to its initial 'bad' state,
+ // and also set related variables corresponding to the same field
+ // (e.g. a bad %M value for __min should also reset __h and __s).
+ // If a valid value is needed later the bad value will cause failure.
+
+ // For some fields we don't know the correct range when parsing and
+ // we have to be liberal in what we accept, e.g. we allow 366 for
+ // day-of-year because that's valid in leap years, and we allow 31
+ // for day-of-month. If those values are needed to determine the
+ // result then we can do a correct range check at the end when we
+ // know the how many days the relevant year or month actually has.
+
+ while (*__fmt)
+ {
+ _CharT __c = *__fmt++;
+ if (!__is_flag)
+ {
+ if (__c == '%')
+ __is_flag = true; // This is the start of a flag.
+ else if (std::isspace(__c, __loc))
+ std::ws(__is); // Match zero or more whitespace characters.
+ else if (!__read_chr(__c)) [[unlikely]]
+ break; // Failed to match the expected character.
+
+ continue; // Process next character in the format string.
+ }
+
+ // Now processing a flag.
+ switch (__c)
+ {
+ case 'a': // Locale's weekday name
+ case 'A': // (full or abbreviated, matched case-insensitively).
+ if (__mod || __num) [[unlikely]]
+ __err = ios_base::failbit;
+ else
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2, __fmt);
+ if (!__is_failed(__err))
+ __wday = weekday(__tm.tm_wday);
+ }
+ __parts |= _ChronoParts::_Weekday;
+ break;
+
+ case 'b': // Locale's month name
+ case 'h': // (full or abbreviated, matched case-insensitively).
+ case 'B':
+ if (__mod || __num) [[unlikely]]
+ __err = ios_base::failbit;
+ else
+ {
+ // strptime behaves differently for %b and %B,
+ // but chrono::parse says they're equivalent.
+ // Luckily libstdc++ std::time_get works as needed.
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2, __fmt);
+ if (!__is_failed(__err))
+ __m = month(__tm.tm_mon + 1);
+ }
+ __parts |= _ChronoParts::_Month;
+ break;
+
+ case 'c': // Locale's date and time representation.
+ if (__mod == 'O' || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2 - (__mod == 'E'), __fmt);
+ if (!__is_failed(__err))
+ {
+ __y = year(__tm.tm_year + 1900);
+ __m = month(__tm.tm_mon + 1);
+ __d = day(__tm.tm_mday);
+ __h = hours(__tm.tm_hour);
+ __min = minutes(__tm.tm_min);
+ __s = duration_cast<_Duration>(seconds(__tm.tm_sec));
+ }
+ }
+ __parts |= _ChronoParts::_DateTime;
+ break;
+
+ case 'C': // Century
+ if (!__mod) [[likely]]
+ {
+ auto __v = __read_signed(__num ? __num : 2);
+ if (!__is_failed(__err))
+ __century = __v * 100;
+ }
+ else if (__mod == 'E')
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 3, __fmt);
+ if (!__is_failed(__err))
+ __century = __tm.tm_year;
+ }
+ else [[unlikely]]
+ __err |= ios_base::failbit;
+ // N.B. don't set this here: __parts |= _ChronoParts::_Year;
+ break;
+
+ case 'd': // Day of month (1-31)
+ case 'e':
+ if (!__mod) [[likely]]
+ {
+ auto __v = __read_unsigned(__num ? __num : 2);
+ if (!__is_failed(__err))
+ __d = day(__v);
+ }
+ else if (__mod == 'O')
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 3, __fmt);
+ if (!__is_failed(__err))
+ __d = day(__tm.tm_mday);
+ }
+ else [[unlikely]]
+ __err |= ios_base::failbit;
+ __parts |= _ChronoParts::_Day;
+ break;
+
+ case 'D': // %m/%d/%y
+ if (__mod || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ auto __month = __read_unsigned(2); // %m
+ __read_chr('/');
+ auto __day = __read_unsigned(2); // %d
+ __read_chr('/');
+ auto __year = __read_unsigned(2); // %y
+ if (__is_failed(__err))
+ break;
+ __y = year(__year + 1900 + 100 * int(__year < 69));
+ __m = month(__month);
+ __d = day(__day);
+ if (!year_month_day(__y, __m, __d).ok())
+ {
+ __y = __yy = __iso_y = __iso_yy = __bad_y;
+ __m = __bad_mon;
+ __d = __bad_day;
+ break;
+ }
+ }
+ __parts |= _ChronoParts::_Date;
+ break;
+
+ case 'F': // %Y-%m-%d - any N modifier only applies to %Y.
+ if (__mod) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ auto __year = __read_signed(__num ? __num : 4); // %Y
+ __read_chr('-');
+ auto __month = __read_unsigned(2); // %m
+ __read_chr('-');
+ auto __day = __read_unsigned(2); // %d
+ if (__is_failed(__err))
+ break;
+ __y = year(__year);
+ __m = month(__month);
+ __d = day(__day);
+ if (!year_month_day(__y, __m, __d).ok())
+ {
+ __y = __yy = __iso_y = __iso_yy = __bad_y;
+ __m = __bad_mon;
+ __d = __bad_day;
+ break;
+ }
+ }
+ __parts |= _ChronoParts::_Date;
+ break;
+
+ case 'g': // Last two digits of ISO week-based year.
+ if (__mod) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ auto __val = __read_unsigned(__num ? __num : 2);
+ if (__val >= 0 && __val <= 99)
+ {
+ __iso_yy = year(__val);
+ if (__century == -1) // No %C has been parsed yet.
+ __century = 2000;
+ }
+ else
+ __iso_yy = __iso_y = __y = __yy = __bad_y;
+ }
+ __parts |= _ChronoParts::_Year;
+ break;
+
+ case 'G': // ISO week-based year.
+ if (__mod) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ __iso_y = year(__read_unsigned(__num ? __num : 4));
+ __parts |= _ChronoParts::_Year;
+ break;
+
+ case 'H': // 24-hour (00-23)
+ case 'I': // 12-hour (1-12)
+ if (__mod == 'E') [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (__mod == 'O')
+ {
+#if 0
+ struct tm __tm{};
+ __tm.tm_ampm = 1;
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 3, __fmt);
+ if (!__is_failed(__err))
+ {
+ if (__c == 'I')
+ {
+ __h12 = hours(__tm.tm_hour);
+ __h = __bad_h;
+ }
+ else
+ __h = hours(__tm.tm_hour);
+ }
+#else
+ // XXX %OI seems to be unimplementable.
+ __err |= ios_base::failbit;
+#endif
+ }
+ else
+ {
+ auto __val = __read_unsigned(__num ? __num : 2);
+ if (__c == 'I' && __val >= 1 && __val <= 12)
+ {
+ __h12 = hours(__val);
+ __h = __bad_h;
+ }
+ else if (__c == 'H' && __val >= 0 && __val <= 23)
+ __h = hours(__val);
+ }
+ __parts |= _ChronoParts::_TimeOfDay;
+ break;
+
+ case 'j': // For duration, count of days, otherwise day of year
+ if (__mod) [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (_M_need == _ChronoParts::_TimeOfDay) // duration
+ {
+ auto __val = __read_signed(__num ? __num : 3);
+ if (!__is_failed(__err))
+ {
+ __h = days(__val); // __h will get added to _M_time
+ __parts |= _ChronoParts::_TimeOfDay;
+ }
+ }
+ else
+ {
+ __dayofyear = __read_unsigned(__num ? __num : 3);
+ // N.B. do not alter __parts here, done after loop.
+ // No need for range checking here either.
+ }
+ break;
+
+ case 'm': // Month (1-12)
+ if (__mod == 'E') [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (__mod == 'O')
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2, __fmt);
+ if (!__is_failed(__err))
+ __m = month(__tm.tm_mon + 1);
+ }
+ else
+ {
+ auto __val = __read_unsigned(__num ? __num : 2);
+ if (__val >= 1 && __val <= 12)
+ __m = month(__val);
+ else
+ __m = __bad_mon;
+ }
+ __parts |= _ChronoParts::_Month;
+ break;
+
+ case 'M': // Minutes
+ if (__mod == 'E') [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (__mod == 'O')
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2, __fmt);
+ if (!__is_failed(__err))
+ __min = minutes(__tm.tm_min);
+ }
+ else
+ {
+ auto __val = __read_unsigned(__num ? __num : 2);
+ if (0 <= __val && __val < 60)
+ __min = minutes(__val);
+ else
+ {
+ __h = __bad_h;
+ __min = __bad_min;
+ __s = __bad_sec;
+ break;
+ }
+ }
+ __parts |= _ChronoParts::_TimeOfDay;
+ break;
+
+ case 'p': // Locale's AM/PM designation for 12-hour clock.
+ if (__mod || __num)
+ __err |= ios_base::failbit;
+ else
+ {
+ // Can't use std::time_get here as it can't parse %p
+ // in isolation without %I. This might be faster anyway.
+ const _CharT* __ampms[2];
+ __tmpunct._M_am_pm(__ampms);
+ int __n = 0, __which = 3;
+ while (__which != 0)
+ {
+ auto __i = __is.peek();
+ if (_Traits::eq_int_type(__i, _Traits::eof()))
+ {
+ __err |= ios_base::eofbit | ios_base::failbit;
+ break;
+ }
+ __i = std::toupper(_Traits::to_char_type(__i), __loc);
+ if (__which & 1)
+ {
+ if (__i != std::toupper(__ampms[0][__n], __loc))
+ __which ^= 1;
+ else if (__ampms[0][__n + 1] == _CharT())
+ {
+ __which = 1;
+ (void) __is.get();
+ break;
+ }
+ }
+ if (__which & 2)
+ {
+ if (__i != std::toupper(__ampms[1][__n], __loc))
+ __which ^= 2;
+ else if (__ampms[1][__n + 1] == _CharT())
+ {
+ __which = 2;
+ (void) __is.get();
+ break;
+ }
+ }
+ if (__which)
+ (void) __is.get();
+ ++__n;
+ }
+ if (__which == 0 || __which == 3)
+ __err |= ios_base::failbit;
+ else
+ __ampm = __which;
+ }
+ break;
+
+ case 'r': // Locale's 12-hour time.
+ if (__mod || __num)
+ __err |= ios_base::failbit;
+ else
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2, __fmt);
+ if (!__is_failed(__err))
+ {
+ __h = hours(__tm.tm_hour);
+ __min = minutes(__tm.tm_min);
+ __s = seconds(__tm.tm_sec);
+ }
+ }
+ __parts |= _ChronoParts::_TimeOfDay;
+ break;
+
+ case 'R': // %H:%M
+ case 'T': // %H:%M:%S
+ if (__mod || __num) [[unlikely]]
+ {
+ __err |= ios_base::failbit;
+ break;
+ }
+ else
+ {
+ auto __val = __read_unsigned(2);
+ if (__val == -1 || __val > 23)
+ {
+ __h = __bad_h;
+ __min = __bad_min;
+ __s = __bad_sec;
+ break;
+ }
+ if (!__read_chr(':'))
+ {
+ __err |= ios_base::failbit;
+ break;
+ }
+ __h = hours(__val);
+
+ __val = __read_unsigned(2);
+ if (__val == -1 || __val > 60)
+ {
+ __h = __bad_h;
+ __min = __bad_min;
+ __s = __bad_sec;
+ break;
+ }
+ __min = minutes(__val);
+
+ __parts |= _ChronoParts::_TimeOfDay;
+
+ if (__c != 'T' || !__read_chr(':'))
+ break;
+ }
+ [[fallthrough]];
+
+ case 'S': // Seconds
+ if (__mod == 'E') [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (__mod == 'O')
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 3, __fmt);
+ if (!__is_failed(__err))
+ __s = seconds(__tm.tm_sec);
+ }
+ else if constexpr (ratio_equal_v<typename _Duration::period,
+ ratio<1>>)
+ {
+ auto __val = __read_unsigned(__num ? __num : 2);
+ if (0 <= __val && __val <= 59)
+ __s = seconds(__val);
+ else
+ {
+ __h = __bad_h;
+ __min = __bad_min;
+ __s = __bad_sec;
+ break;
+ }
+ }
+ else
+ {
+ basic_stringstream<_CharT> __buf;
+ auto __digit = _S_try_read_digit(__is, __err);
+ if (__digit != -1)
+ {
+ __buf.put(_CharT('0') + __digit);
+ __digit = _S_try_read_digit(__is, __err);
+ if (__digit != -1)
+ __buf.put(_CharT('0') + __digit);
+ }
+
+ auto __i = __is.peek();
+ if (_Traits::eq_int_type(__i, _Traits::eof()))
+ __err |= ios_base::eofbit;
+ else
+ {
+ auto& __np = use_facet<numpunct<_CharT>>(__loc);
+ auto __dp = __np.decimal_point();
+ _CharT __c = _Traits::to_char_type(__i);
+ if (__c == __dp)
+ {
+ (void) __is.get();
+ __buf.put(__c);
+ int __prec
+ = hh_mm_ss<_Duration>::fractional_width;
+ do
+ {
+ __digit = _S_try_read_digit(__is, __err);
+ if (__digit != -1)
+ __buf.put(_CharT('0') + __digit);
+ else
+ break;
+ }
+ while (--__prec);
+ }
+ }
+
+ if (!__is_failed(__err))
+ {
+ auto& __ng = use_facet<num_get<_CharT>>(__loc);
+ long double __val;
+ ios_base::iostate __err2{};
+ __ng.get(__buf, {}, __buf, __err2, __val);
+ if (__is_failed(__err2)) [[unlikely]]
+ __err |= __err2;
+ else
+ {
+ duration<long double> __fs(__val);
+ __s = duration_cast<_Duration>(__fs);
+ }
+ }
+ }
+ __parts |= _ChronoParts::_TimeOfDay;
+ break;
+
+ case 'u': // ISO weekday (1-7)
+ case 'w': // Weekday (0-6)
+ if (__mod == 'E') [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (__mod == 'O')
+ {
+ if (__c == 'w')
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 3, __fmt);
+ if (!__is_failed(__err))
+ __wday = weekday(__tm.tm_wday);
+ }
+ else
+ __err |= ios_base::failbit;
+ }
+ else
+ {
+ const int __lo = __c == 'u' ? 1 : 0;
+ const int __hi = __lo + 6;
+ auto __val = __read_unsigned(__num ? __num : 1);
+ if (__lo <= __val && __val <= __hi)
+ __wday = weekday(__val);
+ else
+ {
+ __wday = __bad_wday;
+ break;
+ }
+ }
+ __parts |= _ChronoParts::_Weekday;
+ break;
+
+ case 'U': // Week number of the year (from first Sunday).
+ case 'V': // ISO week-based week number.
+ case 'W': // Week number of the year (from first Monday).
+ if (__mod == 'E') [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (__mod == 'O')
+ {
+ if (__c == 'V') [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ // TODO nl_langinfo_l(ALT_DIGITS) ?
+ // Not implementable using std::time_get.
+ }
+ }
+ else
+ {
+ const int __lo = __c == 'V' ? 1 : 0;
+ const int __hi = 53;
+ auto __val = __read_unsigned(__num ? __num : 2);
+ if (__lo <= __val && __val <= __hi)
+ {
+ switch (__c)
+ {
+ case 'U':
+ __sunday_wk = __val;
+ break;
+ case 'V':
+ __iso_wk = __val;
+ break;
+ case 'W':
+ __monday_wk = __val;
+ break;
+ }
+ }
+ else
+ __iso_wk = __sunday_wk = __monday_wk = -1;
+ }
+ // N.B. do not alter __parts here, done after loop.
+ break;
+
+ case 'x': // Locale's date representation.
+ if (__mod == 'O' || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2 - (__mod == 'E'), __fmt);
+ if (!__is_failed(__err))
+ {
+ __y = year(__tm.tm_year + 1900);
+ __m = month(__tm.tm_mon + 1);
+ __d = day(__tm.tm_mday);
+ }
+ }
+ __parts |= _ChronoParts::_Date;
+ break;
+
+ case 'X': // Locale's time representation.
+ if (__mod == 'O' || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 2 - (__mod == 'E'), __fmt);
+ if (!__is_failed(__err))
+ {
+ __h = hours(__tm.tm_hour);
+ __min = minutes(__tm.tm_min);
+ __s = duration_cast<_Duration>(seconds(__tm.tm_sec));
+ }
+ }
+ __parts |= _ChronoParts::_TimeOfDay;
+ break;
+
+ case 'y': // Last two digits of year.
+ if (__mod) [[unlikely]]
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 3, __fmt);
+ if (!__is_failed(__err))
+ {
+ int __cent = __tm.tm_year < 2000 ? 1900 : 2000;
+ __yy = year(__tm.tm_year - __cent);
+ if (__century == -1) // No %C has been parsed yet.
+ __century = __cent;
+ }
+ }
+ else
+ {
+ auto __val = __read_unsigned(__num ? __num : 2);
+ if (__val >= 0 && __val <= 99)
+ {
+ __yy = year(__val);
+ if (__century == -1) // No %C has been parsed yet.
+ __century = __val < 69 ? 2000 : 1900;
+ }
+ else
+ __y = __yy = __iso_yy = __iso_y = __bad_y;
+ }
+ __parts |= _ChronoParts::_Year;
+ break;
+
+ case 'Y': // Year
+ if (__mod == 'O') [[unlikely]]
+ __err |= ios_base::failbit;
+ else if (__mod == 'E')
+ {
+ struct tm __tm{};
+ __tmget.get(__is, {}, __is, __err, &__tm,
+ __fmt - 3, __fmt);
+ if (!__is_failed(__err))
+ __y = year(__tm.tm_year);
+ }
+ else
+ {
+ auto __val = __read_unsigned(__num ? __num : 4);
+ if (!__is_failed(__err))
+ __y = year(__val);
+ }
+ __parts |= _ChronoParts::_Year;
+ break;
+
+ case 'z':
+ if (__num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ // For %Ez and %Oz read [+|-][h]h[:mm].
+ // For %z read [+|-]hh[mm].
+
+ auto __i = __is.peek();
+ if (_Traits::eq_int_type(__i, _Traits::eof()))
+ {
+ __err |= ios_base::eofbit | ios_base::failbit;
+ break;
+ }
+ _CharT __ic = _Traits::to_char_type(__i);
+ const bool __neg = __ic == _CharT('-');
+ if (__ic == _CharT('-') || __ic == _CharT('+'))
+ (void) __is.get();
+
+ int_least32_t __hh;
+ if (__mod)
+ {
+ // Read h[h]
+ __hh = __read_unsigned(2);
+ }
+ else
+ {
+ // Read hh
+ __hh = 10 * _S_try_read_digit(__is, __err);
+ __hh += _S_try_read_digit(__is, __err);
+ }
+
+ if (__is_failed(__err))
+ break;
+
+ __i = __is.peek();
+ if (_Traits::eq_int_type(__i, _Traits::eof()))
+ {
+ __err |= ios_base::eofbit;
+ __tz_offset = minutes(__hh * (__neg ? -60 : 60));
+ break;
+ }
+ __ic = _Traits::to_char_type(__i);
+
+ bool __read_mm = false;
+ if (__mod)
+ {
+ if (__ic == _GLIBCXX_WIDEN(":")[0])
+ {
+ // Read [:mm] part.
+ (void) __is.get();
+ __read_mm = true;
+ }
+ }
+ else if (_CharT('0') <= __ic && __ic <= _CharT('9'))
+ {
+ // Read [mm] part.
+ __read_mm = true;
+ }
+
+ int_least32_t __mm = 0;
+ if (__read_mm)
+ {
+ __mm = 10 * _S_try_read_digit(__is, __err);
+ __mm += _S_try_read_digit(__is, __err);
+ }
+
+ if (!__is_failed(__err))
+ {
+ auto __z = __hh * 60 + __mm;
+ __tz_offset = minutes(__neg ? -__z : __z);
+ }
+ }
+ break;
+
+ case 'Z':
+ if (__mod || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ basic_string_view<_CharT> __x = _GLIBCXX_WIDEN("_/-+");
+ __tz_abbr.clear();
+ while (true)
+ {
+ auto __i = __is.peek();
+ if (!_Traits::eq_int_type(__i, _Traits::eof()))
+ {
+ _CharT __a = _Traits::to_char_type(__i);
+ if (std::isalnum(__a, __loc)
+ || __x.find(__a) != __x.npos)
+ {
+ __tz_abbr.push_back(__a);
+ (void) __is.get();
+ continue;
+ }
+ }
+ else
+ __err |= ios_base::eofbit;
+ break;
+ }
+ if (__tz_abbr.empty())
+ __err |= ios_base::failbit;
+ }
+ break;
+
+ case 'n': // Exactly one whitespace character.
+ if (__mod || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ _CharT __i = __is.peek();
+ if (_Traits::eq_int_type(__i, _Traits::eof()))
+ __err |= ios_base::eofbit | ios_base::failbit;
+ else if (std::isspace(_Traits::to_char_type(__i), __loc))
+ (void) __is.get();
+ else
+ __err |= ios_base::failbit;
+ }
+ break;
+
+ case 't': // Zero or one whitespace characters.
+ if (__mod || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ {
+ _CharT __i = __is.peek();
+ if (_Traits::eq_int_type(__i, _Traits::eof()))
+ __err |= ios_base::eofbit;
+ else if (std::isspace(_Traits::to_char_type(__i), __loc))
+ (void) __is.get();
+ }
+ break;
+
+ case '%': // A % character.
+ if (__mod || __num) [[unlikely]]
+ __err |= ios_base::failbit;
+ else
+ __read_chr('%');
+ break;
+
+ case 'O': // Modifiers
+ case 'E':
+ if (__mod || __num) [[unlikely]]
+ {
+ __err |= ios_base::failbit;
+ break;
+ }
+ __mod = __c;
+ continue;
+
+ default:
+ if (_CharT('1') <= __c && __c <= _CharT('9'))
+ {
+ if (!__mod) [[likely]]
+ {
+ // %Nx - extract positive decimal integer N
+ auto __end = __fmt + _Traits::length(__fmt);
+ auto [__v, __ptr]
+ = __format::__parse_integer(__fmt - 1, __end);
+ if (__ptr) [[likely]]
+ {
+ __num = __v;
+ __fmt = __ptr;
+ continue;
+ }
+ }
+ }
+ __err |= ios_base::failbit;
+ }
+
+ if (__is_failed(__err)) [[unlikely]]
+ break;
+
+ __is_flag = false;
+ __num = 0;
+ __mod = _CharT();
+ }
+
+ if (__century >= 0)
+ {
+ if (__yy != __bad_y && __y == __bad_y)
+ __y = years(__century) + __yy; // Use %y instead of %Y
+ if (__iso_yy != __bad_y && __iso_y == __bad_y)
+ __iso_y = years(__century) + __iso_yy; // Use %g instead of %G
+ }
+
+ bool __can_use_doy = false;
+ bool __can_use_iso_wk = false;
+ bool __can_use_sun_wk = false;
+ bool __can_use_mon_wk = false;
+
+ // A year + day-of-year can be converted to a full date.
+ if (__y != __bad_y && __dayofyear >= 0)
+ {
+ __can_use_doy = true;
+ __parts |= _ChronoParts::_Date;
+ }
+ else if (__y != __bad_y && __wday != __bad_wday && __sunday_wk >= 0)
+ {
+ __can_use_sun_wk = true;
+ __parts |= _ChronoParts::_Date;
+ }
+ else if (__y != __bad_y && __wday != __bad_wday && __monday_wk >= 0)
+ {
+ __can_use_mon_wk = true;
+ __parts |= _ChronoParts::_Date;
+ }
+ else if (__iso_y != __bad_y && __wday != __bad_wday && __iso_wk > 0)
+ {
+ // An ISO week date can be converted to a full date.
+ __can_use_iso_wk = true;
+ __parts |= _ChronoParts::_Date;
+ }
+
+ if (__is_failed(__err)) [[unlikely]]
+ ; // Don't bother doing any more work.
+ else if (__is_flag) [[unlikely]] // incomplete format flag
+ __err |= ios_base::failbit;
+ else if ((_M_need & __parts) == _M_need) [[likely]]
+ {
+ // We try to avoid calculating _M_sys_days and _M_ymd unless
+ // necessary, because converting sys_days to year_month_day
+ // (or vice versa) requires non-trivial calculations.
+ // If we have y/m/d values then use them to populate _M_ymd
+ // and only convert it to _M_sys_days if the caller needs that.
+ // But if we don't have y/m/d and need to calculate the date
+ // from the day-of-year or a week+weekday then we set _M_sys_days
+ // and only convert it to _M_ymd if the caller needs that.
+
+ // We do more error checking here, but only for the fields that
+ // we actually need to use. For example, we will not diagnose
+ // an invalid dayofyear==366 for non-leap years unless actually
+ // using __dayofyear. This should mean we never produce invalid
+ // results, but it means not all invalid inputs are diagnosed,
+ // e.g. "2023-01-01 366" >> "%F %j" ignores the invalid 366.
+ // We also do not diagnose inconsistent values for the same
+ // field, e.g. "2021 2022 2023" >> "%C%y %Y %Y" just uses 2023.
+
+ // Whether the caller wants _M_wd.
+ // The _Weekday bit is only set for chrono::weekday.
+ const bool __need_wday = _M_need & _ChronoParts::_Weekday;
+
+ // Whether the caller wants _M_sys_days and _M_time.
+ // Only true for time_points.
+ const bool __need_time = _M_need & _ChronoParts::_TimeOfDay;
+
+ if (__need_wday && __wday != __bad_wday)
+ _M_wd = __wday; // Caller only wants a weekday and we have one.
+ else if (_M_need & _ChronoParts::_Date) // subsumes __need_wday
+ {
+ // Whether the caller wants _M_ymd.
+ // True for chrono::year etc., false for time_points.
+ const bool __need_ymd = !__need_wday && !__need_time;
+
+ if ((_M_need & _ChronoParts::_Year && __y == __bad_y)
+ || (_M_need & _ChronoParts::_Month && __m == __bad_mon)
+ || (_M_need & _ChronoParts::_Day && __d == __bad_day))
+ {
+ // Missing at least one of y/m/d so calculate sys_days
+ // from the other data we have available.
+
+ if (__can_use_doy)
+ {
+ if ((0 < __dayofyear && __dayofyear <= 365)
+ || (__dayofyear == 366 && __y.is_leap()))
+ [[likely]]
+ {
+ _M_sys_days = sys_days(__y/January/1)
+ + days(__dayofyear - 1);
+ if (__need_ymd)
+ _M_ymd = year_month_day(_M_sys_days);
+ }
+ else
+ __err |= ios_base::failbit;
+ }
+ else if (__can_use_iso_wk)
+ {
+ // Calculate y/m/d from ISO week date.
+
+ if (__iso_wk == 53)
+ {
+ // A year has 53 weeks iff Jan 1st is a Thursday
+ // or Jan 1 is a Wednesday and it's a leap year.
+ const sys_days __jan4(__iso_y/January/4);
+ weekday __wd1(__jan4 - days(3));
+ if (__wd1 != Thursday)
+ if (__wd1 != Wednesday || !__iso_y.is_leap())
+ __err |= ios_base::failbit;
+ }
+
+ if (!__is_failed(__err)) [[likely]]
+ {
+ // First Thursday is always in week one:
+ sys_days __w(Thursday[1]/January/__iso_y);
+ // First day of week-based year:
+ __w -= Thursday - Monday;
+ __w += days(weeks(__iso_wk - 1));
+ __w += __wday - Monday;
+ _M_sys_days = __w;
+
+ if (__need_ymd)
+ _M_ymd = year_month_day(_M_sys_days);
+ }
+ }
+ else if (__can_use_sun_wk)
+ {
+ // Calculate y/m/d from week number + weekday.
+ sys_days __wk1(__y/January/Sunday[1]);
+ _M_sys_days = __wk1 + weeks(__sunday_wk - 1)
+ + days(__wday.c_encoding());
+ _M_ymd = year_month_day(_M_sys_days);
+ if (_M_ymd.year() != __y) [[unlikely]]
+ __err |= ios_base::failbit;
+ }
+ else if (__can_use_mon_wk)
+ {
+ // Calculate y/m/d from week number + weekday.
+ sys_days __wk1(__y/January/Monday[1]);
+ _M_sys_days = __wk1 + weeks(__monday_wk - 1)
+ + days(__wday.c_encoding() - 1);
+ _M_ymd = year_month_day(_M_sys_days);
+ if (_M_ymd.year() != __y) [[unlikely]]
+ __err |= ios_base::failbit;
+ }
+ else // Should not be able to get here.
+ __err |= ios_base::failbit;
+ }
+ else
+ {
+ // We know that all fields the caller needs are present,
+ // but check that their values are in range.
+ // Make unwanted fields valid so that _M_ymd.ok() is true.
+
+ if (_M_need & _ChronoParts::_Year)
+ {
+ if (!__y.ok()) [[unlikely]]
+ __err |= ios_base::failbit;
+ }
+ else if (__y == __bad_y)
+ __y = 1972y; // Leap year so that Feb 29 is valid.
+
+ if (_M_need & _ChronoParts::_Month)
+ {
+ if (!__m.ok()) [[unlikely]]
+ __err |= ios_base::failbit;
+ }
+ else if (__m == __bad_mon)
+ __m = January;
+
+ if (_M_need & _ChronoParts::_Day)
+ {
+ if (__d < day(1) || __d > (__y/__m/last).day())
+ __err |= ios_base::failbit;
+ }
+ else if (__d == __bad_day)
+ __d = 1d;
+
+ if (year_month_day __ymd(__y, __m, __d); __ymd.ok())
+ {
+ _M_ymd = __ymd;
+ if (__need_wday || __need_time)
+ _M_sys_days = sys_days(_M_ymd);
+ }
+ else [[unlikely]]
+ __err |= ios_base::failbit;
+ }
+
+ if (__need_wday)
+ _M_wd = weekday(_M_sys_days);
+ }
+
+ // Need to set _M_time for both durations and time_points.
+ if (__need_time)
+ {
+ if (__h == __bad_h && __h12 != __bad_h)
+ {
+ if (__ampm == 1)
+ __h = __h12 == hours(12) ? hours(0) : __h12;
+ else if (__ampm == 2)
+ __h = __h12 == hours(12) ? __h12 : __h12 + hours(12);
+ else [[unlikely]]
+ __err |= ios_base::failbit;
+ }
+
+ auto __t = _M_time.zero();
+ bool __ok = false;
+
+ if (__h != __bad_h)
+ {
+ __ok = true;
+ __t += __h;
+ }
+
+ if (__min != __bad_min)
+ {
+ __ok = true;
+ __t += __min;
+ }
+
+ if (__s != __bad_sec)
+ {
+ __ok = true;
+ __t += __s;
+ }
+
+ if (__ok)
+ _M_time = __t;
+ else
+ __err |= ios_base::failbit;
+ }
+
+ if (!__is_failed(__err)) [[likely]]
+ {
+ if (__offset && __tz_offset != __bad_min)
+ *__offset = __tz_offset;
+ if (__abbrev && !__tz_abbr.empty())
+ *__abbrev = std::move(__tz_abbr);
+ }
+ }
+ else
+ __err |= ios_base::failbit;
+ }
+ if (__err)
+ __is.setstate(__err);
+ return __is;
+ }
+ /// @endcond
#undef _GLIBCXX_WIDEN
/// @} group chrono
diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
index e63d6c7..6fdc0c8 100644
--- a/libstdc++-v3/include/std/chrono
+++ b/libstdc++-v3/include/std/chrono
@@ -51,9 +51,8 @@
#endif
#if __cplusplus >= 202002L
-// TODO formatting and parsing
-// # undef __cpp_lib_chrono
-// # define __cpp_lib_chrono 201907L
+# undef __cpp_lib_chrono
+# define __cpp_lib_chrono 201907L
#endif
namespace std _GLIBCXX_VISIBILITY(default)
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 02ead8f..5264c8b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -279,8 +279,8 @@
# define __cpp_lib_barrier 201907L
# endif
#endif
-// #undef __cpp_lib_chrono
-// #define __cpp_lib_chrono 201907L
+#undef __cpp_lib_chrono
+#define __cpp_lib_chrono 201907L
// FIXME: #define __cpp_lib_execution 201902L
#define __cpp_lib_constexpr_complex 201711L
#define __cpp_lib_constexpr_dynamic_alloc 201907L
diff --git a/libstdc++-v3/testsuite/20_util/duration/arithmetic/constexpr_c++17.cc b/libstdc++-v3/testsuite/20_util/duration/arithmetic/constexpr_c++17.cc
index 8ccce35..6fe5475 100644
--- a/libstdc++-v3/testsuite/20_util/duration/arithmetic/constexpr_c++17.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/arithmetic/constexpr_c++17.cc
@@ -22,7 +22,7 @@
#ifndef __cpp_lib_chrono
# error "Feature-test macro for constexpr <chrono> missing"
-#elif __cpp_lib_chrono != 201611
+#elif __cpp_lib_chrono < 201611
# error "Feature-test macro for constexpr <chrono> has wrong value"
#endif
diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc
index ea94b06..5cbc050 100644
--- a/libstdc++-v3/testsuite/20_util/duration/io.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/io.cc
@@ -97,10 +97,110 @@ test_format()
}
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ seconds s;
+ milliseconds ms;
+ microseconds us;
+
+ std::istringstream is(" 2023-07-24 13:05");
+ VERIFY( is >> parse(" %Y-%m-%d %H:%M", s) );
+ VERIFY( is.good() );
+ VERIFY( s == 13h + 5min );
+
+ s = 999s;
+
+ is.clear();
+ is.str("Thursday July 2023");
+ VERIFY( !(is >> parse("%a %b %C%y", s)) );
+ VERIFY( ! is.eof() );
+ VERIFY( s == 999s );
+
+ is.clear();
+ is.str("27");
+ VERIFY( is >> parse("%j", s) );
+ VERIFY( is.eof() );
+ VERIFY( s == 24h * 27 );
+
+ is.clear();
+ is.str("027");
+ VERIFY( is >> parse("%j", s) );
+ VERIFY( ! is.eof() );
+ VERIFY( s == 24h * 27 );
+
+ is.clear();
+ is.str("0027");
+ VERIFY( is >> parse("%j", s) ); // defaults to %3j
+ VERIFY( is.get() == '7' );
+ VERIFY( s == 24h * 2 );
+
+ is.clear();
+ is.str("1234");
+ VERIFY( is >> parse("%2j", s) );
+ VERIFY( is.get() == '3' );
+ VERIFY( s == 24h * 12 );
+
+ is.clear();
+ is.str("001234");
+ VERIFY( is >> parse("%4j", s) );
+ VERIFY( is.get() == '3' );
+ VERIFY( s == 24h * 12 );
+
+ is.clear();
+ is.str("1234");
+ VERIFY( is >> parse("%4j", s) );
+ VERIFY( ! is.eof() );
+ VERIFY( s == 24h * 1234 );
+
+ is.clear();
+ is.str("125");
+ VERIFY( is >> parse("%S", s) );
+ VERIFY( s == 12s );
+ VERIFY( is.get() == '5' );
+
+ is.clear();
+ is.str("0.125");
+ VERIFY( is >> parse("%S", s) );
+ VERIFY( s == 0s );
+ VERIFY( is.get() == '.' );
+
+ is.clear();
+ is.str("0.125");
+ VERIFY( is >> parse("%S", ms) );
+ VERIFY( ms == 125ms );
+ VERIFY( ! is.eof() );
+
+ is.clear();
+ is.str("00.125");
+ VERIFY( is >> parse("%S", ms) );
+ VERIFY( ms == 125ms );
+ VERIFY( ! is.eof() );
+
+ is.clear();
+ is.str("012.345");
+ VERIFY( is >> parse("%S", ms) );
+ VERIFY( ms == 1000ms );
+ VERIFY( is.get() == '2' );
+
+ is.clear();
+ is.str("0.1256");
+ VERIFY( is >> parse("%S", ms) );
+ VERIFY( ms == 125ms );
+ VERIFY( is.get() == '6' );
+
+ is.clear();
+ is.str("0.0009765");
+ VERIFY( is >> parse("%S", us) );
+ VERIFY( us == 976us );
+ VERIFY( is.get() == '5' );
+}
+
int main()
{
test01();
test02();
test_format();
- // TODO: test_parse();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/clock/file/io.cc b/libstdc++-v3/testsuite/std/time/clock/file/io.cc
index c8e82bb..a6c7c71 100644
--- a/libstdc++-v3/testsuite/std/time/clock/file/io.cc
+++ b/libstdc++-v3/testsuite/std/time/clock/file/io.cc
@@ -17,7 +17,25 @@ test_ostream()
VERIFY( ss1.str() == ss2.str() );
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ const sys_seconds expected = sys_days(2023y/August/9) + 20h + 44min;
+ file_time<seconds> tp;
+
+ minutes offset;
+ std::string abbrev;
+ std::istringstream is("002023-08-09 21:44 +01 BST!");
+ VERIFY( is >> parse("%6F %R %z %Z", tp, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == clock_cast<file_clock>(expected) );
+ VERIFY( abbrev == "BST" );
+ VERIFY( offset == 60min );
+}
+
int main()
{
test_ostream();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/clock/gps/io.cc b/libstdc++-v3/testsuite/std/time/clock/gps/io.cc
index 29f3148..c4fe9be 100644
--- a/libstdc++-v3/testsuite/std/time/clock/gps/io.cc
+++ b/libstdc++-v3/testsuite/std/time/clock/gps/io.cc
@@ -6,7 +6,7 @@
#include <testsuite_hooks.h>
void
-test01()
+test_ostream()
{
using std::format;
using namespace std::chrono;
@@ -18,7 +18,25 @@ test01()
VERIFY( s == "2000-01-01 00:00:00 UTC == 2000-01-01 00:00:13 GPS" );
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ const sys_seconds expected = sys_days(2023y/August/9) + 20h + 44min + 3s;
+ gps_seconds tp;
+
+ minutes offset;
+ std::string abbrev;
+ std::istringstream is("2023-8-9 21:44:3 +1 BST#");
+ VERIFY( is >> parse("%9F %T %Oz %Z", tp, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == clock_cast<gps_clock>(expected) );
+ VERIFY( abbrev == "BST" );
+ VERIFY( offset == 60min );
+}
+
int main()
{
- test01();
+ test_ostream();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/clock/local/io.cc b/libstdc++-v3/testsuite/std/time/clock/local/io.cc
new file mode 100644
index 0000000..a7c018d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/clock/local/io.cc
@@ -0,0 +1,42 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <format>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+ using std::format;
+ using namespace std::chrono;
+
+ auto st = sys_days{2000y/January/1};
+ auto tt = clock_cast<tai_clock>(st);
+
+ auto s = format("{0:%F %T %Z} == {1:%F %T %Z}", st, tt);
+ VERIFY( s == "2000-01-01 00:00:00 UTC == 2000-01-01 00:00:32 TAI" );
+}
+
+void
+test_parse()
+{
+ using namespace std::chrono;
+ const sys_seconds expected = sys_days(2023y/August/9) + 21h + 44min;
+ local_seconds tp;
+
+ minutes offset;
+ std::string abbrev;
+ std::istringstream is("2023-8-9 21:44 +1 BST#"); // Not adjusted for offset.
+ VERIFY( is >> parse("%F %R %Oz %Z", tp, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == local_seconds(expected.time_since_epoch()) );
+ VERIFY( abbrev == "BST" );
+ VERIFY( offset == 60min );
+}
+
+int main()
+{
+ test_ostream();
+ test_parse();
+}
diff --git a/libstdc++-v3/testsuite/std/time/clock/system/io.cc b/libstdc++-v3/testsuite/std/time/clock/system/io.cc
index 7bb6851..8bfaad3 100644
--- a/libstdc++-v3/testsuite/std/time/clock/system/io.cc
+++ b/libstdc++-v3/testsuite/std/time/clock/system/io.cc
@@ -73,8 +73,81 @@ test_format()
VERIFY( smod == s );
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ sys_seconds tp, expected = sys_days(2023y/July/24) + 13h + 05min;
+
+ std::istringstream is("24-hour time: 2023-07-24 13:05");
+ VERIFY( is >> parse("24-hour time: %Y-%m-%d %H:%M", tp) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == expected );
+
+ tp = {};
+ is.clear();
+ is.str("12-hour time: 2023-07-24 1.05 PM ");
+ VERIFY( is >> parse("12-hour time: %F %I.%M %p", tp) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == expected );
+
+ tp = {};
+ is.clear();
+ is.str("2023-07-24 14:05 +01");
+ VERIFY( is >> parse("%F %H:%M %z", tp) ); // %z is used even without offset
+ VERIFY( is.eof() );
+ VERIFY( tp == expected );
+
+ tp = {};
+ minutes offset{};
+ is.clear();
+ is.str("2023-07-24 15:35 0230");
+ VERIFY( is >> parse("%F %H:%M %z", tp, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == expected );
+
+ tp = {};
+ std::string abbrev;
+ is.clear();
+ is.str("2023-07-24 08:05 -5:00 EST EST");
+ VERIFY( is >> parse("%F %H:%M %Ez %Z %Z", tp, abbrev) );
+ VERIFY( is.eof() );
+ VERIFY( tp == expected );
+ VERIFY( abbrev == "EST" );
+
+ tp = {};
+ abbrev = {};
+ offset = {};
+ is.clear();
+ is.str("2023-07-24 07:05 -06:00 ABC/+123/-456/_=");
+ VERIFY( is >> parse("%F %H:%M %Ez %Z", tp, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == expected );
+ VERIFY( offset == -360min );
+ VERIFY( abbrev == "ABC/+123/-456/_" );
+
+ tp = sys_seconds(99s);
+ offset = 99min;
+ is.clear();
+ is.str("-02:00 ");
+ VERIFY( ! (is >> parse("%Ez ", tp, offset)) );
+ VERIFY( is.fail() );
+ VERIFY( tp == sys_seconds(99s) ); // tp is only updated on successful parse.
+ VERIFY( offset == 99min ); // offset is only updated on successful parse.
+
+ tp = sys_seconds(99s);
+ abbrev = "99";
+ is.clear();
+ is.str("GMT ");
+ VERIFY( ! (is >> parse("%Z ", tp, abbrev)) );
+ VERIFY( is.fail() );
+ VERIFY( tp == sys_seconds(99s) ); // tp is only updated on successful parse.
+ VERIFY( abbrev == "99" ); // abbrev is only updated on successful parse.
+}
+
int main()
{
test_ostream();
test_format();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/clock/tai/io.cc b/libstdc++-v3/testsuite/std/time/clock/tai/io.cc
index d0255f5..530af75 100644
--- a/libstdc++-v3/testsuite/std/time/clock/tai/io.cc
+++ b/libstdc++-v3/testsuite/std/time/clock/tai/io.cc
@@ -6,7 +6,7 @@
#include <testsuite_hooks.h>
void
-test01()
+test_ostream()
{
using std::format;
using namespace std::chrono;
@@ -18,7 +18,25 @@ test01()
VERIFY( s == "2000-01-01 00:00:00 UTC == 2000-01-01 00:00:32 TAI" );
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ const sys_seconds expected = sys_days(2023y/August/9) + 20h + 44min + 3s;
+ tai_seconds tp;
+
+ minutes offset;
+ std::string abbrev;
+ std::istringstream is("8/9/23 214403 +1 BST#");
+ VERIFY( is >> parse("%D %2H%2M%2S %Oz %Z", tp, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == clock_cast<tai_clock>(expected) );
+ VERIFY( abbrev == "BST" );
+ VERIFY( offset == 60min );
+}
+
int main()
{
- test01();
+ test_ostream();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/clock/utc/io.cc b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc
index 977643f..c49f6f7 100644
--- a/libstdc++-v3/testsuite/std/time/clock/utc/io.cc
+++ b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc
@@ -114,8 +114,39 @@ test_format()
VERIFY( s == "00:00:00" );
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ const sys_seconds expected = sys_days(2023y/August/9) + 20h + 44min + 3s;
+ utc_seconds tp;
+
+ minutes offset;
+ std::string abbrev;
+ std::istringstream is("23 2210 21:44:3 +1 BST#");
+ VERIFY( is >> parse("%y %j0 %4H:%5M:%6S %Oz %Z", tp, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == clock_cast<utc_clock>(expected) );
+ VERIFY( abbrev == "BST" );
+ VERIFY( offset == 60min );
+
+ tp = {};
+ is.clear();
+ is.str("20230809214403 0100 BST:");
+ VERIFY( is >> parse("%Y%m%d%H%M%S %z %Z:", tp) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == clock_cast<utc_clock>(expected) );
+
+ is.clear();
+ is.str("2023-W32-3 20:44:03");
+ VERIFY( is >> parse("%G-W%V-%u %T", tp) );
+ VERIFY( ! is.eof() );
+ VERIFY( tp == clock_cast<utc_clock>(expected) );
+}
+
int main()
{
test_ostream();
test_format();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/day/io.cc b/libstdc++-v3/testsuite/std/time/day/io.cc
index 6158230..d8691b72 100644
--- a/libstdc++-v3/testsuite/std/time/day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/day/io.cc
@@ -67,9 +67,67 @@ test_format()
}
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ day d(0);
+ minutes offset;
+ std::string abbrev;
+ std::istringstream is("2023-08-10 12:46 +01 BST<");
+ VERIFY( is >> parse("%F %R %z %Z", d, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( d == 10d );
+ VERIFY( abbrev == "BST" );
+ VERIFY( offset == 60min );
+
+ abbrev = "nope";
+ offset = 999min;
+ is.clear();
+ is.str("30");
+ VERIFY( is >> parse("%d", d, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( d == 30d );
+ VERIFY( abbrev == "nope" );
+ VERIFY( offset == 999min );
+
+ d = day(255);
+ is.clear();
+ is.str("2023-02-30");
+ is >> parse("%F", d); // Feb 30 is not a valid day
+ VERIFY( is.fail() );
+ VERIFY( d == day(255) );
+
+ is.clear();
+ is.str("February 30");
+ is >> parse("%B %d", d); // Feb 30 is not a valid day
+ VERIFY( is.fail() );
+ VERIFY( d == day(255) );
+
+ is.clear();
+ is.str("February 29");
+ is >> parse("%B %d", d); // But Feb 29 could be valid.
+ VERIFY( is.good() );
+ VERIFY( d == 29d );
+
+ d = day(255);
+ is.clear();
+ is.str("2023 Feb 29");
+ is >> parse("%Y %B %d", d); // But 2023 is not a leap year.
+ VERIFY( is.fail() );
+ VERIFY( d == day(255) );
+
+ d = day(255);
+ is.clear();
+ is.str("20 Feb 29");
+ is >> parse("%y %B %d", d); // But 2020 is a leap year.
+ VERIFY( is.good() );
+ VERIFY( d == 29d );
+}
+
int main()
{
test_ostream();
test_format();
- // TODO: test_parse();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/month/io.cc b/libstdc++-v3/testsuite/std/time/month/io.cc
index 7ceeafd..9cf5b05 100644
--- a/libstdc++-v3/testsuite/std/time/month/io.cc
+++ b/libstdc++-v3/testsuite/std/time/month/io.cc
@@ -90,9 +90,129 @@ test_format()
}
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ std::istringstream is;
+ month m{};
+
+ is.str("JUL");
+ VERIFY( is >> parse("%B", m) );
+ VERIFY( is.eof() );
+ VERIFY( m == July );
+
+ is.clear();
+ is.str("junE bug");
+ VERIFY( is >> parse(" %b bug ", m) );
+ VERIFY( is.eof() );
+ VERIFY( m == June );
+
+ is.clear();
+ is.str("012");
+ VERIFY( is >> parse("%m", m) );
+ VERIFY( ! is.eof() );
+ VERIFY( is.peek() == '2' );
+ VERIFY( m == January );
+
+ is.clear();
+ is.str("012");
+ VERIFY( is >> parse("%4m", m) );
+ VERIFY( is.eof() );
+ VERIFY( m == December );
+
+ m = month(15);
+ is.clear();
+ is.str("Janvember");
+ VERIFY( is >> parse("%B", m) ); // Stops parsing after "Jan"
+ VERIFY( ! is.eof() );
+ VERIFY( is.peek() == 'v' );
+ VERIFY( m == January );
+
+ m = month(15);
+ is.clear();
+ is.str("Junuary");
+ VERIFY( is >> parse("%B", m) ); // Stops parsing after "Jun"
+ VERIFY( ! is.eof() );
+ VERIFY( is.peek() == 'u' );
+ VERIFY( m == June );
+
+ m = month(15);
+ is.clear();
+ is.str("Jebruary");
+ VERIFY( ! (is >> parse("%B", m)) );
+ VERIFY( is.fail() );
+ VERIFY( ! is.eof() );
+ is.clear();
+ VERIFY( is.peek() == 'e' );
+ VERIFY( m == month(15) );
+
+ m = month(13);
+ is.clear();
+ is.str("2023-6-31");
+ VERIFY( ! (is >> parse("%F", m)) ); // June only has 30 days.
+ VERIFY( ! is.eof() );
+ VERIFY( m == month(13) );
+
+ m = month(14);
+ is.clear();
+ is.str("2023-2-29");
+ VERIFY( ! (is >> parse("%Y-%m-%e", m)) ); // Feb only has 28 days in 2023.
+ VERIFY( ! is.eof() );
+ VERIFY( m == month(14) );
+
+ is.clear();
+ is.str("2-29");
+ VERIFY( is >> parse("%m-%d", m) ); // But Feb has 29 days in some years.
+ VERIFY( ! is.eof() );
+ VERIFY( m == February );
+
+ m = month(14);
+ is.clear();
+ is.str("6-31");
+ VERIFY( ! (is >> parse("%m-%d", m)) ); // June only has 30 days in all years.
+ VERIFY( ! is.eof() );
+ VERIFY( m == month(14) );
+
+ m = month(15);
+ is.clear();
+ is.str("2023-13-1");
+ VERIFY( ! (is >> parse("%F", m)) );
+ VERIFY( is.eof() );
+ VERIFY( m == month(15) );
+
+ m = month(16);
+ is.clear();
+ is.str("13/1/23");
+ VERIFY( ! (is >> parse("%D", m)) );
+ VERIFY( m == month(16) );
+
+ m = month(17);
+ is.clear();
+ is.str("13");
+ VERIFY( ! (is >> parse("%m", m)) );
+ VERIFY( ! is.eof() );
+ VERIFY( m == month(17) );
+
+ m = month(18);
+ is.clear();
+ is.str("1234");
+ VERIFY( ! (is >> parse("%3m", m)) );
+ VERIFY( ! is.eof() );
+ is.clear();
+ VERIFY( is.peek() == '4' );
+ VERIFY( m == month(18) );
+
+ is.clear();
+ is.str("2023-W32-5");
+ VERIFY( is >> parse("%G-W%V-%u", m) );
+ VERIFY( ! is.eof() );
+ VERIFY( m == August );
+}
+
int main()
{
test_ostream();
test_format();
- // TODO: test_parse();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/month_day/io.cc b/libstdc++-v3/testsuite/std/time/month_day/io.cc
index 454231d..a3f4599 100644
--- a/libstdc++-v3/testsuite/std/time/month_day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/month_day/io.cc
@@ -22,9 +22,86 @@ test_ostream()
VERIFY( ss.str() == "juil./27" );
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ std::istringstream is;
+ month_day md{};
+
+ is.str("jul 0123");
+ VERIFY( is >> parse("%B %3e", md) );
+ VERIFY( ! is.eof() );
+ VERIFY( is.peek() == '3' );
+ VERIFY( md == July/12 );
+
+ is.str("August 11");
+ VERIFY( is >> parse("%b %d", md) );
+ VERIFY( ! is.eof() );
+ VERIFY( md == August/11 );
+
+ is.clear();
+ is.str("012");
+ VERIFY( is >> parse("%m%2d", md) );
+ VERIFY( is.eof() );
+ VERIFY( md == January/2 );
+
+ is.clear();
+ is.str("012/311");
+ VERIFY( is >> parse("%4m/%d", md) );
+ VERIFY( ! is.eof() );
+ VERIFY( md == December/31 );
+
+ is.clear();
+ is.str("2023-7-31");
+ VERIFY( is >> parse("%F", md) );
+ VERIFY( ! is.eof() );
+ VERIFY( md == July/31 );
+
+ md = month(13)/day(32);
+ is.clear();
+ is.str("2023-13-1");
+ VERIFY( ! (is >> parse("%F", md)) );
+ VERIFY( is.eof() );
+ VERIFY( md == month(13)/day(32) );
+
+ md = month(13)/day(33);
+ is.clear();
+ is.str("2023-6-31");
+ VERIFY( ! (is >> parse("%F", md)) ); // June only has 30 days.
+ VERIFY( ! is.eof() );
+ VERIFY( md == month(13)/day(33) );
+
+ md = month(13)/day(34);
+ is.clear();
+ is.str("6-31");
+ VERIFY( ! (is >> parse("%m-%d", md)) ); // June only has 30 days in any year.
+ VERIFY( ! is.eof() );
+ VERIFY( md == month(13)/day(34) );
+
+ md = month(13)/day(35);
+ is.clear();
+ is.str("2023-2-29");
+ VERIFY( ! (is >> parse("%Y-%m-%e", md)) ); // Feb only has 28 days in 2023.
+ VERIFY( ! is.eof() );
+ VERIFY( md == month(13)/day(35) );
+
+ is.clear();
+ is.str("2-29");
+ VERIFY( is >> parse("%m-%d", md) ); // But Feb has 29 days in some years.
+ VERIFY( ! is.eof() );
+ VERIFY( md == February/29 );
+
+ is.clear();
+ is.str("2023-W32-5");
+ VERIFY( is >> parse("%G-W%V-%u", md) );
+ VERIFY( ! is.eof() );
+ VERIFY( md == August/11 );
+}
+
int main()
{
test_ostream();
// TODO: test_format();
- // TODO: test_parse();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/parse.cc b/libstdc++-v3/testsuite/std/time/parse.cc
new file mode 100644
index 0000000..9b36c5d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/parse.cc
@@ -0,0 +1,309 @@
+// { dg-do run { target c++20 } }
+// { dg-options "-std=gnu++20" }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+template<typename T, typename CharT>
+ concept stream_extractable
+ = requires (std::basic_istream<CharT>& is) { is >> std::declval<T>(); };
+
+void
+test_recommended_practice()
+{
+ std::chrono::seconds s;
+ using parse_manip = decltype(std::chrono::parse("", s));
+ static_assert( stream_extractable<parse_manip, char> );
+ static_assert( not stream_extractable<parse_manip, wchar_t> );
+ using wparse_manip = decltype(std::chrono::parse(L"", s));
+ static_assert( stream_extractable<wparse_manip, wchar_t> );
+ static_assert( not stream_extractable<wparse_manip, char> );
+
+ // These properties are recommended by the standard, to avoid using a
+ // parse manipulator that has a dangling reference to a format string.
+ static_assert( not std::is_move_constructible_v<parse_manip> );
+ static_assert( not std::is_move_assignable_v<parse_manip> );
+ static_assert( not stream_extractable<parse_manip&, char> );
+ static_assert( not stream_extractable<const parse_manip&, char> );
+ static_assert( not stream_extractable<wparse_manip&, wchar_t> );
+ static_assert( not stream_extractable<const wparse_manip&, wchar_t> );
+}
+
+template<typename... Args>
+ concept parsable = requires(Args... args) { std::chrono::parse(args...); };
+
+const std::string f = "format string";
+
+namespace N
+{
+ struct A { };
+
+ void
+ from_stream(std::istream&, const char* fmt, A&)
+ {
+ VERIFY( fmt == f.c_str() );
+ }
+
+ template<typename... Args>
+ void
+ from_stream(std::istream&, const char*, A&, void*, void*) = delete;
+
+ struct B { };
+
+ void
+ from_stream(std::istream&, const char* fmt, B&, std::string* abbrev)
+ {
+ VERIFY( fmt == f.c_str() );
+ VERIFY( abbrev != nullptr );
+ }
+
+ void
+ from_stream(std::istream&, const char*, B&, std::string*, void*) = delete;
+
+ struct C { };
+
+ void
+ from_stream(std::istream&, const char* fmt, C&, std::string* abbrev,
+ std::chrono::minutes* offset)
+ {
+ VERIFY( fmt == f.c_str() );
+ VERIFY( abbrev == nullptr );
+ VERIFY( offset != nullptr );
+ }
+
+ struct D { };
+
+ void
+ from_stream(std::istream&, const char* fmt, D&, std::string* abbrev,
+ std::chrono::minutes* offset)
+ {
+ VERIFY( fmt == f.c_str() );
+ VERIFY( abbrev != nullptr );
+ VERIFY( offset != nullptr );
+ }
+
+ struct E { };
+
+ void
+ from_stream(std::wistream&, const wchar_t*, E&, std::wstring* = nullptr,
+ std::chrono::minutes* = nullptr)
+ { }
+}
+
+void
+test_adl()
+{
+ using std::string;
+ using std::wstring;
+ using std::chrono::minutes;
+
+ string abbrev;
+ minutes offset;
+
+ // Check that valid calls are well-formed.
+ N::A a;
+ (void) std::chrono::parse(f, a);
+ N::B b;
+ (void) std::chrono::parse(f, b, abbrev);
+ N::C c;
+ (void) std::chrono::parse(f, c, offset);
+ // This satisfies the concept, but would fail the VERIFY assertion:
+ static_assert( parsable<const char*, N::C, string, minutes> );
+ N::D d;
+ (void) std::chrono::parse(f, d, abbrev, offset);
+ // This satisfies the concept, but would fail the VERIFY assertion:
+ static_assert( parsable<const char*, N::D, minutes> );
+
+ // Wide strings.
+ static_assert( parsable<const wchar_t*, N::E, wstring> );
+ static_assert( parsable<const wchar_t*, N::E, wstring> );
+ static_assert( parsable<const wchar_t*, N::E, minutes> );
+ static_assert( parsable<const wchar_t*, N::E, wstring, minutes> );
+
+ // Check that invalid calls are properly constrained.
+
+ // from_stream is only overloaded for N::A without abbrev or offset.
+ static_assert( not parsable<const char*, N::A, std::string> );
+ static_assert( not parsable<const char*, N::A, minutes> );
+ static_assert( not parsable<const char*, N::A, string, minutes> );
+ // from_stream is only overloaded for N::B with abbrev.
+ static_assert( not parsable<const char*, N::B> );
+ static_assert( not parsable<const char*, N::B, minutes> );
+ static_assert( not parsable<const char*, N::B, string, minutes> );
+ // from_stream is only overloaded for N::C with abbrev and minutes.
+ static_assert( not parsable<const char*, N::C> );
+ static_assert( not parsable<const char*, N::C, string> );
+ // from_stream is only overloaded for N::D with abbrev and minutes.
+ static_assert( not parsable<const char*, N::D> );
+ static_assert( not parsable<const char*, N::D, string> );
+
+ // Mismatched strings
+ static_assert( not parsable<string, std::chrono::year, wstring> );
+ static_assert( not parsable<string, std::chrono::year, wstring, minutes> );
+
+ using Alloc = __gnu_test::SimpleAllocator<char>;
+ using String = std::basic_string<char, std::char_traits<char>, Alloc>;
+ // Custom allocator
+ static_assert( parsable<String, std::chrono::year> );
+ static_assert( parsable<String, std::chrono::year, String> );
+ static_assert( parsable<String, std::chrono::year, minutes> );
+ static_assert( parsable<String, std::chrono::year, String, minutes> );
+ static_assert( parsable<const char*, std::chrono::year, String> );
+ static_assert( parsable<const char*, std::chrono::year, String, minutes> );
+ // Mismatched allocators
+ static_assert( not parsable<string, std::chrono::year, String> );
+ static_assert( not parsable<string, std::chrono::year, String, minutes> );
+ static_assert( not parsable<String, std::chrono::year, string> );
+ static_assert( not parsable<String, std::chrono::year, string, minutes> );
+}
+
+void
+test_whitespace()
+{
+ using namespace std::chrono_literals;
+ std::chrono::minutes min;
+ std::istringstream is;
+ is.str(" a b 1 ");
+ is >> parse(" a b %M", min);
+ VERIFY( is.good() );
+ VERIFY( min == 1min );
+ is.str(" a b 1 ");
+ is >> parse(" a b %M ", min);
+ VERIFY( is.eof() && !is.fail() );
+ VERIFY( min == 1min );
+ is.clear();
+ is.str(" 1");
+ is >> parse(" %n%M%n", min);
+ VERIFY( is.fail() );
+ is.clear();
+ is.str(" a b 1 ");
+ is >> parse("%n a%n%nb %t%M%n", min);
+ VERIFY( is.good() );
+ VERIFY( min == 1min );
+ is.str("a b 1 ");
+ is >> parse("%ta b %M%n%t", min);
+ VERIFY( is.good() );
+ VERIFY( min == 1min );
+ is.str("1 ");
+ is >> parse("%M%n%t%t", min);
+ VERIFY( is.eof() && !is.fail() );
+ VERIFY( min == 1min );
+ is.clear();
+ is.str("1 ");
+ is >> parse("%M%n%t%t", min);
+ VERIFY( is.eof() && !is.fail() );
+ VERIFY( min == 1min );
+ is.clear();
+ is.str("1 ");
+ is >> parse("%M%n%t%n", min);
+ VERIFY( is.eof() && is.fail() );
+ VERIFY( min == 1min );
+}
+
+void
+test_errors()
+{
+ using namespace std::chrono_literals;
+ std::chrono::minutes min(999);
+ std::chrono::year y(-1);
+ std::istringstream is;
+
+ is.str("x");
+ is >> parse("x", min); // Matches expected pattern, but no minutes present.
+ VERIFY( !is.eof() && is.fail() );
+ VERIFY( min == 999min );
+
+ is.clear();
+ is.str("x");
+ is >> parse("%M", min); // Doesn't match expected pattern.
+ VERIFY( !is.eof() && is.fail() );
+ VERIFY( min == 999min );
+
+ is.clear();
+ is.str("001:002");
+ is >> parse("%H:%M", min); // Extracts "00" then fails to find ':' next.
+ VERIFY( !is.eof() && is.fail() );
+ VERIFY( min == 999min );
+
+ is.clear();
+ is.str("12:61");
+ is >> parse("%H:%M", min); // 61min is out of range.
+ VERIFY( !is.eof() && is.fail() );
+ VERIFY( min == 999min );
+
+ is.clear();
+ is.str("12:15 100");
+ is >> parse("%H:%M %3y", min); // 100y is out of range for %y but not needed
+ VERIFY( is.good() );
+ VERIFY( min == (12h + 15min) );
+
+ min = 999min;
+ is.clear();
+ is.str("12:15 100");
+ is >> parse("%H:%M %3y", y); // 100y is out of range for %y and needed
+ VERIFY( is.fail() );
+ VERIFY( y == -1y );
+
+ is.clear();
+ is.str("23:61 10");
+ is >> parse("%H:%M %3y", y); // 61min is out of range but not needed
+ VERIFY( is.eof() && ! is.fail() );
+ VERIFY( y == 2010y );
+}
+
+void
+test_modifiers()
+{
+ using namespace std::chrono_literals;
+ std::chrono::minutes min;
+ std::istringstream is;
+
+ is.str("0001:000002");
+ is >> parse("%4H:%5M", min);
+ VERIFY( is.good() );
+ VERIFY( min == 60min );
+
+ is.str("0001:000002");
+ is >> parse("%6H:%6M", min);
+ VERIFY( is.good() );
+ VERIFY( min == 62min );
+
+ is.str("002");
+ is >> parse("%4M", min);
+ VERIFY( is.eof() && !is.fail() );
+ VERIFY( min == 2min );
+
+ is.clear();
+ is.str("0061");
+ is >> parse("%3M", min);
+ VERIFY( is.good() );
+ VERIFY( min == 6min );
+
+ is.clear();
+ is.str("0061");
+ is >> parse("%4M", min);
+ VERIFY( !is.eof() && is.fail() );
+
+ is.clear();
+ is.str("0061");
+ is >> parse("%5M", min);
+ VERIFY( is.eof() && is.fail() );
+
+ std::chrono::seconds s;
+ is.clear();
+ is.str("000000000012345");
+ is >> parse("%12S", s); // Read more than 10 digits to check overflow logic.
+ VERIFY( is.good() );
+ VERIFY( s == 12s );
+}
+
+int main()
+{
+ test_recommended_practice();
+ test_adl();
+ test_whitespace();
+ test_errors();
+ test_modifiers();
+}
diff --git a/libstdc++-v3/testsuite/std/time/syn_c++20.cc b/libstdc++-v3/testsuite/std/time/syn_c++20.cc
index 570471a..307d84e 100644
--- a/libstdc++-v3/testsuite/std/time/syn_c++20.cc
+++ b/libstdc++-v3/testsuite/std/time/syn_c++20.cc
@@ -22,9 +22,8 @@
#ifndef __cpp_lib_chrono
# error "Feature test macro for chrono is missing in <chrono>"
-// FIXME
-// #elif __cpp_lib_chrono < 201907L
-// # error "Feature test macro for chrono has wrong value in <chrono>"
+#elif __cpp_lib_chrono < 201907L
+# error "Feature test macro for chrono has wrong value in <chrono>"
#endif
namespace __gnu_test
@@ -126,8 +125,8 @@ namespace __gnu_test
using std::chrono::local_time_format;
- // FIXME
- // using std::chrono::parse;
+ using std::chrono::from_stream;
+ using std::chrono::parse;
using std::chrono::last;
using std::chrono::Sunday;
diff --git a/libstdc++-v3/testsuite/std/time/weekday/io.cc b/libstdc++-v3/testsuite/std/time/weekday/io.cc
index 6cdb984..ba9dce0 100644
--- a/libstdc++-v3/testsuite/std/time/weekday/io.cc
+++ b/libstdc++-v3/testsuite/std/time/weekday/io.cc
@@ -93,9 +93,85 @@ test_format()
}
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ std::istringstream is;
+ weekday wd{};
+
+ is.str("fRi funday");
+ VERIFY( is >> std::chrono::parse(" %A funday", wd) );
+ VERIFY( wd == Friday );
+
+ is.str("MONDAY xxx");
+ VERIFY( is >> std::chrono::parse(" %a xxx ", wd) );
+ VERIFY( wd == Monday );
+
+ is.clear();
+ is.str("1");
+ VERIFY( is >> std::chrono::parse("%u", wd) );
+ VERIFY( wd == Monday );
+ is.clear();
+ is.str("7");
+ VERIFY( is >> std::chrono::parse("%u", wd) );
+ VERIFY( wd == Sunday );
+ wd = weekday(99);
+ is.clear();
+ is.str("0");
+ VERIFY( ! (is >> std::chrono::parse("%u", wd)) );
+ VERIFY( wd == weekday(99) );
+ is.clear();
+ is.str("8");
+ VERIFY( ! (is >> std::chrono::parse("%u", wd)) );
+ VERIFY( wd == weekday(99) );
+
+ is.clear();
+ is.str("003");
+ VERIFY( is >> std::chrono::parse("%3u", wd) );
+ VERIFY( wd == Wednesday );
+ wd = weekday(99);
+ is.clear();
+ is.str("004");
+ VERIFY( ! (is >> std::chrono::parse("%2u", wd)) );
+ VERIFY( wd == weekday(99) );
+
+ is.clear();
+ is.str("1");
+ VERIFY( is >> std::chrono::parse("%w", wd) );
+ VERIFY( wd == Monday );
+ is.clear();
+ is.str("0");
+ VERIFY( is >> std::chrono::parse("%w", wd) );
+ VERIFY( wd == Sunday );
+ wd = weekday(99);
+ is.clear();
+ is.str("7");
+ VERIFY( ! (is >> std::chrono::parse("%w", wd)) );
+ VERIFY( wd == weekday(99) );
+ is.clear();
+ is.str("8");
+ VERIFY( ! (is >> std::chrono::parse("%w", wd)) );
+ VERIFY( wd == weekday(99) );
+
+ is.clear();
+ is.str("003");
+ VERIFY( is >> std::chrono::parse("%3w", wd) );
+ VERIFY( wd == Wednesday );
+ is.clear();
+ is.str("004");
+ VERIFY( is >> std::chrono::parse("%2w", wd) );
+ VERIFY( wd == Sunday );
+
+ is.clear();
+ is.str("2023-8-11");
+ VERIFY( is >> std::chrono::parse("%F", wd) );
+ VERIFY( wd == Friday );
+}
+
int main()
{
test_ostream();
test_format();
- // TODO: test_parse();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/year/io.cc b/libstdc++-v3/testsuite/std/time/year/io.cc
index 07316e9..0c47464 100644
--- a/libstdc++-v3/testsuite/std/time/year/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year/io.cc
@@ -81,9 +81,81 @@ test_format()
}
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ year y;
+
+ std::istringstream is("2023");
+ VERIFY( is >> parse("%Y", y) );
+ VERIFY( ! is.eof() );
+ VERIFY( y == year(2023) );
+
+ is.clear();
+ is.str("2023");
+ VERIFY( is >> parse("%5Y", y) );
+ VERIFY( is.eof() );
+ VERIFY( y == year(2023) );
+
+ is.clear();
+ is.str("2023");
+ VERIFY( is >> parse("%2Y", y) );
+ VERIFY( ! is.eof() );
+ VERIFY( y == year(20) );
+
+ is.clear();
+ is.str("2023");
+ VERIFY( is >> parse("%y", y) );
+ VERIFY( ! is.eof() );
+ VERIFY( y == year(2020) );
+
+ minutes offset;
+ std::string abbrev;
+
+ is.clear();
+ is.str("23 20 25:61 +1:30 WAT"); // Invalid %H:%M doesn't matter for year.
+ VERIFY( is >> parse("%y %C %H:%M %Oz %Z", y, abbrev, offset) );
+ VERIFY( is.eof() );
+ VERIFY( y == year(2023) );
+ VERIFY( abbrev == "WAT" );
+ VERIFY( offset == 90min );
+
+ is.clear();
+ is.str("2022 367");
+ VERIFY( is >> parse("%Y %j", y) ); // Invalid day-of-year doesn't matter.
+ VERIFY( ! is.eof() );
+ VERIFY( y == 2022y );
+
+ y = 999y;
+ is.clear();
+ is.str("2023");
+ VERIFY( ! (is >> parse("%G", y)) ); // ISO year not aligned with Gregorian.
+ VERIFY( y == 999y );
+
+ is.clear();
+ is.str("2023-W01-1"); // 2023-1-2
+ is >> parse("%G-W%V-%u", y); // Can get Gregorian year from full ISO date.
+ VERIFY( ! is.eof() );
+ VERIFY( y == 2023y );
+
+ is.clear();
+ is.str("2022-W052-7"); // 2023-1-1
+ is >> parse("%G-W%3V-%2u", y);
+ VERIFY( is.eof() );
+ VERIFY( y == 2023y );
+
+ y = year(1);
+ is.clear();
+ is.str("2023 01");
+ VERIFY( !( is >> parse("%Y %z xx", y)) ); // Gets EOF and can't parse " xx".
+ VERIFY( is.eof() );
+ VERIFY( y == year(1) );
+}
+
int main()
{
test_ostream();
test_format();
- // TODO: test_parse();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/year_month/io.cc b/libstdc++-v3/testsuite/std/time/year_month/io.cc
index 8c0eb9b..8dac248 100644
--- a/libstdc++-v3/testsuite/std/time/year_month/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year_month/io.cc
@@ -22,9 +22,57 @@ test_ostream()
VERIFY( ss.str() == "2023/juil." );
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ year_month ym;
+
+ std::istringstream is("20238");
+ VERIFY( is >> parse("%Y%m", ym) );
+ VERIFY( is.eof() );
+ VERIFY( ym == 2023y/August );
+
+ ym = 1y/January;
+ is.clear();
+ is.str("20238");
+ VERIFY( ! (is >> parse("%5Y%m", ym)) );
+ VERIFY( is.eof() );
+ VERIFY( ym == 1y/January );
+
+ is.clear();
+ is.str("2023");
+ VERIFY( is >> parse("%2Y%1m", ym) );
+ VERIFY( ! is.eof() );
+ VERIFY( ym == 20y/February );
+
+ is.clear();
+ is.str("2012");
+ VERIFY( is >> parse("%y%m", ym) );
+ VERIFY( ! is.eof() );
+ VERIFY( ym == 2020y/December );
+
+ minutes offset;
+ std::string abbrev;
+
+ is.clear();
+ is.str("4/1/20 25:61 +1:30 WAT"); // Invalid %H:%M doesn't matter for year_mon
+ VERIFY( is >> parse("%D %H:%M %Oz %Z", ym, abbrev, offset) );
+ VERIFY( is.eof() );
+ VERIFY( ym == 2020y/April );
+ VERIFY( abbrev == "WAT" );
+ VERIFY( offset == 90min );
+
+ is.clear();
+ is.str("02022-W052-7");
+ is >> parse("%6G-W%4V-%2u", ym);
+ VERIFY( is.eof() );
+ VERIFY( ym == 2023y/January );
+}
+
int main()
{
test_ostream();
// TODO: test_format();
- // TODO: test_parse();
+ test_parse();
}
diff --git a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
index 688885b..6c30a87 100644
--- a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
@@ -113,9 +113,72 @@ test_format()
}
}
+void
+test_parse()
+{
+ using namespace std::chrono;
+ const year_month_day expected = 2023y/August/10;
+ year_month_day ymd;
+
+ minutes offset;
+ std::string abbrev;
+ std::istringstream is("23 2220 21:44:3 +1 'BST'");
+ VERIFY( is >> parse("%y %j0 %4H:%5M:%6S %Oz '%Z'", ymd, abbrev, offset) );
+ VERIFY( ! is.eof() );
+ VERIFY( ymd == expected );
+ VERIFY( abbrev == "BST" );
+ VERIFY( offset == 60min );
+
+ is.clear();
+ is.str("2023 365");
+ VERIFY( is >> parse("%Y %j", ymd) );
+ VERIFY( ymd == 2023y/December/31 );
+
+ ymd = 1970y/January/1;
+ is.clear();
+ is.str("2023 366");
+ VERIFY( ! (is >> parse("%Y %j", ymd)) ); // Not a leap year, no 366th day.
+ VERIFY( ymd == 1970y/January/1 );
+
+ is.clear();
+ is.str("2020 366");
+ VERIFY( is >> parse("%Y %j", ymd) );
+ VERIFY( ! is.eof() );
+ VERIFY( ymd == 2020y/December/31 );
+
+ ymd = 1970y/January/1;
+ is.clear();
+ is.str("2020 0");
+ VERIFY( ! (is >> parse("%Y %j", ymd)) ); // zero is invalid for day-of-year
+ VERIFY( is.eof() );
+ VERIFY( ymd == 1970y/January/1 );
+
+ is.clear();
+ is.str("2023-01-01 00:30 0100");
+ VERIFY( is >> parse("%F %R %z", ymd) );
+ VERIFY( ! is.eof() );
+ VERIFY( ymd == 2023y/January/1 ); // Date not adjusted by TZ offset.
+
+ ymd = {};
+ is.clear();
+ is.str("2022-W52-6");
+ VERIFY( is >> parse("%G-W%V-%u", ymd) );
+ VERIFY( ymd == 2022y/December/31 );
+
+ is.clear();
+ is.str("2022-W52-8");
+ VERIFY( ! (is >> parse("%G-W%V-%u", ymd)) ); // 8 is not a valid weekday
+ is.clear();
+ is.str("2022-W52-0");
+ VERIFY( ! (is >> parse("%G-W%V-%u", ymd)) ); // 0 is not a valid weekday
+ is.clear();
+ is.str("2022-W53-1");
+ VERIFY( ! (is >> parse("%G-W%V-%u", ymd)) ); // W53 is not valid for 2022
+}
+
int main()
{
test_ostream();
test_format();
- // TODO: test_parse();
+ test_parse();
}