Skip to content

Commit 2a5b3a2

Browse files
Vasil Pashovvasil-pashov
Vasil Pashov
authored andcommitted
Fix dates issue on windows
1 parent a15e884 commit 2a5b3a2

File tree

10 files changed

+69
-57
lines changed

10 files changed

+69
-57
lines changed

cpp/arcticdb/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,9 +523,9 @@ set(arcticdb_srcs
523523
util/offset_string.cpp
524524
util/sparse_utils.cpp
525525
util/string_utils.cpp
526-
util/timer.cpp
527526
util/trace.cpp
528527
util/type_handler.cpp
528+
util/format_date.cpp
529529
version/key_block.hpp
530530
version/key_block.cpp
531531
version/local_versioned_engine.cpp

cpp/arcticdb/pipeline/write_frame.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <arcticdb/stream/incompletes.hpp>
2121
#include <arcticdb/async/task_scheduler.hpp>
2222
#include <arcticdb/util/format_date.hpp>
23+
2324
#include <vector>
2425
#include <array>
2526
#include <ranges>

cpp/arcticdb/util/format_date.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#define BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG // Allows using nanoseconds in boost.
2+
#include <arcticdb/util/format_date.hpp>
3+
#include <boost/date_time/posix_time/time_formatters.hpp>
4+
#include <boost/date_time/posix_time/posix_time_duration.hpp>
5+
#include <boost/date_time/posix_time/conversion.hpp>
6+
7+
namespace arcticdb::util {
8+
9+
std::string format_timestamp(const entity::timestamp ts) {
10+
// Use boost as it can handle nanoseconds both on all OS's.
11+
// std::std::chrono::time_point<std::chrono::system_clock> does not handle nanoseconds on Windows and Mac
12+
const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
13+
const boost::posix_time::ptime time = epoch + boost::posix_time::nanoseconds{ts};
14+
// Custom formatting seems to work best compared to other options.
15+
// * using std::put_time(std::gmtime(...)) throws on Windows when pre-epoch dates are used
16+
// * using boosts time_facet requires the facet used for formatting to be allocated on the heap for each
17+
// formatting call (because it requires calling std::stringstream::imbue which takes onwership of the passed
18+
// pointer
19+
return fmt::format(
20+
"{}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
21+
int{time.date().year()},
22+
int{time.date().month()},
23+
int{time.date().day()},
24+
time.time_of_day().hours(),
25+
time.time_of_day().minutes(),
26+
time.time_of_day().seconds(),
27+
time.time_of_day().fractional_seconds()
28+
);
29+
}
30+
}

cpp/arcticdb/util/format_date.hpp

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,11 @@
77

88
#pragma once
99

10-
#include <fmt/format.h>
11-
#include <chrono>
12-
#include <ctime>
13-
#include <iomanip>
10+
#include <string>
11+
#include <arcticdb/entity/types.hpp>
1412

1513
namespace arcticdb::util {
1614

17-
using Clock = std::chrono::system_clock;
18-
using TimePoint = std::chrono::time_point<Clock>;
19-
20-
inline std::string format_timestamp(entity::timestamp ts) {
21-
std::stringstream ss;
22-
#if defined(_WIN32) || defined(__APPLE__)
23-
auto time_point = Clock::time_point(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(ts)));
24-
#else
25-
auto time_point = Clock::time_point(std::chrono::nanoseconds(ts));
26-
#endif
27-
std::time_t t = Clock::to_time_t(time_point);
28-
auto fraction = time_point - std::chrono::time_point_cast<std::chrono::seconds>(time_point);
29-
ss << std::put_time(std::gmtime(&t), "%F %T.")
30-
<< std::chrono::duration_cast<std::chrono::milliseconds>(fraction).count();
31-
return ss.str();
32-
}
15+
std::string format_timestamp(entity::timestamp ts);
3316

3417
}

cpp/arcticdb/util/storage_lock.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <arcticdb/stream/index.hpp>
1414
#include <arcticdb/util/exponential_backoff.hpp>
1515
#include <arcticdb/util/configs_map.hpp>
16+
#include <arcticdb/util/format_date.hpp>
1617

1718
#include <fmt/std.h>
1819
#include <mutex>
@@ -163,7 +164,12 @@ class StorageLock {
163164
ts_ = 0;
164165
log::lock().info("Lock timed out, giving up after {}", wait_ms);
165166
mutex_.unlock();
166-
throw StorageLockTimeout{fmt::format("Storage lock {} timeout out after {} ms. Lock held since {} (UTC)", name_, total_wait, date_and_time(*read_ts))};
167+
throw StorageLockTimeout{fmt::format(
168+
"Storage lock {} timeout out after {} ms. Lock held since {} (UTC)",
169+
name_,
170+
total_wait,
171+
util::format_timestamp(*read_ts)
172+
)};
167173
}
168174
}
169175
ts_ = create_ref_key(store);

cpp/arcticdb/util/test/test_format_date.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@
1010
#include <arcticdb/util/format_date.hpp>
1111

