@@ -574,7 +574,8 @@ inline auto localtime(std::time_t time) -> std::tm {
574574#if FMT_USE_LOCAL_TIME
575575template <typename Duration,
576576 FMT_ENABLE_IF (detail::has_current_zone<Duration>())>
577- FMT_DEPRECATED inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
577+ FMT_DEPRECATED inline auto localtime(std::chrono::local_time<Duration> time)
578+ -> std::tm {
578579 using namespace std ::chrono;
579580 using namespace fmt_detail ;
580581 return localtime (detail::to_time_t (current_zone ()->to_sys <Duration>(time)));
@@ -911,7 +912,14 @@ template <typename Derived> struct null_chrono_spec_handler {
911912 FMT_CONSTEXPR void on_tz_name () { unsupported (); }
912913};
913914
914- struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
915+ class tm_format_checker : public null_chrono_spec_handler <tm_format_checker> {
916+ private:
917+ bool no_timezone_ = false ;
918+
919+ public:
920+ explicit tm_format_checker (bool no_timezone = false )
921+ : no_timezone_(no_timezone) {}
922+
915923 FMT_NORETURN inline void unsupported () {
916924 FMT_THROW (format_error (" no format" ));
917925 }
@@ -949,8 +957,12 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
949957 FMT_CONSTEXPR void on_24_hour_time () {}
950958 FMT_CONSTEXPR void on_iso_time () {}
951959 FMT_CONSTEXPR void on_am_pm () {}
952- FMT_CONSTEXPR void on_utc_offset (numeric_system) {}
953- FMT_CONSTEXPR void on_tz_name () {}
960+ FMT_CONSTEXPR void on_utc_offset (numeric_system) {
961+ if (no_timezone_) FMT_THROW (format_error (" no timezone" ));
962+ }
963+ FMT_CONSTEXPR void on_tz_name () {
964+ if (no_timezone_) FMT_THROW (format_error (" no timezone" ));
965+ }
954966};
955967
956968inline auto tm_wday_full_name (int wday) -> const char* {
@@ -1005,7 +1017,7 @@ template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
10051017inline auto to_nonnegative_int (T value, Int upper) -> Int {
10061018 if (!std::is_unsigned<Int>::value &&
10071019 (value < 0 || to_unsigned (value) > to_unsigned (upper))) {
1008- FMT_THROW (fmt:: format_error (" chrono value is out of range" ));
1020+ FMT_THROW (format_error (" chrono value is out of range" ));
10091021 }
10101022 return static_cast <Int>(value);
10111023}
@@ -2242,8 +2254,8 @@ template <typename Char> struct formatter<std::tm, Char> {
22422254 ctx.out (), basic_string_view<Char>(buf.data (), buf.size ()), specs);
22432255 }
22442256
2245- public:
2246- FMT_CONSTEXPR auto parse (parse_context<Char>& ctx ) -> const Char* {
2257+ FMT_CONSTEXPR auto do_parse (parse_context<Char>& ctx,
2258+ bool no_timezone = false ) -> const Char* {
22472259 auto it = ctx.begin (), end = ctx.end ();
22482260 if (it == end || *it == ' }' ) return it;
22492261
@@ -2256,12 +2268,18 @@ template <typename Char> struct formatter<std::tm, Char> {
22562268 if (it == end) return it;
22572269 }
22582270
2259- end = detail::parse_chrono_format (it, end, detail::tm_format_checker ());
2271+ end = detail::parse_chrono_format (it, end,
2272+ detail::tm_format_checker (no_timezone));
22602273 // Replace the default format string only if the new spec is not empty.
22612274 if (end != it) fmt_ = {it, detail::to_unsigned (end - it)};
22622275 return end;
22632276 }
22642277
2278+ public:
2279+ FMT_CONSTEXPR auto parse (parse_context<Char>& ctx) -> const Char* {
2280+ return do_parse (ctx);
2281+ }
2282+
22652283 template <typename FormatContext>
22662284 auto format (const std::tm& tm, FormatContext& ctx) const
22672285 -> decltype(ctx.out()) {
@@ -2317,6 +2335,10 @@ struct formatter<local_time<Duration>, Char> : formatter<std::tm, Char> {
23172335 this ->fmt_ = detail::string_literal<Char, ' %' , ' F' , ' ' , ' %' , ' T' >();
23182336 }
23192337
2338+ FMT_CONSTEXPR auto parse (parse_context<Char>& ctx) -> const Char* {
2339+ return this ->do_parse (ctx, true );
2340+ }
2341+
23202342 template <typename FormatContext>
23212343 auto format (local_time<Duration> val, FormatContext& ctx) const
23222344 -> decltype(ctx.out()) {
0 commit comments