Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 26 additions & 23 deletions cpp/src/gandiva/precompiled/time.cc
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,27 @@ bool is_valid_time(const int hours, const int minutes, const int seconds) {
seconds < 60;
}

// Normalize sub-seconds value to milliseconds precision (3 digits).
// Truncates if more than 3 digits are provided, pads with zeros if fewer than 3 digits
static inline int32_t normalize_subseconds_to_millis(int32_t subseconds,
int32_t num_digits) {
if (num_digits <= 0 || num_digits == 3) {
// No need to adjust
return subseconds;
}
// Calculate the power of 10 adjustment needed
int32_t digit_diff = num_digits - 3;
while (digit_diff > 0) {
subseconds /= 10;
digit_diff--;
}
while (digit_diff < 0) {
subseconds *= 10;
digit_diff++;
}
return subseconds;
}

// MONTHS_BETWEEN returns number of months between dates date1 and date2.
// If date1 is later than date2, then the result is positive.
// If date1 is earlier than date2, then the result is negative.
Expand Down Expand Up @@ -744,17 +765,8 @@ gdv_timestamp castTIMESTAMP_utf8(int64_t context, const char* input, gdv_int32 l
}

// adjust the milliseconds
if (sub_seconds_len > 0) {
if (sub_seconds_len > 3) {
const char* msg = "Invalid millis for timestamp value ";
set_error_for_date(length, input, msg, context);
return 0;
}
while (sub_seconds_len < 3) {
ts_fields[TimeFields::kSubSeconds] *= 10;
sub_seconds_len++;
}
}
ts_fields[TimeFields::kSubSeconds] =
normalize_subseconds_to_millis(ts_fields[TimeFields::kSubSeconds], sub_seconds_len);
// handle timezone
if (encountered_zone) {
int err = 0;
Expand Down Expand Up @@ -864,18 +876,9 @@ gdv_time32 castTIME_utf8(int64_t context, const char* input, int32_t length) {
}

// adjust the milliseconds
if (sub_seconds_len > 0) {
if (sub_seconds_len > 3) {
const char* msg = "Invalid millis for time value ";
set_error_for_date(length, input, msg, context);
return 0;
}

while (sub_seconds_len < 3) {
time_fields[TimeFields::kSubSeconds - TimeFields::kHours] *= 10;
sub_seconds_len++;
}
}
time_fields[TimeFields::kSubSeconds - TimeFields::kHours] =
normalize_subseconds_to_millis(
time_fields[TimeFields::kSubSeconds - TimeFields::kHours], sub_seconds_len);

int32_t input_hours = time_fields[TimeFields::kHours - TimeFields::kHours];
int32_t input_minutes = time_fields[TimeFields::kMinutes - TimeFields::kHours];
Expand Down
54 changes: 39 additions & 15 deletions cpp/src/gandiva/precompiled/time_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,26 @@ TEST(TestTime, TestCastTimestamp) {
"Not a valid time for timestamp value 2000-01-01 00:00:100");
context.Reset();

EXPECT_EQ(castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.0001", 24), 0);
EXPECT_EQ(context.get_error(),
"Invalid millis for timestamp value 2000-01-01 00:00:00.0001");
context.Reset();

EXPECT_EQ(castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.1000", 24), 0);
EXPECT_EQ(context.get_error(),
"Invalid millis for timestamp value 2000-01-01 00:00:00.1000");
context.Reset();
// Test truncation of subseconds to 3 digits (milliseconds)
// "2000-01-01 00:00:00.0001" should truncate to "2000-01-01 00:00:00.000"
EXPECT_EQ(castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.0001", 24),
castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.000", 23));

// "2000-01-01 00:00:00.1000" should truncate to "2000-01-01 00:00:00.100"
EXPECT_EQ(castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.1000", 24),
castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.100", 23));

// "2000-01-01 00:00:00.123456789" should truncate to "2000-01-01 00:00:00.123"
EXPECT_EQ(castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.123456789", 29),
castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.123", 23));

// "2000-01-01 00:00:00.1999" should truncate to "2000-01-01 00:00:00.199"
EXPECT_EQ(castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.1999", 24),
castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.199", 23));

// "2000-01-01 00:00:00.1994" should truncate to "2000-01-01 00:00:00.199"
EXPECT_EQ(castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.1994", 24),
castTIMESTAMP_utf8(context_ptr, "2000-01-01 00:00:00.199", 23));
}

TEST(TestTime, TestCastTimeUtf8) {
Expand Down Expand Up @@ -165,13 +176,26 @@ TEST(TestTime, TestCastTimeUtf8) {
EXPECT_EQ(context.get_error(), "Not a valid time value 00:00:100");
context.Reset();

EXPECT_EQ(castTIME_utf8(context_ptr, "00:00:00.0001", 13), 0);
EXPECT_EQ(context.get_error(), "Invalid millis for time value 00:00:00.0001");
context.Reset();
// Test truncation of subseconds to 3 digits (milliseconds)
// "00:00:00.0001" should truncate to "00:00:00.000"
EXPECT_EQ(castTIME_utf8(context_ptr, "00:00:00.0001", 13),
castTIME_utf8(context_ptr, "00:00:00.000", 12));

EXPECT_EQ(castTIME_utf8(context_ptr, "00:00:00.1000", 13), 0);
EXPECT_EQ(context.get_error(), "Invalid millis for time value 00:00:00.1000");
context.Reset();
// "00:00:00.1000" should truncate to "00:00:00.100"
EXPECT_EQ(castTIME_utf8(context_ptr, "00:00:00.1000", 13),
castTIME_utf8(context_ptr, "00:00:00.100", 12));

// "9:45:30.123456789" should truncate to "9:45:30.123"
EXPECT_EQ(castTIME_utf8(context_ptr, "9:45:30.123456789", 17),
castTIME_utf8(context_ptr, "9:45:30.123", 11));

// "00:00:00.1999" should truncate to "00:00:00.199"
EXPECT_EQ(castTIME_utf8(context_ptr, "00:00:00.1999", 13),
castTIME_utf8(context_ptr, "00:00:00.199", 12));

// "00:00:00.1994" should truncate to "00:00:00.199"
EXPECT_EQ(castTIME_utf8(context_ptr, "00:00:00.1994", 13),
castTIME_utf8(context_ptr, "00:00:00.199", 12));
}

#ifndef _WIN32
Expand Down
11 changes: 7 additions & 4 deletions dev/tasks/java-jars/github.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
archery_arch: "amd64"
archery_arch_alias: "x86_64"
archery_arch_short: "amd64"
- runs_on: ["buildjet-8vcpu-ubuntu-2204-arm"]
- runs_on: ["ubuntu-24.04-arm"]
arch: "aarch_64"
archery_arch: "arm64v8"
archery_arch_alias: "aarch64"
Expand All @@ -65,6 +65,11 @@ jobs:
env:
{{ macros.github_set_sccache_envvars()|indent(8) }}
run: |
# ubuntu-24.04-arm ships Docker Compose v2 as a plugin only; archery needs standalone docker-compose
if ! command -v docker-compose &>/dev/null; then
printf '#!/bin/sh\nexec docker compose "$@"\n' | sudo tee /usr/local/bin/docker-compose >/dev/null
sudo chmod +x /usr/local/bin/docker-compose
fi
archery docker run \
-e ARROW_JAVA_BUILD=OFF \
-e ARROW_JAVA_TEST=OFF \
Expand Down Expand Up @@ -148,7 +153,7 @@ jobs:
# used on test We uninstall Homebrew's Protobuf to ensure using
# bundled Protobuf.
brew uninstall protobuf
# fix cmake and boost versionsAdd commentMore actions
# fix cmake and boost versions
brew uninstall -f boost || true
brew uninstall -f cmake || true
mkdir -p homebrew-custom/Formula
Expand All @@ -158,8 +163,6 @@ jobs:
cp ./homebrew-custom/Formula/*.rb "$(brew --repo local/homebrew-custom)/Formula/"
brew install -v local/homebrew-custom/cmake
brew install -v local/homebrew-custom/boost
brew pin cmake
brew pin boost
#


Expand Down
Loading