Skip to content

Commit 70e13f0

Browse files
committed
Report an error when timezone is not available
1 parent b9e0e94 commit 70e13f0

2 files changed

Lines changed: 36 additions & 9 deletions

File tree

include/fmt/chrono.h

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,8 @@ inline auto localtime(std::time_t time) -> std::tm {
574574
#if FMT_USE_LOCAL_TIME
575575
template <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

956968
inline 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)>
10051017
inline 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()) {

test/chrono-test.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,12 @@ TEST(chrono_test, local_time) {
385385
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
386386
}
387387

388-
// TODO: check that %z and %Z result in an error
388+
EXPECT_THROW_MSG(
389+
fmt::format(fmt::runtime("{:%z}"), time), fmt::format_error,
390+
"no timezone");
391+
EXPECT_THROW_MSG(
392+
fmt::format(fmt::runtime("{:%Z}"), time), fmt::format_error,
393+
"no timezone");
389394
}
390395

391396
TEST(chrono_test, daylight_savings_time_end) {

0 commit comments

Comments
 (0)