Skip to content

Commit

Permalink
Merge pull request #1 from Enmk/fix_convertFieldToType
Browse files Browse the repository at this point in the history
Fixed convertFieldToType case of converting Date and Date32 to DateTime64 Field
  • Loading branch information
lucas-tubi authored May 30, 2023
2 parents 954e76c + 93cf343 commit 01e2326
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 6 deletions.
16 changes: 10 additions & 6 deletions src/Interpreters/convertFieldToType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,19 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID
}
else if (which_type.isDateTime64() && which_from_type.isDate())
{
const DataTypeDateTime64 & data_type_date_time64 = static_cast<const DataTypeDateTime64 &>(type);
const Int64 value = data_type_date_time64.getTimeZone().fromDayNum(DayNum(src.get<UInt64>()));
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, data_type_date_time64.getScaleMultiplier());
const auto & date_time64_type = static_cast<const DataTypeDateTime64 &>(type);
const auto value = date_time64_type.getTimeZone().fromDayNum(DayNum(src.get<UInt16>()));
return DecimalField(
DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, date_time64_type.getScaleMultiplier()),
date_time64_type.getScale());
}
else if (which_type.isDateTime64() && which_from_type.isDate32())
{
const DataTypeDateTime64 & data_type_date_time64 = static_cast<const DataTypeDateTime64 &>(type);
const Int64 value = data_type_date_time64.getTimeZone().fromDayNum(DayNum(src.get<Int32>()));
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, data_type_date_time64.getScaleMultiplier());
const auto & date_time64_type = static_cast<const DataTypeDateTime64 &>(type);
const auto value = date_time64_type.getTimeZone().fromDayNum(ExtendedDayNum(static_cast<Int32>(src.get<Int32>())));
return DecimalField(
DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, date_time64_type.getScaleMultiplier()),
date_time64_type.getScale());
}
else if (type.isValueRepresentedByNumber() && src.getType() != Field::Types::String)
{
Expand Down
185 changes: 185 additions & 0 deletions src/Interpreters/tests/gtest_convertFieldToType.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#include <initializer_list>
#include <limits>
#include <ostream>
#include <Core/Field.h>
#include <Core/iostream_debug_helpers.h>
#include <Interpreters/convertFieldToType.h>
#include <DataTypes/DataTypeFactory.h>

#include <gtest/gtest.h>
#include "base/Decimal.h"
#include "base/types.h"
#include "gtest/gtest.h"

using namespace DB;

struct ConvertFieldToTypeTestParams
{
const char * from_type; // MUST NOT BE NULL
const Field from_value;
const char * to_type; // MUST NOT BE NULL
const std::optional<Field> expected_value;
};

std::ostream & operator << (std::ostream & ostr, const ConvertFieldToTypeTestParams & params)
{
return ostr << "{"
<< "\n\tfrom_type : " << params.from_type
<< "\n\tfrom_value : " << params.from_value
<< "\n\tto_type : " << params.to_type
<< "\n\texpected : " << (params.expected_value ? *params.expected_value : Field())
<< "\n}";
}

class ConvertFieldToTypeTest : public ::testing::TestWithParam<ConvertFieldToTypeTestParams>
{};

TEST_P(ConvertFieldToTypeTest, convert)
{
const auto & params = GetParam();

ASSERT_NE(nullptr, params.from_type);
ASSERT_NE(nullptr, params.to_type);

const auto & type_factory = DataTypeFactory::instance();
const auto from_type = type_factory.get(params.from_type);
const auto to_type = type_factory.get(params.to_type);

if (params.expected_value)
{
const auto result = convertFieldToType(params.from_value, *to_type, from_type.get());
EXPECT_EQ(*params.expected_value, result);
}
else
{
EXPECT_ANY_THROW(convertFieldToType(params.from_value, *to_type, from_type.get()));
}
}

// Basically nuber of seconds in a day, works for UTC here
const long long int Day = 24 * 60 * 60;

// 123 is arbitrary value here

INSTANTIATE_TEST_SUITE_P(
DateToDateTime64,
ConvertFieldToTypeTest,
::testing::ValuesIn(std::initializer_list<ConvertFieldToTypeTestParams>{
// min value of Date
{
"Date",
Field(0),
"DateTime64(0, 'UTC')",
DecimalField(DateTime64(0), 0)
},
// Max value of Date
{
"Date",
Field(std::numeric_limits<DB::UInt16>::max()),
"DateTime64(0, 'UTC')",
DecimalField(DateTime64(std::numeric_limits<DB::UInt16>::max() * Day), 0)
},
// check that scale is respected
{
"Date",
Field(123),
"DateTime64(0, 'UTC')",
DecimalField(DateTime64(123 * Day), 0)
},
{
"Date",
Field(1),
"DateTime64(1, 'UTC')",
DecimalField(DateTime64(Day * 10), 1)
},
{
"Date",
Field(123),
"DateTime64(3, 'UTC')",
DecimalField(DateTime64(123 * Day * 1000), 3)
},
{
"Date",
Field(123),
"DateTime64(6, 'UTC')",
DecimalField(DateTime64(123 * Day * 1'000'000), 6)
},
})
);

INSTANTIATE_TEST_SUITE_P(
Date32ToDateTime64,
ConvertFieldToTypeTest,
::testing::ValuesIn(std::initializer_list<ConvertFieldToTypeTestParams>{
// min value of Date32: 1st Jan 1900 (see DATE_LUT_MIN_YEAR)
{
"Date32",
Field(-25'567),
"DateTime64(0, 'UTC')",
DecimalField(DateTime64(-25'567 * Day), 0)
},
// max value of Date32: 31 Dec 2299 (see DATE_LUT_MAX_YEAR)
{
"Date32",
Field(120'529),
"DateTime64(0, 'UTC')",
DecimalField(DateTime64(120'529 * Day), 0)
},
// check that scale is respected
{
"Date32",
Field(123),
"DateTime64(0, 'UTC')",
DecimalField(DateTime64(123 * Day), 0)
},
{
"Date32",
Field(123),
"DateTime64(1, 'UTC')",
DecimalField(DateTime64(123 * Day * 10), 1)
},
{
"Date32",
Field(123),
"DateTime64(3, 'UTC')",
DecimalField(DateTime64(123 * Day * 1000), 3)
},
{
"Date32",
Field(123),
"DateTime64(6, 'UTC')",
DecimalField(DateTime64(123 * Day * 1'000'000), 6)
}
})
);

INSTANTIATE_TEST_SUITE_P(
DateTimeToDateTime64,
ConvertFieldToTypeTest,
::testing::ValuesIn(std::initializer_list<ConvertFieldToTypeTestParams>{
{
"DateTime",
Field(1),
"DateTime64(0, 'UTC')",
DecimalField(DateTime64(1), 0)
},
{
"DateTime",
Field(1),
"DateTime64(1, 'UTC')",
DecimalField(DateTime64(1'0), 1)
},
{
"DateTime",
Field(123),
"DateTime64(3, 'UTC')",
DecimalField(DateTime64(123'000), 3)
},
{
"DateTime",
Field(123),
"DateTime64(6, 'UTC')",
DecimalField(DateTime64(123'000'000), 6)
},
})
);

0 comments on commit 01e2326

Please sign in to comment.