Skip to content

Commit 4283740

Browse files
committed
simplify with C++20 chrono features
1 parent 941f48e commit 4283740

File tree

2 files changed

+19
-126
lines changed

2 files changed

+19
-126
lines changed

cpp/src/arrow/compute/kernels/scalar_temporal_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include <gtest/gtest.h>
2121

2222
#include "arrow/compute/api_scalar.h"
23-
#include "arrow/util/chrono_internal.h" // for ARROW_USE_STD_CHRONO
2423
#include "arrow/compute/cast.h"
2524
#include "arrow/compute/kernels/test_util_internal.h"
2625
#include "arrow/testing/gtest_util.h"
@@ -30,6 +29,7 @@
3029
#include "arrow/type_fwd.h"
3130
#include "arrow/type_traits.h"
3231
#include "arrow/util/checked_cast.h"
32+
#include "arrow/util/chrono_internal.h" // for ARROW_USE_STD_CHRONO
3333
#include "arrow/util/formatting.h"
3434
#include "arrow/util/logging_internal.h"
3535

cpp/src/arrow/util/chrono_internal.h

Lines changed: 18 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@
2929
/// database, eliminating the need for users to install IANA tzdata separately.
3030

3131
#include <chrono>
32-
#include <ctime>
33-
#include <iomanip>
34-
#include <sstream>
3532
#include <string>
3633
#include <string_view>
3734

@@ -53,6 +50,9 @@
5350

5451
#if ARROW_USE_STD_CHRONO
5552
// Use C++20 standard library chrono
53+
# include <format>
54+
# include <iterator>
55+
# include <ostream>
5656
#else
5757
// Use vendored Howard Hinnant date library
5858
# include "arrow/vendored/datetime.h"
@@ -119,18 +119,16 @@ using std::chrono::ceil;
119119
using std::chrono::floor;
120120
using std::chrono::round;
121121

