Skip to content

Commit 01e2326

Browse files
authored
Merge pull request #1 from Enmk/fix_convertFieldToType
Fixed convertFieldToType case of converting Date and Date32 to DateTime64 Field
2 parents 954e76c + 93cf343 commit 01e2326

2 files changed

Lines changed: 195 additions & 6 deletions

File tree

src/Interpreters/convertFieldToType.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,19 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID
194194
}
195195
else if (which_type.isDateTime64() && which_from_type.isDate())
196196
{
197-
const DataTypeDateTime64 & data_type_date_time64 = static_cast<const DataTypeDateTime64 &>(type);
198-
const Int64 value = data_type_date_time64.getTimeZone().fromDayNum(DayNum(src.get<UInt64>()));
199-
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, data_type_date_time64.getScaleMultiplier());
197+
const auto & date_time64_type = static_cast<const DataTypeDateTime64 &>(type);
198+
const auto value = date_time64_type.getTimeZone().fromDayNum(DayNum(src.get<UInt16>()));
199+
return DecimalField(
200+
DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, date_time64_type.getScaleMultiplier()),
201+
date_time64_type.getScale());
200202
}
201203
else if (which_type.isDateTime64() && which_from_type.isDate32())
202204
{
203-
const DataTypeDateTime64 & data_type_date_time64 = static_cast<const DataTypeDateTime64 &>(type);
204-
const Int64 value = data_type_date_time64.getTimeZone().fromDayNum(DayNum(src.get<Int32>()));
205-
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, data_type_date_time64.getScaleMultiplier());
205+
const auto & date_time64_type = static_cast<const DataTypeDateTime64 &>(type);
206+
const auto value = date_time64_type.getTimeZone().fromDayNum(ExtendedDayNum(static_cast<Int32>(src.get<Int32>())));
207+
return DecimalField(
208+
DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(value, 0, date_time64_type.getScaleMultiplier()),
209+
date_time64_type.getScale());
206210
}
207211
else if (type.isValueRepresentedByNumber() && src.getType() != Field::Types::String)
208212
{
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#include <initializer_list>
2+
#include <limits>
3+
#include <ostream>
4+
#include <Core/Field.h>
5+
#include <Core/iostream_debug_helpers.h>
6+
#include <Interpreters/convertFieldToType.h>
7+
#include <DataTypes/DataTypeFactory.h>
8+
9+
#include <gtest/gtest.h>
10+
#include "base/Decimal.h"
11+
#include "base/types.h"
12+
#include "gtest/gtest.h"
13+
14+
using namespace DB;
15+
16+
struct ConvertFieldToTypeTestParams
17+
{
18+
const char * from_type; // MUST NOT BE NULL
19+
const Field from_value;
20+
const char * to_type; // MUST NOT BE NULL
21+
const std::optional<Field> expected_value;
22+
};
23+
24+
std::ostream & operator << (std::ostream & ostr, const ConvertFieldToTypeTestParams & params)
25+
{
26+
return ostr << "{"
27+
<< "\n\tfrom_type : " << params.from_type
28+
<< "\n\tfrom_value : " << params.from_value
29+
<< "\n\tto_type : " << params.to_type
30+
<< "\n\texpected : " << (params.expected_value ? *params.expected_value : Field())
31+
<< "\n}";
32+
}
33+
34+
class ConvertFieldToTypeTest : public ::testing::TestWithParam<ConvertFieldToTypeTestParams>
35+
{};
36+
37+
TEST_P(ConvertFieldToTypeTest, convert)
38+
{
39+
const auto & params = GetParam();
40+
41+
ASSERT_NE(nullptr, params.from_type);
42+
ASSERT_NE(nullptr, params.to_type);
43+
44+
const auto & type_factory = DataTypeFactory::instance();
45+
const auto from_type = type_factory.get(params.from_type);
46+
const auto to_type = type_factory.get(params.to_type);
47+
48+
if (params.expected_value)
49+
{
50+
const auto result = convertFieldToType(params.from_value, *to_type, from_type.get());
51+
EXPECT_EQ(*params.expected_value, result);
52+
}
53+
else
54+
{
55+
EXPECT_ANY_THROW(convertFieldToType(params.from_value, *to_type, from_type.get()));
56+
}
57+
}
58+
59+
// Basically nuber of seconds in a day, works for UTC here
60+
const long long int Day = 24 * 60 * 60;
61+
62+
// 123 is arbitrary value here
63+
64+
INSTANTIATE_TEST_SUITE_P(
65+
DateToDateTime64,
66+
ConvertFieldToTypeTest,
67+
::testing::ValuesIn(std::initializer_list<ConvertFieldToTypeTestParams>{
68+
// min value of Date
69+
{
70+
"Date",
71+
Field(0),
72+
"DateTime64(0, 'UTC')",
73+
DecimalField(DateTime64(0), 0)
74+
},
75+
// Max value of Date
76+
{
77+
"Date",
78+
Field(std::numeric_limits<DB::UInt16>::max()),
79+
"DateTime64(0, 'UTC')",
80+
DecimalField(DateTime64(std::numeric_limits<DB::UInt16>::max() * Day), 0)
81+
},
82+
// check that scale is respected
83+
{
84+
"Date",
85+
Field(123),
86+
"DateTime64(0, 'UTC')",
87+
DecimalField(DateTime64(123 * Day), 0)
88+
},
89+
{
90+
"Date",
91+
Field(1),
92+
"DateTime64(1, 'UTC')",
93+
DecimalField(DateTime64(Day * 10), 1)
94+
},
95+
{
96+
"Date",
97+
Field(123),
98+
"DateTime64(3, 'UTC')",
99+
DecimalField(DateTime64(123 * Day * 1000), 3)
100+
},
101+
{
102+
"Date",
103+
Field(123),
104+
"DateTime64(6, 'UTC')",
105+
DecimalField(DateTime64(123 * Day * 1'000'000), 6)
106+
},
107+
})
108+
);
109+
110+
INSTANTIATE_TEST_SUITE_P(
111+
Date32ToDateTime64,
112+
ConvertFieldToTypeTest,
113+
::testing::ValuesIn(std::initializer_list<ConvertFieldToTypeTestParams>{
114+
// min value of Date32: 1st Jan 1900 (see DATE_LUT_MIN_YEAR)
115+
{
116+
"Date32",
117+
Field(-25'567),
118+
"DateTime64(0, 'UTC')",
119+
DecimalField(DateTime64(-25'567 * Day), 0)
120+
},
121+
// max value of Date32: 31 Dec 2299 (see DATE_LUT_MAX_YEAR)
122+
{
123+
"Date32",
124+
Field(120'529),
125+
"DateTime64(0, 'UTC')",
126+
DecimalField(DateTime64(120'529 * Day), 0)
127+
},
128+
// check that scale is respected
129+
{
130+
"Date32",
131+
Field(123),
132+
"DateTime64(0, 'UTC')",
133+
DecimalField(DateTime64(123 * Day), 0)
134+
},
135+
{
136+
"Date32",
137+
Field(123),
138+
"DateTime64(1, 'UTC')",
139+
DecimalField(DateTime64(123 * Day * 10), 1)
140+
},
141+
{
142+
"Date32",
143+
Field(123),
144+
"DateTime64(3, 'UTC')",
145+
DecimalField(DateTime64(123 * Day * 1000), 3)
146+
},
147+
{
148+
"Date32",
149+
Field(123),
150+
"DateTime64(6, 'UTC')",
151+
DecimalField(DateTime64(123 * Day * 1'000'000), 6)
152+
}
153+
})
154+
);
155+
156+
INSTANTIATE_TEST_SUITE_P(
157+
DateTimeToDateTime64,
158+
ConvertFieldToTypeTest,
159+
::testing::ValuesIn(std::initializer_list<ConvertFieldToTypeTestParams>{
160+
{
161+
"DateTime",
162+
Field(1),
163+
"DateTime64(0, 'UTC')",
164+
DecimalField(DateTime64(1), 0)
165+
},
166+
{
167+
"DateTime",
168+
Field(1),
169+
"DateTime64(1, 'UTC')",
170+
DecimalField(DateTime64(1'0), 1)
171+
},
172+
{
173+
"DateTime",
174+
Field(123),
175+
"DateTime64(3, 'UTC')",
176+
DecimalField(DateTime64(123'000), 3)
177+
},
178+
{
179+
"DateTime",
180+
Field(123),
181+
"DateTime64(6, 'UTC')",
182+
DecimalField(DateTime64(123'000'000), 6)
183+
},
184+
})
185+
);

0 commit comments

Comments
 (0)