Skip to content
Closed
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
29 changes: 28 additions & 1 deletion cpp/src/arrow/compute/kernels/scalar_cast_temporal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
#include "arrow/compute/kernels/scalar_cast_internal.h"
#include "arrow/compute/kernels/temporal_internal.h"
#include "arrow/util/bitmap_reader.h"
#include "arrow/util/int_util.h"
#include "arrow/util/logging_internal.h"
#include "arrow/util/time.h"
#include "arrow/util/value_parsing.h"

namespace arrow {

using internal::IntegersCanFit;
using internal::ParseTimestampISO8601;
using internal::ParseYYYY_MM_DD;

Expand Down Expand Up @@ -495,6 +497,22 @@ struct CastFunctor<O, I,
}
};

// Integer values represent days when cast to date32. Keep the same
// overflow-checking semantics as integer-to-integer casts while writing the
// result into the date32 array's int32 storage.
template <typename I>
struct CastFunctor<Date32Type, I, enable_if_t<is_integer_type<I>::value>> {
static Status Exec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) {
const auto& options = checked_cast<const CastState&>(*ctx->state()).options;
if (!options.allow_int_overflow) {
RETURN_NOT_OK(IntegersCanFit(batch[0].array, *int32()));
}
CastNumberToNumberUnsafe(batch[0].type()->id(), Type::INT32, batch[0].array,
out->array_span_mutable());
return Status::OK();
}
};

namespace {

template <typename Type>
Expand Down Expand Up @@ -523,9 +541,18 @@ std::shared_ptr<CastFunction> GetDate32Cast() {
// date32 -> date32
AddZeroCopyCast(Type::DATE32, date32(), date32(), func.get());

// int32 -> date32
// int32 -> date32 (zero copy)
AddZeroCopyCast(Type::INT32, int32(), date32(), func.get());

// Other integer types -> date32
for (const auto& in_ty : IntTypes()) {
if (in_ty->id() == Type::INT32) {
continue;
}
auto exec = GenerateInteger<CastFunctor, Date32Type>(in_ty->id());
DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, date32(), std::move(exec)));
}

// date64 -> date32
AddSimpleCast<Date64Type, Date32Type>(date64(), date32(), func.get());

Expand Down
15 changes: 15 additions & 0 deletions cpp/src/arrow/compute/kernels/scalar_cast_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ TEST(Cast, CanCast) {
ExpectCanCast(null(), {smallint()});
ExpectCanCast(tinyint(), {smallint()}); // cast between compatible storage types

for (const auto& integer_type : kIntegerTypes) {
ExpectCanCast(integer_type, {date32()});
}
ExpectCanCast(date32(), {utf8(), large_utf8()});
ExpectCanCast(date64(), {utf8(), large_utf8()});
ExpectCanCast(timestamp(TimeUnit::NANO), {utf8(), large_utf8()});
Expand Down Expand Up @@ -2831,6 +2834,18 @@ TEST(Cast, DateToDate) {
CheckCast(day_64_will_be_truncated, day_32, options);
}

TEST(Cast, IntegerToDate32) {
auto expected = ArrayFromJSON(date32(), "[0, null, 1, 2]");
for (const auto& integer_type : kIntegerTypes) {
CheckCast(ArrayFromJSON(integer_type, "[0, null, 1, 2]"), expected);
}

CheckCastFails(ArrayFromJSON(int64(), "[0, 2147483648]"),
CastOptions::Safe(date32()));
CheckCastFails(ArrayFromJSON(uint64(), "[0, 4294967296]"),
CastOptions::Safe(date32()));
}

TEST(Cast, DateZeroCopy) {
for (auto zero_copy_to_type : {
date32(),
Expand Down
11 changes: 11 additions & 0 deletions python/pyarrow/tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,17 @@ def test_cast_timestamp_to_int():
assert result.equals(expected)


@pytest.mark.numpy
def test_cast_date32_from_numpy_integers():
expected = pa.array([datetime.date(1970, 1, 1),
datetime.date(1970, 1, 2),
datetime.date(1970, 1, 3)], type=pa.date32())
for dtype in ['int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32',
'int64', 'uint64']:
result = pa.array(np.array([0, 1, 2], dtype=dtype), type=pa.date32())
assert result.equals(expected)


def test_cast_date32_to_int():
arr = pa.array([0, 1, 2], type='i4')

Expand Down
Loading