Skip to content

Commit e10e922

Browse files
committed
zoned_time
1 parent 0e078f6 commit e10e922

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

CMakeLists.txt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,65 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
265265
set(WERROR_FLAG -Werror)
266266
endif ()
267267

268+
include(CheckCXXSourceCompiles)
269+
include(CheckCXXCompilerFlag)
270+
271+
# Set C++ standard for main build
272+
set(CMAKE_CXX_STANDARD 20)
273+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
274+
275+
# Check compiler-specific C++20 support
276+
if (MSVC)
277+
set(CMAKE_REQUIRED_FLAGS "/std:c++20")
278+
check_cxx_compiler_flag("/std:c++20" HAVE_CXX20_FLAG)
279+
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
280+
set(CMAKE_REQUIRED_FLAGS "-std=c++20")
281+
check_cxx_compiler_flag("-std=c++20" HAVE_CXX20_FLAG)
282+
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
283+
set(CMAKE_REQUIRED_FLAGS "-std=c++20")
284+
check_cxx_compiler_flag("-std=c++20" HAVE_CXX20_FLAG)
285+
else ()
286+
# Fallback for other compilers
287+
set(CMAKE_REQUIRED_FLAGS "-std=c++20")
288+
check_cxx_compiler_flag("-std=c++20" HAVE_CXX20_FLAG)
289+
endif ()
290+
291+
# Test C++20 feature availability
292+
if (HAVE_CXX20_FLAG)
293+
set(FMT_HAVE_STD_ZONED_TIME_TEST
294+
"#include <chrono>
295+
#if !defined(__cpp_lib_chrono) || __cpp_lib_chrono < 201907L
296+
#error \"zoned_time not available\"
297+
#endif
298+
int main() {
299+
using namespace std::chrono;
300+
zoned_time<std::chrono::seconds> zt;
301+
return 0;
302+
}")
303+
check_cxx_source_compiles("${FMT_HAVE_STD_ZONED_TIME_TEST}" FMT_HAS_STD_ZONED_TIME)
304+
else ()
305+
set(FMT_HAS_STD_ZONED_TIME OFF)
306+
message(STATUS "C++20 not supported, zoned_time disabled")
307+
endif ()
308+
309+
if (FMT_HAS_STD_ZONED_TIME)
310+
# Ensure we set the macro for both the library target and the header-only target.
311+
foreach (tgt IN LISTS INSTALL_TARGETS)
312+
if (TARGET ${tgt})
313+
if ("${tgt}" STREQUAL "fmt-header-only")
314+
# header-only target: INTERFACE definition is propagated to consumers
315+
target_compile_definitions(${tgt} INTERFACE FMT_HAS_STD_ZONED_TIME=1)
316+
else ()
317+
# library target: PUBLIC so it's visible to dependents as well
318+
target_compile_definitions(${tgt} PUBLIC FMT_HAS_STD_ZONED_TIME=1)
319+
endif ()
320+
endif ()
321+
endforeach ()
322+
else ()
323+
# Optional: provide a global fallback so that builds relying on a global define still see it.
324+
# add_compile_definitions(FMT_HAS_STD_ZONED_TIME=1) # usually not needed; prefer target-specific defs
325+
endif ()
326+
268327
if (MSVC)
269328
set(PEDANTIC_COMPILE_FLAGS /W3)
270329
set(WERROR_FLAG /WX)

include/fmt/chrono.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@ using utc_time = std::chrono::time_point<detail::utc_clock, Duration>;
265265
template <class Duration>
266266
using local_time = std::chrono::time_point<detail::local_t, Duration>;
267267

268+
#if defined(FMT_HAS_STD_ZONED_TIME)
269+
template <typename Duration, typename TimeZonePtr>
270+
using zoned_time = std::chrono::zoned_time<Duration, TimeZonePtr>;
271+
#endif
272+
268273
namespace detail {
269274

270275
// Prevents expansion of a preceding token as a function-style macro.
@@ -2240,6 +2245,42 @@ struct formatter<local_time<Duration>, Char>
22402245
}
22412246
};
22422247

2248+
#if defined(FMT_HAS_STD_ZONED_TIME)
2249+
template <typename Duration, typename TimeZonePtr, typename Char>
2250+
struct formatter<zoned_time<Duration, TimeZonePtr>, Char,
2251+
std::enable_if_t<std::is_pointer_v<TimeZonePtr>>>
2252+
: private formatter<std::tm, Char> {
2253+
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
2254+
return this->do_parse(ctx, true);
2255+
}
2256+
2257+
template <typename FormatContext>
2258+
auto format(const std::chrono::zoned_time<Duration, TimeZonePtr>& val,
2259+
FormatContext& ctx) const -> decltype(ctx.out()) {
2260+
auto time_info = val.get_info();
2261+
auto time_since_epoch = val.get_local_time().time_since_epoch();
2262+
auto seconds_since_epoch =
2263+
detail::duration_cast<std::chrono::seconds>(time_since_epoch);
2264+
// Use gmtime to prevent time zone conversion since local_time has an
2265+
// unspecified time zone.
2266+
std::tm t = gmtime(seconds_since_epoch.count());
2267+
// Create a custom tm with timezone info if supported
2268+
if constexpr (detail::has_tm_zone<std::tm>::value) {
2269+
t.tm_zone = time_info.abbrev.c_str();
2270+
t.tm_gmtoff = time_info.offset.count();
2271+
}
2272+
using period = typename Duration::period;
2273+
if (period::num == 1 && period::den == 1 &&
2274+
!std::is_floating_point<typename Duration::rep>::value) {
2275+
return formatter<std::tm, Char>::format(t, ctx);
2276+
}
2277+
auto subsecs =
2278+
detail::duration_cast<Duration>(time_since_epoch - seconds_since_epoch);
2279+
return formatter<std::tm, Char>::do_format(t, ctx, &subsecs);
2280+
}
2281+
};
2282+
#endif
2283+
22432284
FMT_END_EXPORT
22442285
FMT_END_NAMESPACE
22452286

0 commit comments

Comments
 (0)