122-
// trunc is not in std::chrono - implement proper truncation toward zero
123-
// floor rounds toward negative infinity, but trunc rounds toward zero
122+
// trunc (truncation toward zero) is not in std::chrono, only floor/ceil/round
124123
template <typename ToDuration, typename Rep, typename Period>
125124
constexpr ToDuration trunc(const std::chrono::duration<Rep, Period>& d) {
126-
auto floor_result = std::chrono::floor<ToDuration>(d);
127-
auto remainder = d - floor_result;
128-
// If original was negative and there's a non-zero remainder,
129-
// floor went too far negative, so add one unit back
130-
if (d.count() < 0 && remainder.count() != 0) {
131-
return floor_result + ToDuration{1};
125+
auto floored = std::chrono::floor<ToDuration>(d);
126+
// floor rounds toward -infinity; for negative values with remainder, add 1 to get
127+
// toward zero
128+
if (d.count() < 0 && (d - floored).count() != 0) {
129+
return floored + ToDuration{1};
132130
}
133-
return floor_result;
131+
return floored;
134132
}
135133

136134
// Timezone lookup
@@ -140,127 +138,22 @@ inline const time_zone* locate_zone(std::string_view tz_name) {
140138

141139
inline const time_zone* current_zone() { return std::chrono::current_zone(); }
142140

143-
// Helper to get subsecond decimal places based on duration period
144-
template <typename Duration>
145-
constexpr int get_subsecond_decimals() {
146-
using Period = typename Duration::period;
147-
if constexpr (Period::den == 1000)
148-
return 3; // milliseconds
149-
else if constexpr (Period::den == 1000000)
150-
return 6; // microseconds
151-
else if constexpr (Period::den == 1000000000)
152-
return 9; // nanoseconds
153-
else
154-
return 0; // seconds or coarser
155-
}
156-
157-
// Formatting support with subsecond precision and timezone handling
158-
// Mimics the vendored date library's to_stream behavior for compatibility
141+
// Formatting support - streams directly using C++20 std::vformat_to
142+
// Provides: direct streaming, stream state preservation, chaining, rich format specifiers
159143
template <typename CharT, typename Traits, typename Duration, typename TimeZonePtr>
160144
std::basic_ostream<CharT, Traits>& to_stream(
161145
std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
162146
const std::chrono::zoned_time<Duration, TimeZonePtr>& zt) {
163-
// Get local time and timezone info
164-
auto lt = zt.get_local_time();
165-
auto info = zt.get_info();
166-
167-
auto lt_days = std::chrono::floor<days>(lt);
168-
auto ymd = year_month_day{lt_days};
169-
170-
// Calculate time of day components
171-
auto time_since_midnight = lt - local_time<days>{lt_days};
172-
auto total_secs = std::chrono::duration_cast<std::chrono::seconds>(time_since_midnight);
173-
auto h = std::chrono::duration_cast<std::chrono::hours>(time_since_midnight);
174-
auto m = std::chrono::duration_cast<std::chrono::minutes>(time_since_midnight - h);
175-
auto s = std::chrono::duration_cast<std::chrono::seconds>(time_since_midnight - h - m);
176-
177-
// Build std::tm for strftime
178-
std::tm tm{};
179-
tm.tm_sec = static_cast<int>(s.count());
180-
tm.tm_min = static_cast<int>(m.count());
181-
tm.tm_hour = static_cast<int>(h.count());
182-
tm.tm_mday = static_cast<int>(static_cast<unsigned>(ymd.day()));
183-
tm.tm_mon = static_cast<int>(static_cast<unsigned>(ymd.month())) - 1;
184-
tm.tm_year = static_cast<int>(ymd.year()) - 1900;
185-
186-
auto wd = weekday{lt_days};
187-
tm.tm_wday = static_cast<int>(wd.c_encoding());
188-
189-
auto year_start =
190-
std::chrono::local_days{ymd.year() / std::chrono::January / std::chrono::day{1}};
191-
tm.tm_yday = static_cast<int>((lt_days - year_start).count());
192-
tm.tm_isdst = info.save != std::chrono::minutes{0} ? 1 : 0;
193-
194-
// Timezone offset calculation
195-
auto offset_mins = std::chrono::duration_cast<std::chrono::minutes>(info.offset);
196-
bool neg_offset = offset_mins.count() < 0;
197-
auto abs_offset = neg_offset ? -offset_mins : offset_mins;
198-
auto off_h = std::chrono::duration_cast<std::chrono::hours>(abs_offset);
199-
auto off_m = abs_offset - off_h;
200-
201-
// Calculate subsecond value
202-
constexpr int decimals = get_subsecond_decimals<Duration>();
203-
int64_t subsec_value = 0;
204-
if constexpr (decimals > 0) {
205-
auto subsec_duration = time_since_midnight - total_secs;
206-
subsec_value = std::chrono::duration_cast<Duration>(subsec_duration).count();
207-
if (subsec_value < 0) subsec_value = -subsec_value;
208-
}
209-
210-
// Parse format string, handle %S, %z, %Z specially
211-
std::string result;
212-
for (const CharT* p = fmt; *p; ++p) {
213-
if (*p == '%' && *(p + 1)) {
214-
CharT spec = *(p + 1);
215-
if (spec == 'S') {
216-
// %S with subsecond precision
217-
result += (tm.tm_sec < 10 ? "0" : "") + std::to_string(tm.tm_sec);
218-
if constexpr (decimals > 0) {
219-
std::ostringstream ss;
220-
ss << '.' << std::setfill('0') << std::setw(decimals) << subsec_value;
221-
result += ss.str();
222-
}
223-
++p;
224-
} else if (spec == 'z') {
225-
// %z timezone offset
226-
std::ostringstream ss;
227-
ss << (neg_offset ? '-' : '+') << std::setfill('0') << std::setw(2)
228-
<< off_h.count() << std::setfill('0') << std::setw(2) << off_m.count();
229-
result += ss.str();
230-
++p;
231-
} else if (spec == 'Z') {
232-
// %Z timezone abbreviation
233-
result += info.abbrev;
234-
++p;
235-
} else {
236-
// Use strftime for other specifiers
237-
char buf[64];
238-
char small_fmt[3] = {'%', static_cast<char>(spec), '\0'};
239-
if (std::strftime(buf, sizeof(buf), small_fmt, &tm) > 0) {
240-
result += buf;
241-
}
242-
++p;
243-
}
244-
} else {
245-
result += static_cast<char>(*p);
246-
}
247-
}
248-
249-
return os << result;
147+
std::vformat_to(std::ostreambuf_iterator<CharT>(os), std::string("{:") + fmt + "}",
148+
std::make_format_args(zt));
149+
return os;
250150
}
251151

152+
// Format a duration using strftime-like format specifiers
153+
// Converts "%H%M" style to C++20's "{:%H%M}" style and uses std::vformat
252154
template <typename Duration>
253155
std::string format(const char* fmt, const Duration& d) {
254-
std::ostringstream ss;
255-
auto total_minutes = std::chrono::duration_cast<std::chrono::minutes>(d).count();
256-
bool negative = total_minutes < 0;
257-
if (negative) total_minutes = -total_minutes;
258-
auto hours = total_minutes / 60;
259-
auto mins = total_minutes % 60;
260-
ss << (negative ? "-" : "+");
261-
ss << std::setfill('0') << std::setw(2) << hours;
262-
ss << std::setfill('0') << std::setw(2) << mins;
263-
return ss.str();
156+
return std::vformat(std::string("{:") + fmt + "}", std::make_format_args(d));
264157
}
265158

266159
// Literals namespace

0 commit comments

Comments
 (0)