1212
TEST(FormatDate, ZeroTs) {
13-
using namespace arcticdb;
14-
ASSERT_EQ("1970-01-01 00:00:00.0", util::format_timestamp(0));
13+
ASSERT_EQ("1970-01-01 00:00:00.000000000", arcticdb::util::format_timestamp(0));
14+
}
15+
16+
TEST(FormatDate, PrependZero) {
17+
ASSERT_EQ("2025-06-09 08:06:09.000000000", arcticdb::util::format_timestamp(1749456369000000000));
18+
ASSERT_EQ("2025-06-09 08:06:09.000000001", arcticdb::util::format_timestamp(1749456369000000000 + 1));
19+
ASSERT_EQ("2025-06-09 00:00:00.000000000", arcticdb::util::format_timestamp(1749427200000000000));
20+
}
21+
22+
TEST(FormatDate, PreEpoch) {
23+
ASSERT_EQ("1969-12-31 23:59:59.999999999", arcticdb::util::format_timestamp(-1));
24+
ASSERT_EQ("1969-12-31 23:59:59.000000000", arcticdb::util::format_timestamp(-1'000'000'000));
1525
}
1626

1727
TEST(FormatDate, April2821) {
18-
using namespace arcticdb;
19-
#ifdef __linux__
20-
ASSERT_EQ("2021-04-28 16:11:35.213", util::format_timestamp(1619626295213000000));
21-
#else
22-
ASSERT_EQ("2021-04-28 16:11:35.0", util::format_timestamp(1619626295213000000));
23-
#endif
28+
ASSERT_EQ("2021-04-28 16:11:35.213000000", arcticdb::util::format_timestamp(1619626295213000000));
2429
}

cpp/arcticdb/util/timer.cpp

Lines changed: 0 additions & 13 deletions
This file was deleted.

cpp/arcticdb/util/timer.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,4 @@ arcticdb::ScopedTimerTotal timer2{#name, [&data](auto totals) { \
292292
std::copy(std::begin(totals), std::end(totals), std::back_inserter(data)); \
293293
}};
294294

295-
[[nodiscard]] std::string date_and_time(int64_t ts);
296-
297-
298295
} //namespace arcticdb

cpp/arcticdb/version/version_core.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <arcticdb/version/version_utils.hpp>
2929
#include <arcticdb/entity/merge_descriptors.hpp>
3030
#include <arcticdb/processing/component_manager.hpp>
31+
#include <arcticdb/util/format_date.hpp>
32+
3133
#include <ranges>
3234

3335
namespace arcticdb::version_store {
@@ -1262,9 +1264,9 @@ static void check_incompletes_index_ranges_dont_overlap(const std::shared_ptr<Pi
12621264
sorting::check<ErrorCode::E_UNSORTED_DATA>(
12631265
!last_existing_index_value.has_value() || key.start_time() >= *last_existing_index_value,
12641266
"Cannot append staged segments to existing data as incomplete segment contains index value < existing data (in UTC): {} <= {}",
1265-
date_and_time(key.start_time()),
1267+
util::format_timestamp(key.start_time()),
12661268
// Should never reach "" but the standard mandates that all function arguments are evaluated
1267-
last_existing_index_value ? date_and_time(*last_existing_index_value) : ""
1269+
last_existing_index_value ? util::format_timestamp(*last_existing_index_value) : ""
12681270
);
12691271
auto [_, inserted] = unique_timestamp_ranges.emplace(key.start_time(), key.end_time());
12701272
// This is correct because incomplete segments aren't column sliced
@@ -1273,7 +1275,7 @@ static void check_incompletes_index_ranges_dont_overlap(const std::shared_ptr<Pi
12731275
// -1 as end_time is stored as 1 greater than the last index value in the segment
12741276
inserted || key.end_time() -1 == key.start_time(),
12751277
"Cannot finalize staged data as 2 or more incomplete segments cover identical index values (in UTC): ({}, {})",
1276-
date_and_time(key.start_time()), date_and_time(key.end_time()));
1278+
util::format_timestamp(key.start_time()), util::format_timestamp(key.end_time()));
12771279
}
12781280

12791281
for (auto it = unique_timestamp_ranges.begin(); it != unique_timestamp_ranges.end(); it++) {
@@ -1283,10 +1285,11 @@ static void check_incompletes_index_ranges_dont_overlap(const std::shared_ptr<Pi
12831285
// -1 as end_time is stored as 1 greater than the last index value in the segment
12841286
next_it->first >= it->second - 1,
12851287
"Cannot finalize staged data as incomplete segment index values overlap one another (in UTC): ({}, {}) intersects ({}, {})",
1286-
date_and_time(it->first),
1287-
date_and_time(it->second - 1),
1288-
date_and_time(next_it->first),
1289-
date_and_time(next_it->second - 1));
1288+
util::format_timestamp(it->first),
1289+
util::format_timestamp(it->second - 1),
1290+
util::format_timestamp(next_it->first),
1291+
util::format_timestamp(next_it->second - 1)
1292+
);
12901293
}
12911294
}
12921295
}
@@ -1903,8 +1906,8 @@ VersionedItem sort_merge_impl(
19031906
sorting::check<ErrorCode::E_UNSORTED_DATA>(
19041907
last_index_on_disc <= incomplete_start,
19051908
"Cannot append staged segments to existing data as incomplete segment contains index value {} < existing data {}",
1906-
date_and_time(incomplete_start),
1907-
date_and_time(last_index_on_disc)
1909+
util::format_timestamp(incomplete_start),
1910+
util::format_timestamp(last_index_on_disc)
19081911
);
19091912
}
19101913
pipeline_context->total_rows_ = num_versioned_rows + get_slice_rowcounts(segments);

python/tests/hypothesis/arcticdb/test_resample.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
COLUMN_DTYPE = ["float", "int", "uint"]
1717
ALL_AGGREGATIONS = ["sum", "mean", "min", "max", "first", "last", "count"]
18-
MIN_DATE = np.datetime64('1969-06-01')
19-
MAX_DATE = np.datetime64('1970-06-01')
18+
MIN_DATE = np.datetime64('1969-01-01')
19+
MAX_DATE = np.datetime64('1971-01-01')
2020

2121
pytestmark = pytest.mark.pipeline
2222

0 commit comments

Comments
 (0)