From e1ce10dd98209f095c596a4c557c6cdf041f01db Mon Sep 17 00:00:00 2001 From: Adarsh-Me <122873385+Adarsh-Me@users.noreply.github.com> Date: Sat, 12 Sep 2026 15:52:35 +0000 Subject: [PATCH 1/8] GH-51267: [C++] Add std::chrono opt-in with toolchain feature detection Introduce ARROW_USE_STD_CHRONO (CMake, AUTO/ON/OFF) and use_std_chrono (Meson, auto/enabled/disabled) options that probe the toolchain for working C++20 chrono timezone support (__cpp_lib_chrono >= 201907L). AUTO preserves the current platform default (std::chrono on Windows toolchains with timezone support, vendored datetime fallback elsewhere) until the minimum-toolchain prerequisites are met. Explicit opt-in fails at configure time on unsupported toolchains instead of breaking the build. Standard-backend binaries no longer bundle the vendored datetime implementation; config.cc and the timezone-config test honor the selected backend. --- cpp/CMakeLists.txt | 4 ++ cpp/cmake_modules/CheckStdChrono.cmake | 93 ++++++++++++++++++++++++++ cpp/cmake_modules/DefineOptions.cmake | 8 +++ cpp/meson.build | 32 +++++++++ cpp/meson.options | 8 +++ cpp/src/arrow/CMakeLists.txt | 9 ++- cpp/src/arrow/config.cc | 20 +++++- cpp/src/arrow/meson.build | 65 ++++++++++-------- cpp/src/arrow/public_api_test.cc | 5 +- cpp/src/arrow/util/chrono_internal.h | 41 +++++++----- cpp/src/arrow/util/config.h.cmake | 1 + cpp/src/arrow/util/meson.build | 2 + 12 files changed, 239 insertions(+), 49 deletions(-) create mode 100644 cpp/cmake_modules/CheckStdChrono.cmake diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index b4dd0e1a7779..bc9a7eca6551 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -346,6 +346,10 @@ endif() include(SetupCxxFlags) +# GH-51267: resolve the datetime backend (C++20 std::chrono vs the vendored +# datetime fallback) with toolchain feature detection. +include(CheckStdChrono) + if(${CMAKE_CXX_FLAGS_DEBUG} MATCHES "-Og") # GH-47475: xxhash fails inlining when -Og is used. # See: https://github.com/Cyan4973/xxHash/issues/943 diff --git a/cpp/cmake_modules/CheckStdChrono.cmake b/cpp/cmake_modules/CheckStdChrono.cmake new file mode 100644 index 000000000000..5c13489cdd99 --- /dev/null +++ b/cpp/cmake_modules/CheckStdChrono.cmake @@ -0,0 +1,93 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Resolve the ARROW_USE_STD_CHRONO option (AUTO, ON or OFF) into a boolean. +# +# GH-51267 tracks removing the vendored datetime fallback once all supported +# toolchains provide working C++20 chrono timezone support. Until then: +# - AUTO keeps the historical platform default: std::chrono is used on Windows +# toolchains whose standard library provides working C++20 chrono timezone +# support, and the vendored datetime fallback is used everywhere else. +# - ON opts into std::chrono unconditionally, failing the configure step when +# the toolchain does not provide working C++20 chrono timezone support. +# - OFF always uses the vendored datetime fallback. +# +# The resolved value is consumed via arrow/util/config.h (ARROW_USE_STD_CHRONO) +# by arrow/util/chrono_internal.h and to decide whether the vendored datetime +# implementation is built. + +include(CheckCXXSourceCompiles) + +# When Arrow is consumed as a CMake subproject, ARROW_USE_STD_CHRONO is not +# defined; skip detection and let arrow/util/chrono_internal.h fall back to its +# default backend selection (vendored datetime fallback). +if(DEFINED ARROW_USE_STD_CHRONO) +if(NOT "${ARROW_USE_STD_CHRONO}" MATCHES "^(AUTO|ON|OFF)$") + message(FATAL_ERROR "ARROW_USE_STD_CHRONO must be one of AUTO, ON or OFF " + "(got \"${ARROW_USE_STD_CHRONO}\")") +endif() + +set(_ARROW_STD_CHRONO_TEST_SOURCE + " +#include +#if !defined(__cpp_lib_chrono) || __cpp_lib_chrono < 201907L +# error \"C++20 chrono timezone support (__cpp_lib_chrono >= 201907L) is unavailable\" +#endif +int main() { return 0; } +") + +function(_arrow_check_std_chrono_support out_var) + # check_cxx_source_compiles() compiles with the toolchain default standard, + # so force C++20 explicitly for this probe. + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_REQUIRED_FLAGS "/std:c++20") + else() + set(CMAKE_REQUIRED_FLAGS "-std=c++20") + endif() + check_cxx_source_compiles("${_ARROW_STD_CHRONO_TEST_SOURCE}" ${out_var}) +endfunction() + +if("${ARROW_USE_STD_CHRONO}" STREQUAL "AUTO") + if(WIN32) + _arrow_check_std_chrono_support(ARROW_HAVE_STD_CHRONO) + if(ARROW_HAVE_STD_CHRONO) + set(ARROW_USE_STD_CHRONO ON) + else() + message(STATUS "C++20 chrono timezone support unavailable," + " using vendored datetime fallback") + set(ARROW_USE_STD_CHRONO OFF) + endif() + else() + # Non-Windows toolchains keep the vendored fallback until the minimum + # toolchain prerequisites in GH-51267 are met. Toolchains with validated + # support can opt into std::chrono with -DARROW_USE_STD_CHRONO=ON. + set(ARROW_USE_STD_CHRONO OFF) + endif() +elseif(ARROW_USE_STD_CHRONO) + _arrow_check_std_chrono_support(ARROW_HAVE_STD_CHRONO) + if(NOT ARROW_HAVE_STD_CHRONO) + message(FATAL_ERROR "ARROW_USE_STD_CHRONO=ON requires working C++20 chrono " + "timezone support (__cpp_lib_chrono >= 201907L), which " + "the current toolchain does not provide") + endif() +endif() + +message(STATUS "Using C++20 std::chrono datetime backend: ${ARROW_USE_STD_CHRONO}") + +endif() + +unset(_ARROW_STD_CHRONO_TEST_SOURCE) diff --git a/cpp/cmake_modules/DefineOptions.cmake b/cpp/cmake_modules/DefineOptions.cmake index 1d12edd0613e..ac77d864053f 100644 --- a/cpp/cmake_modules/DefineOptions.cmake +++ b/cpp/cmake_modules/DefineOptions.cmake @@ -196,6 +196,14 @@ takes precedence over ccache if a storage backend is configured" ON) define_option(ARROW_WITH_MUSL "Whether the system libc is musl or not" OFF) + define_option_string(ARROW_USE_STD_CHRONO + "Use C++20 std::chrono instead of the vendored datetime library;\\ +AUTO keeps the current platform default (GH-51267)" + "AUTO" + "AUTO" + "ON" + "OFF") + define_option(ARROW_ENABLE_THREADING "Enable threading in Arrow core" ON) #---------------------------------------------------------------------- diff --git a/cpp/meson.build b/cpp/meson.build index 5532d866db33..dbbcedeedf31 100644 --- a/cpp/meson.build +++ b/cpp/meson.build @@ -114,6 +114,38 @@ needs_zlib = get_option('zlib').enabled() needs_zstd = get_option('zstd').enabled() needs_utilities = get_option('utilities').enabled() +# GH-51267: resolve the datetime backend (C++20 std::chrono vs the vendored +# datetime fallback) with toolchain feature detection. This mirrors the +# ARROW_USE_STD_CHRONO CMake option. +std_chrono_probe_src = ''' +#include +#if !defined(__cpp_lib_chrono) || __cpp_lib_chrono < 201907L +#error "C++20 chrono timezone support (__cpp_lib_chrono >= 201907L) is unavailable" +#endif +int main() { return 0; } +''' +have_std_chrono = cpp_compiler.links( + std_chrono_probe_src, + name: 'C++20 chrono timezone support', +) +std_chrono_opt = get_option('use_std_chrono') +if std_chrono_opt.enabled() + if not have_std_chrono + error( + 'use_std_chrono=enabled requires working C++20 chrono timezone ' + + 'support, which the current toolchain does not provide', + ) + endif + needs_std_chrono = true +elif std_chrono_opt.disabled() + needs_std_chrono = false +else + # auto: keep the historical platform default (std::chrono on Windows + # toolchains with timezone support, vendored fallback elsewhere) until the + # minimum-toolchain prerequisites in GH-51267 are met. + needs_std_chrono = host_machine.system() == 'windows' and have_std_chrono +endif + if needs_flight or needs_substrait protobuf_dep = dependency('protobuf') protoc = find_program('protoc') diff --git a/cpp/meson.options b/cpp/meson.options index 3124bb61fc66..872257dfd4d2 100644 --- a/cpp/meson.options +++ b/cpp/meson.options @@ -139,6 +139,14 @@ option( type: 'feature', description: 'Build the Arrow googletest unit tests', ) +option( + 'use_std_chrono', + type: 'feature', + value: 'auto', + description: ''' +Use C++20 std::chrono instead of the vendored datetime library; +auto keeps the current platform default (GH-51267)''', +) option( 'utf8proc', type: 'feature', diff --git a/cpp/src/arrow/CMakeLists.txt b/cpp/src/arrow/CMakeLists.txt index eead221dbdae..356e680b0b53 100644 --- a/cpp/src/arrow/CMakeLists.txt +++ b/cpp/src/arrow/CMakeLists.txt @@ -549,9 +549,16 @@ set(ARROW_VENDORED_SRCS vendored/uriparser/UriRecompose.c vendored/uriparser/UriResolve.c vendored/uriparser/UriShorten.c) -if(APPLE) +if(APPLE AND NOT ARROW_USE_STD_CHRONO) list(APPEND ARROW_VENDORED_SRCS vendored/datetime/ios.mm) endif() +if(ARROW_USE_STD_CHRONO) + # GH-51267: standard-backend binaries use C++20 std::chrono and do not bundle + # the vendored datetime implementation. The remaining direct users of + # arrow/vendored/datetime.h (formatting, parsing, pretty printing) only rely + # on its header-only calendar types. + list(REMOVE_ITEM ARROW_VENDORED_SRCS vendored/datetime.cpp) +endif() set_source_files_properties(vendored/datetime.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) arrow_add_object_library(ARROW_VENDORED ${ARROW_VENDORED_SRCS}) diff --git a/cpp/src/arrow/config.cc b/cpp/src/arrow/config.cc index 41cc6decc6bf..5fa44d9ddc51 100644 --- a/cpp/src/arrow/config.cc +++ b/cpp/src/arrow/config.cc @@ -22,7 +22,11 @@ #include "arrow/util/config.h" #include "arrow/util/config_internal.h" #include "arrow/util/cpu_info.h" +// GH-51267: only the vendored datetime backend bundles the vendored timezone +// implementation; std::chrono builds use the OS timezone database instead. +#if !defined(ARROW_USE_STD_CHRONO) || !ARROW_USE_STD_CHRONO #include "arrow/vendored/datetime.h" +#endif namespace arrow { @@ -64,7 +68,9 @@ std::string MakeSimdLevelString(QueryFlagFunction&& query_flag) { } } +#if !defined(ARROW_USE_STD_CHRONO) || !ARROW_USE_STD_CHRONO std::optional timezone_db_path; +#endif // ARROW_USE_STD_CHRONO }; // namespace @@ -77,11 +83,17 @@ RuntimeInfo GetRuntimeInfo() { MakeSimdLevelString([&](int64_t flags) { return cpu_info->IsSupported(flags); }); info.detected_simd_level = MakeSimdLevelString([&](int64_t flags) { return cpu_info->IsDetected(flags); }); +#if defined(ARROW_USE_STD_CHRONO) && ARROW_USE_STD_CHRONO + // GH-51267: std::chrono builds always use the OS timezone database. + info.using_os_timezone_db = true; + info.timezone_db_path = std::optional(); +#else info.using_os_timezone_db = USE_OS_TZDB; #if !USE_OS_TZDB info.timezone_db_path = timezone_db_path; #else info.timezone_db_path = std::optional(); +#endif #endif return info; } @@ -91,7 +103,11 @@ RuntimeInfo GetRuntimeInfo() { Status Initialize(const GlobalOptions& options) noexcept { ARROW_SUPPRESS_DEPRECATION_WARNING if (options.timezone_db_path.has_value()) { -#if !USE_OS_TZDB +#if defined(ARROW_USE_STD_CHRONO) && ARROW_USE_STD_CHRONO + return Status::Invalid( + "Arrow was built with C++20 std::chrono and uses the OS timezone database, " + "so a downloaded database cannot be provided at runtime."); +#elif !USE_OS_TZDB try { arrow_vendored::date::set_install(options.timezone_db_path.value()); arrow_vendored::date::reload_tzdb(); @@ -103,7 +119,7 @@ Status Initialize(const GlobalOptions& options) noexcept { return Status::Invalid( "Arrow was set to use OS timezone database at compile time, " "so a downloaded database cannot be provided at runtime."); -#endif // !USE_OS_TZDB +#endif // ARROW_USE_STD_CHRONO / USE_OS_TZDB } ARROW_UNSUPPRESS_DEPRECATION_WARNING return Status::OK(); diff --git a/cpp/src/arrow/meson.build b/cpp/src/arrow/meson.build index fea26ef4e452..257abc1cc8bf 100644 --- a/cpp/src/arrow/meson.build +++ b/cpp/src/arrow/meson.build @@ -34,6 +34,42 @@ else simdjson_dep = disabler() endif +# GH-51267: standard-backend builds (use_std_chrono) use C++20 std::chrono and +# do not bundle the vendored datetime implementation. All other builds keep it +# as a fallback. +arrow_vendored_sources = [ + 'vendored/base64.cpp', +] +if not needs_std_chrono + arrow_vendored_sources += 'vendored/datetime.cpp' +endif +arrow_vendored_sources += [ + 'vendored/double-conversion/bignum-dtoa.cc', + 'vendored/double-conversion/bignum.cc', + 'vendored/double-conversion/cached-powers.cc', + 'vendored/double-conversion/double-to-string.cc', + 'vendored/double-conversion/fast-dtoa.cc', + 'vendored/double-conversion/fixed-dtoa.cc', + 'vendored/double-conversion/string-to-double.cc', + 'vendored/double-conversion/strtod.cc', + 'vendored/musl/strptime.c', + 'vendored/uriparser/UriCommon.c', + 'vendored/uriparser/UriCompare.c', + 'vendored/uriparser/UriEscape.c', + 'vendored/uriparser/UriFile.c', + 'vendored/uriparser/UriIp4.c', + 'vendored/uriparser/UriIp4Base.c', + 'vendored/uriparser/UriMemory.c', + 'vendored/uriparser/UriNormalize.c', + 'vendored/uriparser/UriNormalizeBase.c', + 'vendored/uriparser/UriParse.c', + 'vendored/uriparser/UriParseBase.c', + 'vendored/uriparser/UriQuery.c', + 'vendored/uriparser/UriRecompose.c', + 'vendored/uriparser/UriResolve.c', + 'vendored/uriparser/UriShorten.c', +] + arrow_components = { 'arrow_array': { 'sources': [ @@ -114,34 +150,7 @@ arrow_components = { }, 'memory_pool': {'sources': ['memory_pool.cc']}, 'vendored': { - 'sources': [ - 'vendored/base64.cpp', - 'vendored/datetime.cpp', - 'vendored/double-conversion/bignum-dtoa.cc', - 'vendored/double-conversion/bignum.cc', - 'vendored/double-conversion/cached-powers.cc', - 'vendored/double-conversion/double-to-string.cc', - 'vendored/double-conversion/fast-dtoa.cc', - 'vendored/double-conversion/fixed-dtoa.cc', - 'vendored/double-conversion/string-to-double.cc', - 'vendored/double-conversion/strtod.cc', - 'vendored/musl/strptime.c', - 'vendored/uriparser/UriCommon.c', - 'vendored/uriparser/UriCompare.c', - 'vendored/uriparser/UriEscape.c', - 'vendored/uriparser/UriFile.c', - 'vendored/uriparser/UriIp4.c', - 'vendored/uriparser/UriIp4Base.c', - 'vendored/uriparser/UriMemory.c', - 'vendored/uriparser/UriNormalize.c', - 'vendored/uriparser/UriNormalizeBase.c', - 'vendored/uriparser/UriParse.c', - 'vendored/uriparser/UriParseBase.c', - 'vendored/uriparser/UriQuery.c', - 'vendored/uriparser/UriRecompose.c', - 'vendored/uriparser/UriResolve.c', - 'vendored/uriparser/UriShorten.c', - ], + 'sources': arrow_vendored_sources, }, 'arrow_base': { 'sources': [ diff --git a/cpp/src/arrow/public_api_test.cc b/cpp/src/arrow/public_api_test.cc index 12c703f120f6..280dc9abf545 100644 --- a/cpp/src/arrow/public_api_test.cc +++ b/cpp/src/arrow/public_api_test.cc @@ -19,6 +19,7 @@ #include #include "arrow/config.h" +#include "arrow/util/config.h" // Include various "api.h" entrypoints and check they don't leak internal symbols @@ -125,7 +126,9 @@ TEST(Misc, BuildInfo) { // TODO(GH-48593): Remove when libc++ supports std::chrono timezones. ARROW_SUPPRESS_DEPRECATION_WARNING TEST(Misc, SetTimezoneConfig) { -#ifndef _WIN32 +#if defined(ARROW_USE_STD_CHRONO) && ARROW_USE_STD_CHRONO + GTEST_SKIP() << "std::chrono builds use the OS timezone database (GH-51267)"; +#elif !defined(_WIN32) GTEST_SKIP() << "Can only set the Timezone database on Windows"; #elif !defined(ARROW_FILESYSTEM) GTEST_SKIP() << "Need filesystem support to test timezone config."; diff --git a/cpp/src/arrow/util/chrono_internal.h b/cpp/src/arrow/util/chrono_internal.h index ea4051bccf59..301b3747cf42 100644 --- a/cpp/src/arrow/util/chrono_internal.h +++ b/cpp/src/arrow/util/chrono_internal.h @@ -32,7 +32,18 @@ #include #include -// Feature detection for C++20 chrono timezone support +#include "arrow/util/config.h" + +// Backend selection (GH-51267). +// +// The CMake (ARROW_USE_STD_CHRONO) and Meson (use_std_chrono) options probe the +// toolchain for working C++20 chrono timezone support and record the result in +// arrow/util/config.h. Builds that do not set ARROW_USE_STD_CHRONO keep the +// historical default below: std::chrono on Windows toolchains advertising +// __cpp_lib_chrono >= 201907L, and the vendored Howard Hinnant date library +// elsewhere. +// +// Feature detection for C++20 chrono timezone support: // https://en.cppreference.com/w/cpp/compiler_support/20.html#cpp_lib_chrono_201907L // // On Windows with MSVC: std::chrono uses Windows' internal timezone database, @@ -41,24 +52,20 @@ // On Windows with MinGW/GCC: libstdc++ reads tzdata files via TZDIR env var. // Set TZDIR=/usr/share/zoneinfo to use the system tzdata. // -// On non-Windows: GCC libstdc++ has a bug where DST state is incorrectly reset when -// a timezone transitions between rule sets (e.g., Australia/Broken_Hill around -// 2000-02-29). Until this is fixed, we use the vendored date.h library. +// On non-Windows: GCC libstdc++ had a bug where DST state is incorrectly reset +// when a timezone transitions between rule sets (e.g., Australia/Broken_Hill +// around 2000-02-29); those toolchains keep using the vendored date.h library +// until the minimum-toolchain prerequisites in GH-51267 are met. // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116110 - -// Use std::chrono on Windows when C++20 chrono timezone support is available. -// The __cpp_lib_chrono >= 201907L feature test macro indicates full support: -// - MSVC: Uses Windows' internal timezone database (no IANA tzdata needed) -// - GCC/libstdc++: Requires TZDIR environment variable to locate tzdata -// - Clang/libc++: Does not define 201907L (no timezone support), so falls back // -// On non-Windows, we use the vendored date library due to a GCC libstdc++ bug -// where DST state is incorrectly reset during timezone rule transitions. -// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116110 -#if defined(_WIN32) && defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L -# define ARROW_USE_STD_CHRONO 1 -#else -# define ARROW_USE_STD_CHRONO 0 +// On Windows with Clang/libc++: __cpp_lib_chrono < 201907L (no timezone +// support), so the vendored library is used. +#ifndef ARROW_USE_STD_CHRONO +# if defined(_WIN32) && defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L +# define ARROW_USE_STD_CHRONO 1 +# else +# define ARROW_USE_STD_CHRONO 0 +# endif #endif #if ARROW_USE_STD_CHRONO diff --git a/cpp/src/arrow/util/config.h.cmake b/cpp/src/arrow/util/config.h.cmake index cf98757c4a8d..b2ee21cf0b0e 100644 --- a/cpp/src/arrow/util/config.h.cmake +++ b/cpp/src/arrow/util/config.h.cmake @@ -54,6 +54,7 @@ #cmakedefine ARROW_HDFS #cmakedefine ARROW_S3 #cmakedefine ARROW_USE_GLOG +#cmakedefine01 ARROW_USE_STD_CHRONO #cmakedefine ARROW_USE_NATIVE_INT128 #cmakedefine ARROW_WITH_BROTLI #cmakedefine ARROW_WITH_BZ2 diff --git a/cpp/src/arrow/util/meson.build b/cpp/src/arrow/util/meson.build index 729cfba47222..97d4bc6c53d9 100644 --- a/cpp/src/arrow/util/meson.build +++ b/cpp/src/arrow/util/meson.build @@ -59,6 +59,8 @@ conf_data.set('ARROW_HDFS', needs_hdfs) conf_data.set('ARROW_S3', needs_s3) conf_data.set('ARROW_USE_GLOG', false) +conf_data.set('ARROW_USE_STD_CHRONO', needs_std_chrono) + has_int128 = cpp_compiler.has_define('__SIZEOF_INT128__') conf_data.set('ARROW_USE_NATIVE_INT128', has_int128) From 0330202957c3b38cd99960d365317f2bd8aff9ed Mon Sep 17 00:00:00 2001 From: Adarsh Date: Sat, 12 Sep 2026 23:48:59 +0530 Subject: [PATCH 2/8] GH-51267: [C++] address review findings on the std::chrono opt-in Three review findings on the opt-in: - Keep vendored/datetime.cpp in the build when Gandiva is enabled: its cast_time.cc and gdv_function_stubs.cc call the timezone functions whose definitions live in that translation unit, and Gandiva links arrow_shared/arrow_static, so removing it breaks the link. - Emit ARROW_USE_STD_CHRONO through a definition that stays undefined when the option is unset (Arrow as a subproject with ARROW_DEFINE_OPTIONS=OFF): #cmakedefine01 always generated 0, which disabled chrono_internal.h's platform fallback and flipped those builds from the std::chrono default to the vendored backend. An explicit OFF still emits 0 so the fallback cannot override it. Meson resolves its option unconditionally and keeps emitting the decision. - Make the R package backend-aware: runtime_info() now reports whether Arrow reads the OS timezone database, and the startup tzdb configuration skips the vendored database path (whose use fails) when the OS database is in use, instead of printing a false 'timezones will not be available' warning on builds where they work. --- cpp/src/arrow/CMakeLists.txt | 22 ++++++++++++++++++++-- cpp/src/arrow/util/config.h.cmake | 2 +- cpp/src/arrow/util/meson.build | 8 +++++++- r/R/arrow-info.R | 3 ++- r/R/arrow-package.R | 7 +++++++ r/src/config.cpp | 6 +++++- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/cpp/src/arrow/CMakeLists.txt b/cpp/src/arrow/CMakeLists.txt index 356e680b0b53..2317ae938d72 100644 --- a/cpp/src/arrow/CMakeLists.txt +++ b/cpp/src/arrow/CMakeLists.txt @@ -406,6 +406,21 @@ string(REPLACE "${CMAKE_BINARY_DIR}" "" REDACTED_CXX_FLAGS cmake_path(GET PROJECT_SOURCE_DIR PARENT_PATH ARROW_PROJECT_SOURCE_DIR) string(REPLACE "${ARROW_PROJECT_SOURCE_DIR}" "" REDACTED_CXX_FLAGS ${REDACTED_CXX_FLAGS}) +# GH-51267: emit the resolved datetime backend, but keep the macro undefined +# when the option is unset (Arrow as a subproject with ARROW_DEFINE_OPTIONS=OFF) +# so chrono_internal.h's platform fallback still decides there — #cmakedefine01 +# would emit 0 and flip those builds from the std::chrono default to the +# vendored fallback. An explicit OFF must stay 0 so the fallback cannot +# re-enable std::chrono against the user's choice. +if(DEFINED ARROW_USE_STD_CHRONO) + if(ARROW_USE_STD_CHRONO) + set(ARROW_USE_STD_CHRONO_DEFINITION "#define ARROW_USE_STD_CHRONO 1") + else() + set(ARROW_USE_STD_CHRONO_DEFINITION "#define ARROW_USE_STD_CHRONO 0") + endif() +else() + set(ARROW_USE_STD_CHRONO_DEFINITION "/* #undef ARROW_USE_STD_CHRONO */") +endif() configure_file("util/config.h.cmake" "util/config.h" ESCAPE_QUOTES) configure_file("util/config_internal.h.cmake" "util/config_internal.h" ESCAPE_QUOTES) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/util/config.h" @@ -552,11 +567,14 @@ set(ARROW_VENDORED_SRCS if(APPLE AND NOT ARROW_USE_STD_CHRONO) list(APPEND ARROW_VENDORED_SRCS vendored/datetime/ios.mm) endif() -if(ARROW_USE_STD_CHRONO) +if(ARROW_USE_STD_CHRONO AND NOT ARROW_GANDIVA) # GH-51267: standard-backend binaries use C++20 std::chrono and do not bundle # the vendored datetime implementation. The remaining direct users of # arrow/vendored/datetime.h (formatting, parsing, pretty printing) only rely - # on its header-only calendar types. + # on its header-only calendar types — except Gandiva, whose cast_time.cc and + # gdv_function_stubs.cc call the timezone functions defined in + # vendored/datetime.cpp and link arrow_shared/arrow_static, so the TU is + # kept whenever Gandiva is built. list(REMOVE_ITEM ARROW_VENDORED_SRCS vendored/datetime.cpp) endif() set_source_files_properties(vendored/datetime.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION diff --git a/cpp/src/arrow/util/config.h.cmake b/cpp/src/arrow/util/config.h.cmake index b2ee21cf0b0e..ccce34a9a870 100644 --- a/cpp/src/arrow/util/config.h.cmake +++ b/cpp/src/arrow/util/config.h.cmake @@ -54,7 +54,7 @@ #cmakedefine ARROW_HDFS #cmakedefine ARROW_S3 #cmakedefine ARROW_USE_GLOG -#cmakedefine01 ARROW_USE_STD_CHRONO +@ARROW_USE_STD_CHRONO_DEFINITION@ #cmakedefine ARROW_USE_NATIVE_INT128 #cmakedefine ARROW_WITH_BROTLI #cmakedefine ARROW_WITH_BZ2 diff --git a/cpp/src/arrow/util/meson.build b/cpp/src/arrow/util/meson.build index 97d4bc6c53d9..c53564330a35 100644 --- a/cpp/src/arrow/util/meson.build +++ b/cpp/src/arrow/util/meson.build @@ -59,7 +59,13 @@ conf_data.set('ARROW_HDFS', needs_hdfs) conf_data.set('ARROW_S3', needs_s3) conf_data.set('ARROW_USE_GLOG', false) -conf_data.set('ARROW_USE_STD_CHRONO', needs_std_chrono) +# Meson options always resolve (there is no unset state), so the resolved +# decision is emitted directly and chrono_internal.h's fallback is bypassed. +if needs_std_chrono + conf_data.set('ARROW_USE_STD_CHRONO_DEFINITION', '#define ARROW_USE_STD_CHRONO 1') +else + conf_data.set('ARROW_USE_STD_CHRONO_DEFINITION', '#define ARROW_USE_STD_CHRONO 0') +endif has_int128 = cpp_compiler.has_define('__SIZEOF_INT128__') conf_data.set('ARROW_USE_NATIVE_INT128', has_int128) diff --git a/r/R/arrow-info.R b/r/R/arrow-info.R index 64d292712dd0..78b06cb86c4c 100644 --- a/r/R/arrow-info.R +++ b/r/R/arrow-info.R @@ -59,7 +59,8 @@ arrow_info <- function() { ), runtime_info = list( simd_level = runtimeinfo[1], - detected_simd_level = runtimeinfo[2] + detected_simd_level = runtimeinfo[2], + using_os_timezone_db = runtimeinfo[3] == "true" ), build_info = list( cpp_version = buildinfo[1], diff --git a/r/R/arrow-package.R b/r/R/arrow-package.R index 2706faee5cb1..df45f907cc5d 100644 --- a/r/R/arrow-package.R +++ b/r/R/arrow-package.R @@ -180,6 +180,13 @@ s3_finalizer <- new.env(parent = emptyenv()) configure_tzdb <- function() { if (requireNamespace("tzdb", quietly = TRUE)) { + if (runtime_info()[[3]] == "true") { + # GH-51267: builds reading the OS timezone database (C++20 std::chrono, + # or the vendored library built against the OS tzdata) cannot use a + # downloaded database, and their timezones already work — skip the + # vendored path instead of surfacing a false startup failure. + return(invisible()) + } tryCatch( { tzdb::tzdb_initialize() diff --git a/r/src/config.cpp b/r/src/config.cpp index 950e29a168a1..0642af9ffdd0 100644 --- a/r/src/config.cpp +++ b/r/src/config.cpp @@ -31,7 +31,11 @@ std::vector build_info() { // [[arrow::export]] std::vector runtime_info() { auto info = arrow::GetRuntimeInfo(); - return {info.simd_level, info.detected_simd_level}; + // The third element reports whether Arrow reads the OS timezone database + // (C++20 std::chrono backend, or the vendored library built against the + // OS tzdata); R's startup skips the tzdb package path in that case. + return {info.simd_level, info.detected_simd_level, + info.using_os_timezone_db ? "true" : "false"}; } // [[arrow::export]] From 7c3b8dbd26a9e827e7035717cb6837df4fb6a5ae Mon Sep 17 00:00:00 2001 From: Adarsh Date: Sun, 13 Sep 2026 00:59:14 +0530 Subject: [PATCH 3/8] GH-51267: [C++] fix option description line continuation in DefineOptions The ARROW_USE_STD_CHRONO description used an escaped backslash (\) before the newline instead of a line-continuation backslash, so the newline stayed inside the quoted description and leaked into the generated ArrowOptions.cmake as a bare statement line. Every consumer that includes the installed file via find_package(Arrow) (pyarrow wheels, the R package build) then failed to configure with 'Parse error. Expected "(", got identifier with text "keeps"'. Use a single continuation backslash, matching ARROW_PACKAGE_KIND. --- cpp/cmake_modules/DefineOptions.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/cmake_modules/DefineOptions.cmake b/cpp/cmake_modules/DefineOptions.cmake index ac77d864053f..d85b0c799a25 100644 --- a/cpp/cmake_modules/DefineOptions.cmake +++ b/cpp/cmake_modules/DefineOptions.cmake @@ -197,7 +197,7 @@ takes precedence over ccache if a storage backend is configured" ON) define_option(ARROW_WITH_MUSL "Whether the system libc is musl or not" OFF) define_option_string(ARROW_USE_STD_CHRONO - "Use C++20 std::chrono instead of the vendored datetime library;\\ + "Use C++20 std::chrono instead of the vendored datetime library;\ AUTO keeps the current platform default (GH-51267)" "AUTO" "AUTO" From b8a4be479b0d6ca18df90438618901587aabaa88 Mon Sep 17 00:00:00 2001 From: Adarsh Date: Sun, 13 Sep 2026 01:11:15 +0530 Subject: [PATCH 4/8] GH-51267: [C++] apply clang-format, cmake-format and meson-fmt Formatting only, as requested by the pre-commit hooks: indented preprocessor directives inside #if blocks in config.cc, the body of if(DEFINED ARROW_USE_STD_CHRONO) in CheckStdChrono.cmake, and the meson-fmt style for the touched meson.build files. --- cpp/cmake_modules/CheckStdChrono.cmake | 74 +++++++++++++------------- cpp/meson.build | 6 +-- cpp/src/arrow/config.cc | 8 +-- cpp/src/arrow/meson.build | 8 +-- cpp/src/arrow/util/meson.build | 10 +++- 5 files changed, 53 insertions(+), 53 deletions(-) diff --git a/cpp/cmake_modules/CheckStdChrono.cmake b/cpp/cmake_modules/CheckStdChrono.cmake index 5c13489cdd99..f12a88d69fe2 100644 --- a/cpp/cmake_modules/CheckStdChrono.cmake +++ b/cpp/cmake_modules/CheckStdChrono.cmake @@ -36,13 +36,13 @@ include(CheckCXXSourceCompiles) # defined; skip detection and let arrow/util/chrono_internal.h fall back to its # default backend selection (vendored datetime fallback). if(DEFINED ARROW_USE_STD_CHRONO) -if(NOT "${ARROW_USE_STD_CHRONO}" MATCHES "^(AUTO|ON|OFF)$") - message(FATAL_ERROR "ARROW_USE_STD_CHRONO must be one of AUTO, ON or OFF " - "(got \"${ARROW_USE_STD_CHRONO}\")") -endif() + if(NOT "${ARROW_USE_STD_CHRONO}" MATCHES "^(AUTO|ON|OFF)$") + message(FATAL_ERROR "ARROW_USE_STD_CHRONO must be one of AUTO, ON or OFF " + "(got \"${ARROW_USE_STD_CHRONO}\")") + endif() -set(_ARROW_STD_CHRONO_TEST_SOURCE - " + set(_ARROW_STD_CHRONO_TEST_SOURCE + " #include #if !defined(__cpp_lib_chrono) || __cpp_lib_chrono < 201907L # error \"C++20 chrono timezone support (__cpp_lib_chrono >= 201907L) is unavailable\" @@ -50,43 +50,43 @@ set(_ARROW_STD_CHRONO_TEST_SOURCE int main() { return 0; } ") -function(_arrow_check_std_chrono_support out_var) - # check_cxx_source_compiles() compiles with the toolchain default standard, - # so force C++20 explicitly for this probe. - if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(CMAKE_REQUIRED_FLAGS "/std:c++20") - else() - set(CMAKE_REQUIRED_FLAGS "-std=c++20") - endif() - check_cxx_source_compiles("${_ARROW_STD_CHRONO_TEST_SOURCE}" ${out_var}) -endfunction() + function(_arrow_check_std_chrono_support out_var) + # check_cxx_source_compiles() compiles with the toolchain default standard, + # so force C++20 explicitly for this probe. + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_REQUIRED_FLAGS "/std:c++20") + else() + set(CMAKE_REQUIRED_FLAGS "-std=c++20") + endif() + check_cxx_source_compiles("${_ARROW_STD_CHRONO_TEST_SOURCE}" ${out_var}) + endfunction() -if("${ARROW_USE_STD_CHRONO}" STREQUAL "AUTO") - if(WIN32) - _arrow_check_std_chrono_support(ARROW_HAVE_STD_CHRONO) - if(ARROW_HAVE_STD_CHRONO) - set(ARROW_USE_STD_CHRONO ON) + if("${ARROW_USE_STD_CHRONO}" STREQUAL "AUTO") + if(WIN32) + _arrow_check_std_chrono_support(ARROW_HAVE_STD_CHRONO) + if(ARROW_HAVE_STD_CHRONO) + set(ARROW_USE_STD_CHRONO ON) + else() + message(STATUS "C++20 chrono timezone support unavailable," + " using vendored datetime fallback") + set(ARROW_USE_STD_CHRONO OFF) + endif() else() - message(STATUS "C++20 chrono timezone support unavailable," - " using vendored datetime fallback") + # Non-Windows toolchains keep the vendored fallback until the minimum + # toolchain prerequisites in GH-51267 are met. Toolchains with validated + # support can opt into std::chrono with -DARROW_USE_STD_CHRONO=ON. set(ARROW_USE_STD_CHRONO OFF) endif() - else() - # Non-Windows toolchains keep the vendored fallback until the minimum - # toolchain prerequisites in GH-51267 are met. Toolchains with validated - # support can opt into std::chrono with -DARROW_USE_STD_CHRONO=ON. - set(ARROW_USE_STD_CHRONO OFF) - endif() -elseif(ARROW_USE_STD_CHRONO) - _arrow_check_std_chrono_support(ARROW_HAVE_STD_CHRONO) - if(NOT ARROW_HAVE_STD_CHRONO) - message(FATAL_ERROR "ARROW_USE_STD_CHRONO=ON requires working C++20 chrono " - "timezone support (__cpp_lib_chrono >= 201907L), which " - "the current toolchain does not provide") + elseif(ARROW_USE_STD_CHRONO) + _arrow_check_std_chrono_support(ARROW_HAVE_STD_CHRONO) + if(NOT ARROW_HAVE_STD_CHRONO) + message(FATAL_ERROR "ARROW_USE_STD_CHRONO=ON requires working C++20 chrono " + "timezone support (__cpp_lib_chrono >= 201907L), which " + "the current toolchain does not provide") + endif() endif() -endif() -message(STATUS "Using C++20 std::chrono datetime backend: ${ARROW_USE_STD_CHRONO}") + message(STATUS "Using C++20 std::chrono datetime backend: ${ARROW_USE_STD_CHRONO}") endif() diff --git a/cpp/meson.build b/cpp/meson.build index dbbcedeedf31..e611dbc06b65 100644 --- a/cpp/meson.build +++ b/cpp/meson.build @@ -131,10 +131,8 @@ have_std_chrono = cpp_compiler.links( std_chrono_opt = get_option('use_std_chrono') if std_chrono_opt.enabled() if not have_std_chrono - error( - 'use_std_chrono=enabled requires working C++20 chrono timezone ' - + 'support, which the current toolchain does not provide', - ) + error('use_std_chrono=enabled requires working C++20 chrono timezone ' + + 'support, which the current toolchain does not provide') endif needs_std_chrono = true elif std_chrono_opt.disabled() diff --git a/cpp/src/arrow/config.cc b/cpp/src/arrow/config.cc index 5fa44d9ddc51..7fd0f4244fbf 100644 --- a/cpp/src/arrow/config.cc +++ b/cpp/src/arrow/config.cc @@ -25,7 +25,7 @@ // GH-51267: only the vendored datetime backend bundles the vendored timezone // implementation; std::chrono builds use the OS timezone database instead. #if !defined(ARROW_USE_STD_CHRONO) || !ARROW_USE_STD_CHRONO -#include "arrow/vendored/datetime.h" +# include "arrow/vendored/datetime.h" #endif namespace arrow { @@ -89,11 +89,11 @@ RuntimeInfo GetRuntimeInfo() { info.timezone_db_path = std::optional(); #else info.using_os_timezone_db = USE_OS_TZDB; -#if !USE_OS_TZDB +# if !USE_OS_TZDB info.timezone_db_path = timezone_db_path; -#else +# else info.timezone_db_path = std::optional(); -#endif +# endif #endif return info; } diff --git a/cpp/src/arrow/meson.build b/cpp/src/arrow/meson.build index 257abc1cc8bf..1489c450d156 100644 --- a/cpp/src/arrow/meson.build +++ b/cpp/src/arrow/meson.build @@ -37,9 +37,7 @@ endif # GH-51267: standard-backend builds (use_std_chrono) use C++20 std::chrono and # do not bundle the vendored datetime implementation. All other builds keep it # as a fallback. -arrow_vendored_sources = [ - 'vendored/base64.cpp', -] +arrow_vendored_sources = ['vendored/base64.cpp'] if not needs_std_chrono arrow_vendored_sources += 'vendored/datetime.cpp' endif @@ -149,9 +147,7 @@ arrow_components = { 'dependencies': [dl_dep], }, 'memory_pool': {'sources': ['memory_pool.cc']}, - 'vendored': { - 'sources': arrow_vendored_sources, - }, + 'vendored': {'sources': arrow_vendored_sources}, 'arrow_base': { 'sources': [ 'builder.cc', diff --git a/cpp/src/arrow/util/meson.build b/cpp/src/arrow/util/meson.build index c53564330a35..92cb39ed37cf 100644 --- a/cpp/src/arrow/util/meson.build +++ b/cpp/src/arrow/util/meson.build @@ -62,9 +62,15 @@ conf_data.set('ARROW_USE_GLOG', false) # Meson options always resolve (there is no unset state), so the resolved # decision is emitted directly and chrono_internal.h's fallback is bypassed. if needs_std_chrono - conf_data.set('ARROW_USE_STD_CHRONO_DEFINITION', '#define ARROW_USE_STD_CHRONO 1') + conf_data.set( + 'ARROW_USE_STD_CHRONO_DEFINITION', + '#define ARROW_USE_STD_CHRONO 1', + ) else - conf_data.set('ARROW_USE_STD_CHRONO_DEFINITION', '#define ARROW_USE_STD_CHRONO 0') + conf_data.set( + 'ARROW_USE_STD_CHRONO_DEFINITION', + '#define ARROW_USE_STD_CHRONO 0', + ) endif has_int128 = cpp_compiler.has_define('__SIZEOF_INT128__') From 6b40569255a812206f9dc94a7e2a336f9c5c7a4b Mon Sep 17 00:00:00 2001 From: Adarsh Date: Sun, 13 Sep 2026 12:15:44 +0530 Subject: [PATCH 5/8] GH-51267: [C++] stop the chrono probe from carrying a second /std switch The probe set /std:c++20 through CMAKE_REQUIRED_FLAGS on top of the project-wide CMAKE_CXX_STANDARD=20 that try_compile already applies. Under CMake 4.1 + MSVC 19.44 the probe failed spuriously, so AUTO resolved to the vendored backend on Windows and the substrait tests then failed at runtime with 'Timezone database not found at ...\Downloads\tzdata' (the vendored tzdb is not available there). Compile the probe with the project standard alone and surface the compiler output on failure, so a probe regression is diagnosable from CI instead of hiding in CMakeError.log. --- cpp/cmake_modules/CheckStdChrono.cmake | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/cpp/cmake_modules/CheckStdChrono.cmake b/cpp/cmake_modules/CheckStdChrono.cmake index f12a88d69fe2..66387a48b39e 100644 --- a/cpp/cmake_modules/CheckStdChrono.cmake +++ b/cpp/cmake_modules/CheckStdChrono.cmake @@ -30,8 +30,6 @@ # by arrow/util/chrono_internal.h and to decide whether the vendored datetime # implementation is built. -include(CheckCXXSourceCompiles) - # When Arrow is consumed as a CMake subproject, ARROW_USE_STD_CHRONO is not # defined; skip detection and let arrow/util/chrono_internal.h fall back to its # default backend selection (vendored datetime fallback). @@ -51,14 +49,20 @@ int main() { return 0; } ") function(_arrow_check_std_chrono_support out_var) - # check_cxx_source_compiles() compiles with the toolchain default standard, - # so force C++20 explicitly for this probe. - if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(CMAKE_REQUIRED_FLAGS "/std:c++20") - else() - set(CMAKE_REQUIRED_FLAGS "-std=c++20") + # Arrow pins the project-wide standard to C++20 (SetupCxxFlags), so + # try_compile already compiles the probe with /std:c++20. Passing the + # switch a second time through CMAKE_REQUIRED_FLAGS made the probe fail + # spuriously under CMake 4 + MSVC, which silently downgraded Windows + # AUTO builds to the vendored backend whose tzdb lookups then fail at + # runtime. The compiler output is surfaced on failure so a probe + # regression is diagnosable from CI directly. + try_compile(${out_var} + SOURCE_FROM_VAR "arrow_std_chrono_probe.cxx" + _ARROW_STD_CHRONO_TEST_SOURCE + OUTPUT_VARIABLE _chrono_probe_output) + if(NOT ${out_var}) + message(STATUS "C++20 chrono probe failed with:\n${_chrono_probe_output}") endif() - check_cxx_source_compiles("${_ARROW_STD_CHRONO_TEST_SOURCE}" ${out_var}) endfunction() if("${ARROW_USE_STD_CHRONO}" STREQUAL "AUTO") From 3f415971d427f5f34b4cf0f6476b866d6adc20e6 Mon Sep 17 00:00:00 2001 From: Oracle Public Cloud User Date: Wed, 16 Sep 2026 12:41:26 +0000 Subject: [PATCH 6/8] GH-51267: [C++] address remaining review findings on the std::chrono opt-in Probe gap (CMake + Meson): chrono_internal.h needs (std::vformat, std::make_format_args) and std::chrono::locate_zone, but the probe only checked __cpp_lib_chrono, so toolchains advertising the macro without (e.g. GCC 12) passed detection and broke the build. The probe now compiles those APIs too (compile+link only, never runs). R startup: configure_tzdb() warned 'Timezones will not be available' when the tzdb package was missing even on builds reading the OS timezone database, where timezones already work. Check using_os_timezone_db first and skip configuration entirely. Gandiva mix: std::chrono + Gandiva builds keep vendored/datetime.cpp (Gandiva calls its timezone functions directly), but Initialize() rejected timezone_db_path whenever the std backend was on, leaving Gandiva with no way to load its tzdb. Gate on the new ARROW_HAVE_VENDORED_DATETIME (emitted by CMake and Meson) so those builds can still point the vendored copy at a database, while pure-std builds keep rejecting the path. Subproject: with ARROW_DEFINE_OPTIONS=OFF the macro stays undefined and chrono_internal.h falls back to Windows-std, but config.cc used its own defined-check and reported using_os_timezone_db=false. It now includes chrono_internal.h so both use the shared predicate; the SetTimezoneConfig skip in public_api_test.cc follows it as well. --- cpp/cmake_modules/CheckStdChrono.cmake | 29 ++++++++++++++++---- cpp/meson.build | 21 +++++++++++++-- cpp/src/arrow/CMakeLists.txt | 10 +++++++ cpp/src/arrow/config.cc | 37 +++++++++++++++++--------- cpp/src/arrow/public_api_test.cc | 8 +++++- cpp/src/arrow/util/config.h.cmake | 4 +++ cpp/src/arrow/util/meson.build | 5 ++++ r/R/arrow-package.R | 14 +++++----- 8 files changed, 100 insertions(+), 28 deletions(-) diff --git a/cpp/cmake_modules/CheckStdChrono.cmake b/cpp/cmake_modules/CheckStdChrono.cmake index 66387a48b39e..73097c940538 100644 --- a/cpp/cmake_modules/CheckStdChrono.cmake +++ b/cpp/cmake_modules/CheckStdChrono.cmake @@ -42,10 +42,29 @@ if(DEFINED ARROW_USE_STD_CHRONO) set(_ARROW_STD_CHRONO_TEST_SOURCE " #include +#include +#include +#include +#include +#include #if !defined(__cpp_lib_chrono) || __cpp_lib_chrono < 201907L # error \"C++20 chrono timezone support (__cpp_lib_chrono >= 201907L) is unavailable\" #endif -int main() { return 0; } +#if !defined(__cpp_lib_format) +# error \"C++20 formatting support (__cpp_lib_format) is unavailable\" +#endif +int main() { + // arrow/util/chrono_internal.h (GH-51267) needs working timezone lookup + // and ; the toolchain must provide both, not just the chrono + // feature-test macro (e.g. GCC 12 advertises __cpp_lib_chrono but has + // no ). The probe only compiles and links, it never runs, so + // referencing locate_zone here needs no timezone database on the host. + const std::chrono::time_zone* tz = std::chrono::locate_zone(\"UTC\"); + std::ostringstream os; + std::vformat_to(std::ostreambuf_iterator(os), \"{:%Y}\", + std::make_format_args(std::chrono::system_clock::now())); + return tz == nullptr; +} ") function(_arrow_check_std_chrono_support out_var) @@ -56,9 +75,8 @@ int main() { return 0; } # AUTO builds to the vendored backend whose tzdb lookups then fail at # runtime. The compiler output is surfaced on failure so a probe # regression is diagnosable from CI directly. - try_compile(${out_var} - SOURCE_FROM_VAR "arrow_std_chrono_probe.cxx" - _ARROW_STD_CHRONO_TEST_SOURCE + try_compile(${out_var} SOURCE_FROM_VAR + "arrow_std_chrono_probe.cxx" _ARROW_STD_CHRONO_TEST_SOURCE OUTPUT_VARIABLE _chrono_probe_output) if(NOT ${out_var}) message(STATUS "C++20 chrono probe failed with:\n${_chrono_probe_output}") @@ -85,7 +103,8 @@ int main() { return 0; } _arrow_check_std_chrono_support(ARROW_HAVE_STD_CHRONO) if(NOT ARROW_HAVE_STD_CHRONO) message(FATAL_ERROR "ARROW_USE_STD_CHRONO=ON requires working C++20 chrono " - "timezone support (__cpp_lib_chrono >= 201907L), which " + "timezone and formatting support (__cpp_lib_chrono >= 201907L " + "and __cpp_lib_format), which " "the current toolchain does not provide") endif() endif() diff --git a/cpp/meson.build b/cpp/meson.build index e611dbc06b65..e02534127927 100644 --- a/cpp/meson.build +++ b/cpp/meson.build @@ -119,10 +119,27 @@ needs_utilities = get_option('utilities').enabled() # ARROW_USE_STD_CHRONO CMake option. std_chrono_probe_src = ''' #include +#include +#include +#include +#include +#include #if !defined(__cpp_lib_chrono) || __cpp_lib_chrono < 201907L #error "C++20 chrono timezone support (__cpp_lib_chrono >= 201907L) is unavailable" #endif -int main() { return 0; } +#if !defined(__cpp_lib_format) +#error "C++20 formatting support (__cpp_lib_format) is unavailable" +#endif +int main() { + // arrow/util/chrono_internal.h (GH-51267) needs working timezone lookup + // and ; the toolchain must provide both, not just the chrono + // feature-test macro. The probe only compiles and links, it never runs. + const std::chrono::time_zone* tz = std::chrono::locate_zone("UTC"); + std::ostringstream os; + std::vformat_to(std::ostreambuf_iterator(os), "{:%Y}", + std::make_format_args(std::chrono::system_clock::now())); + return tz == nullptr; +} ''' have_std_chrono = cpp_compiler.links( std_chrono_probe_src, @@ -131,7 +148,7 @@ have_std_chrono = cpp_compiler.links( std_chrono_opt = get_option('use_std_chrono') if std_chrono_opt.enabled() if not have_std_chrono - error('use_std_chrono=enabled requires working C++20 chrono timezone ' + error('use_std_chrono=enabled requires working C++20 chrono timezone and formatting ' + 'support, which the current toolchain does not provide') endif needs_std_chrono = true diff --git a/cpp/src/arrow/CMakeLists.txt b/cpp/src/arrow/CMakeLists.txt index 2317ae938d72..232745f6f9d5 100644 --- a/cpp/src/arrow/CMakeLists.txt +++ b/cpp/src/arrow/CMakeLists.txt @@ -421,6 +421,16 @@ if(DEFINED ARROW_USE_STD_CHRONO) else() set(ARROW_USE_STD_CHRONO_DEFINITION "/* #undef ARROW_USE_STD_CHRONO */") endif() +# GH-51267: record whether the vendored datetime implementation is linked, so +# config.cc knows whether a runtime timezone database path can be honored. +# It stays bundled for Gandiva builds even with the std::chrono backend +# (Gandiva calls its timezone functions directly), and whenever the option +# is unset (subproject with ARROW_DEFINE_OPTIONS=OFF keeps datetime.cpp). +if(ARROW_USE_STD_CHRONO AND NOT ARROW_GANDIVA) + set(ARROW_HAVE_VENDORED_DATETIME 0) +else() + set(ARROW_HAVE_VENDORED_DATETIME 1) +endif() configure_file("util/config.h.cmake" "util/config.h" ESCAPE_QUOTES) configure_file("util/config_internal.h.cmake" "util/config_internal.h" ESCAPE_QUOTES) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/util/config.h" diff --git a/cpp/src/arrow/config.cc b/cpp/src/arrow/config.cc index 7fd0f4244fbf..db8ef84c40bd 100644 --- a/cpp/src/arrow/config.cc +++ b/cpp/src/arrow/config.cc @@ -19,12 +19,18 @@ #include +// GH-51267: chrono_internal.h owns the ARROW_USE_STD_CHRONO fallback used when +// the macro is undefined (subproject with ARROW_DEFINE_OPTIONS=OFF); including +// it here keeps config.cc's backend checks consistent with the header instead +// of drifting to the vendored backend on Windows-std builds. +#include "arrow/util/chrono_internal.h" #include "arrow/util/config.h" #include "arrow/util/config_internal.h" #include "arrow/util/cpu_info.h" -// GH-51267: only the vendored datetime backend bundles the vendored timezone -// implementation; std::chrono builds use the OS timezone database instead. -#if !defined(ARROW_USE_STD_CHRONO) || !ARROW_USE_STD_CHRONO +// GH-51267: only builds bundling the vendored datetime implementation carry +// its timezone code; std::chrono builds use the OS timezone database instead, +// except Gandiva builds which still call the vendored functions directly. +#if ARROW_HAVE_VENDORED_DATETIME # include "arrow/vendored/datetime.h" #endif @@ -68,9 +74,9 @@ std::string MakeSimdLevelString(QueryFlagFunction&& query_flag) { } } -#if !defined(ARROW_USE_STD_CHRONO) || !ARROW_USE_STD_CHRONO +#if ARROW_HAVE_VENDORED_DATETIME std::optional timezone_db_path; -#endif // ARROW_USE_STD_CHRONO +#endif // ARROW_HAVE_VENDORED_DATETIME }; // namespace @@ -83,7 +89,7 @@ RuntimeInfo GetRuntimeInfo() { MakeSimdLevelString([&](int64_t flags) { return cpu_info->IsSupported(flags); }); info.detected_simd_level = MakeSimdLevelString([&](int64_t flags) { return cpu_info->IsDetected(flags); }); -#if defined(ARROW_USE_STD_CHRONO) && ARROW_USE_STD_CHRONO +#if ARROW_USE_STD_CHRONO // GH-51267: std::chrono builds always use the OS timezone database. info.using_os_timezone_db = true; info.timezone_db_path = std::optional(); @@ -103,11 +109,8 @@ RuntimeInfo GetRuntimeInfo() { Status Initialize(const GlobalOptions& options) noexcept { ARROW_SUPPRESS_DEPRECATION_WARNING if (options.timezone_db_path.has_value()) { -#if defined(ARROW_USE_STD_CHRONO) && ARROW_USE_STD_CHRONO - return Status::Invalid( - "Arrow was built with C++20 std::chrono and uses the OS timezone database, " - "so a downloaded database cannot be provided at runtime."); -#elif !USE_OS_TZDB +#if ARROW_HAVE_VENDORED_DATETIME +# if !USE_OS_TZDB try { arrow_vendored::date::set_install(options.timezone_db_path.value()); arrow_vendored::date::reload_tzdb(); @@ -115,11 +118,19 @@ Status Initialize(const GlobalOptions& options) noexcept { return Status::IOError(e.what()); } timezone_db_path = options.timezone_db_path.value(); -#else +# else return Status::Invalid( "Arrow was set to use OS timezone database at compile time, " "so a downloaded database cannot be provided at runtime."); -#endif // ARROW_USE_STD_CHRONO / USE_OS_TZDB +# endif +#else + // GH-51267: pure std::chrono builds bundle no vendored timezone code + // (Gandiva builds still do; they take the branch above), so a downloaded + // database cannot be provided at runtime. + return Status::Invalid( + "Arrow was built with C++20 std::chrono and uses the OS timezone database, " + "so a downloaded database cannot be provided at runtime."); +#endif // ARROW_HAVE_VENDORED_DATETIME } ARROW_UNSUPPRESS_DEPRECATION_WARNING return Status::OK(); diff --git a/cpp/src/arrow/public_api_test.cc b/cpp/src/arrow/public_api_test.cc index 280dc9abf545..bb43c63b40bc 100644 --- a/cpp/src/arrow/public_api_test.cc +++ b/cpp/src/arrow/public_api_test.cc @@ -108,6 +108,12 @@ TEST(TransitiveDependencies, WindowsHeadersExposed) { #endif } +// GH-51267: included after the InternalDependencies checks above so the +// vendored datetime headers pulled in on non-std builds don't trip them. +// chrono_internal.h owns the ARROW_USE_STD_CHRONO fallback, so the skip below +// stays consistent with the real backend on subproject builds too. +#include "arrow/util/chrono_internal.h" + TEST(Misc, BuildInfo) { const auto& info = GetBuildInfo(); // The runtime version (GetBuildInfo) should have the same major number as the @@ -126,7 +132,7 @@ TEST(Misc, BuildInfo) { // TODO(GH-48593): Remove when libc++ supports std::chrono timezones. ARROW_SUPPRESS_DEPRECATION_WARNING TEST(Misc, SetTimezoneConfig) { -#if defined(ARROW_USE_STD_CHRONO) && ARROW_USE_STD_CHRONO +#if ARROW_USE_STD_CHRONO GTEST_SKIP() << "std::chrono builds use the OS timezone database (GH-51267)"; #elif !defined(_WIN32) GTEST_SKIP() << "Can only set the Timezone database on Windows"; diff --git a/cpp/src/arrow/util/config.h.cmake b/cpp/src/arrow/util/config.h.cmake index ccce34a9a870..5b1c15203335 100644 --- a/cpp/src/arrow/util/config.h.cmake +++ b/cpp/src/arrow/util/config.h.cmake @@ -55,6 +55,10 @@ #cmakedefine ARROW_S3 #cmakedefine ARROW_USE_GLOG @ARROW_USE_STD_CHRONO_DEFINITION@ +// GH-51267: whether the vendored datetime implementation is linked (it still +// is for Gandiva builds even with the std::chrono backend). config.cc uses +// this to decide whether a runtime timezone database path can be honored. +#cmakedefine01 ARROW_HAVE_VENDORED_DATETIME #cmakedefine ARROW_USE_NATIVE_INT128 #cmakedefine ARROW_WITH_BROTLI #cmakedefine ARROW_WITH_BZ2 diff --git a/cpp/src/arrow/util/meson.build b/cpp/src/arrow/util/meson.build index 92cb39ed37cf..aba9dec4aa70 100644 --- a/cpp/src/arrow/util/meson.build +++ b/cpp/src/arrow/util/meson.build @@ -73,6 +73,11 @@ else ) endif +# GH-51267: Meson never builds Gandiva (needs_gandiva is always false above), +# so the vendored datetime implementation is present exactly when the +# standard backend is off. This mirrors ARROW_HAVE_VENDORED_DATETIME in CMake. +conf_data.set('ARROW_HAVE_VENDORED_DATETIME', not needs_std_chrono) + has_int128 = cpp_compiler.has_define('__SIZEOF_INT128__') conf_data.set('ARROW_USE_NATIVE_INT128', has_int128) diff --git a/r/R/arrow-package.R b/r/R/arrow-package.R index df45f907cc5d..684f0b47855c 100644 --- a/r/R/arrow-package.R +++ b/r/R/arrow-package.R @@ -179,14 +179,14 @@ s3_finalizer <- new.env(parent = emptyenv()) } configure_tzdb <- function() { + if (runtime_info()[[3]] == "true") { + # GH-51267: builds reading the OS timezone database (C++20 std::chrono, + # or the vendored library built against the OS tzdata) already have + # working timezones — skip configuration entirely instead of warning + # that timezones will not be available when the tzdb package is missing. + return(invisible()) + } if (requireNamespace("tzdb", quietly = TRUE)) { - if (runtime_info()[[3]] == "true") { - # GH-51267: builds reading the OS timezone database (C++20 std::chrono, - # or the vendored library built against the OS tzdata) cannot use a - # downloaded database, and their timezones already work — skip the - # vendored path instead of surfacing a false startup failure. - return(invisible()) - } tryCatch( { tzdb::tzdb_initialize() From 6c58a7ef4608d50178fb29b36819b967192668ff Mon Sep 17 00:00:00 2001 From: Oracle Public Cloud User Date: Wed, 16 Sep 2026 14:34:43 +0000 Subject: [PATCH 7/8] GH-51267: [C++] bind the probe time point before make_format_args std::make_format_args takes its arguments by reference, so passing system_clock::now() directly fails to compile on libstdc++ (MinGW error: cannot bind non-const lvalue reference to an rvalue). Bind the time point to a local first. --- cpp/cmake_modules/CheckStdChrono.cmake | 5 ++++- cpp/meson.build | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cpp/cmake_modules/CheckStdChrono.cmake b/cpp/cmake_modules/CheckStdChrono.cmake index 73097c940538..ad1f4c9c995e 100644 --- a/cpp/cmake_modules/CheckStdChrono.cmake +++ b/cpp/cmake_modules/CheckStdChrono.cmake @@ -59,10 +59,13 @@ int main() { // feature-test macro (e.g. GCC 12 advertises __cpp_lib_chrono but has // no ). The probe only compiles and links, it never runs, so // referencing locate_zone here needs no timezone database on the host. + // NOTE: make_format_args takes its arguments by reference, so the + // time point must be bound to a local first, not passed as a temporary. const std::chrono::time_zone* tz = std::chrono::locate_zone(\"UTC\"); std::ostringstream os; + const auto now = std::chrono::system_clock::now(); std::vformat_to(std::ostreambuf_iterator(os), \"{:%Y}\", - std::make_format_args(std::chrono::system_clock::now())); + std::make_format_args(now)); return tz == nullptr; } ") diff --git a/cpp/meson.build b/cpp/meson.build index e02534127927..2813dd8ca8df 100644 --- a/cpp/meson.build +++ b/cpp/meson.build @@ -134,10 +134,13 @@ int main() { // arrow/util/chrono_internal.h (GH-51267) needs working timezone lookup // and ; the toolchain must provide both, not just the chrono // feature-test macro. The probe only compiles and links, it never runs. + // NOTE: make_format_args takes its arguments by reference, so the + // time point must be bound to a local first, not passed as a temporary. const std::chrono::time_zone* tz = std::chrono::locate_zone("UTC"); std::ostringstream os; + const auto now = std::chrono::system_clock::now(); std::vformat_to(std::ostreambuf_iterator(os), "{:%Y}", - std::make_format_args(std::chrono::system_clock::now())); + std::make_format_args(now)); return tz == nullptr; } ''' From 5576d2e0cfabb91ab7b5738b188619d64c6688b2 Mon Sep 17 00:00:00 2001 From: Oracle Public Cloud User Date: Wed, 16 Sep 2026 14:37:03 +0000 Subject: [PATCH 8/8] GH-51267: [C++] include chrono_internal.h outside namespace arrow in test The include landed inside 'namespace arrow', nesting the header's own namespaces (arrow::arrow::internal) and breaking every arrow:: lookup below it on MSVC. Close and reopen the namespace around the include. --- cpp/src/arrow/public_api_test.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/public_api_test.cc b/cpp/src/arrow/public_api_test.cc index bb43c63b40bc..c055846e72dc 100644 --- a/cpp/src/arrow/public_api_test.cc +++ b/cpp/src/arrow/public_api_test.cc @@ -108,12 +108,17 @@ TEST(TransitiveDependencies, WindowsHeadersExposed) { #endif } +} // namespace arrow + // GH-51267: included after the InternalDependencies checks above so the -// vendored datetime headers pulled in on non-std builds don't trip them. +// vendored datetime headers pulled in on non-std builds don't trip them, +// and outside namespace arrow so the header's own namespaces resolve. // chrono_internal.h owns the ARROW_USE_STD_CHRONO fallback, so the skip below // stays consistent with the real backend on subproject builds too. #include "arrow/util/chrono_internal.h" +namespace arrow { + TEST(Misc, BuildInfo) { const auto& info = GetBuildInfo(); // The runtime version (GetBuildInfo) should have the same major number as the