1#pragma GCC diagnostic ignored "-Wmissing-declarations"
2#include <gtest/gtest.h>
3
4#include <IO/ReadHelpers.h>
5#include <IO/WriteHelpers.h>
6#include <IO/parseDateTimeBestEffort.h>
7
8#include <Common/PODArray.h>
9
10/** Test formatting and parsing predefined DateTime64 values to/from string
11 */
12
13using namespace DB;
14
15struct DateTime64StringsTestParam
16{
17 const std::string_view comment;
18 const std::string_view string;
19 DateTime64 dt64;
20 UInt32 scale;
21 const DateLUTImpl & timezone = DateLUT::instance();
22};
23
24static std::ostream & operator << (std::ostream & ostr, const DateTime64StringsTestParam & param)
25{
26 return ostr << param.comment;
27}
28
29class DateTime64StringsTest : public ::testing::TestWithParam<DateTime64StringsTestParam> {};
30class DateTime64StringParseTest : public DateTime64StringsTest{};
31class DateTime64StringParseBestEffortTest : public DateTime64StringsTest{};
32class DateTime64StringWriteTest : public DateTime64StringsTest {};
33
34
35TEST_P(DateTime64StringParseTest, readDateTime64Text)
36{
37 const auto & param = GetParam();
38 ReadBufferFromMemory read_buffer(param.string.data(), param.string.size());
39
40 DateTime64 actual;
41 EXPECT_TRUE(tryReadDateTime64Text(actual, param.scale, read_buffer));
42
43 EXPECT_EQ(param.dt64, actual);
44}
45
46TEST_P(DateTime64StringParseTest, parseDateTime64BestEffort)
47{
48 const auto & param = GetParam();
49 ReadBufferFromMemory read_buffer(param.string.data(), param.string.size());
50
51 DateTime64 actual;
52 EXPECT_TRUE(tryParseDateTime64BestEffort(actual, param.scale, read_buffer, param.timezone, DateLUT::instance("UTC")));
53
54 EXPECT_EQ(param.dt64, actual);
55}
56
57TEST_P(DateTime64StringWriteTest, WriteText)
58{
59 const auto & param = GetParam();
60
61 PaddedPODArray<char> actual_string(param.string.size() * 2, '\0'); // TODO: detect overflows
62
63 WriteBuffer write_buffer(actual_string.data(), actual_string.size());
64 EXPECT_NO_THROW(writeDateTimeText(param.dt64, param.scale, write_buffer));
65
66 EXPECT_STREQ(param.string.data(), actual_string.data());
67}
68
69TEST_P(DateTime64StringParseBestEffortTest, parse)
70{
71 const auto & param = GetParam();
72 ReadBufferFromMemory read_buffer(param.string.data(), param.string.size());
73
74 DateTime64 actual;
75 EXPECT_TRUE(tryParseDateTime64BestEffort(actual, param.scale, read_buffer, param.timezone, DateLUT::instance("UTC")));
76
77 EXPECT_EQ(param.dt64, actual);
78}
79
80
81// YYYY-MM-DD HH:MM:SS.NNNNNNNNN
82INSTANTIATE_TEST_CASE_P(Basic,
83 DateTime64StringParseTest,
84 ::testing::ValuesIn(std::initializer_list<DateTime64StringsTestParam>{
85 {
86 "When subsecond part is missing from string it is set to zero.",
87 "2019-09-16 19:20:17",
88 1568650817'000,
89 3
90 },
91 {
92 "When subsecond part is present in string, but it is zero, it is set to zero.",
93 "2019-09-16 19:20:17.0",
94 1568650817'000,
95 3
96 },
97 {
98 "When scale is 0, subsecond part is not set.",
99 "2019-09-16 19:20:17",
100 1568650817ULL,
101 0
102 },
103 {
104 "When scale is 0, subsecond part is 0 despite beeing present in string.",
105 "2019-09-16 19:20:17.123",
106 1568650817ULL,
107 0
108 },
109 {
110 "When subsecond part is present in string, it is set correctly to DateTime64 value of scale 3.",
111 "2019-09-16 19:20:17.123",
112 1568650817'123,
113 3
114 },
115 {
116 "When subsecond part is present in string (and begins with 0), it is set correctly to DateTime64 value of scale 3.",
117 "2019-09-16 19:20:17.012",
118 1568650817'012,
119 3
120 },
121 {
122 "When subsecond part scale is smaller than DateTime64 scale, subsecond part is properly adjusted (as if padded from right with zeroes).",
123 "2019-09-16 19:20:17.123",
124 1568650817'12300ULL,
125 5
126 },
127 {
128 "When subsecond part scale is larger than DateTime64 scale, subsecond part is truncated.",
129 "2019-09-16 19:20:17.123",
130 1568650817'1ULL,
131 1
132 }
133 }),
134);
135
136INSTANTIATE_TEST_CASE_P(BestEffort,
137 DateTime64StringParseBestEffortTest,
138 ::testing::ValuesIn(std::initializer_list<DateTime64StringsTestParam>{
139 {
140 "When subsecond part is unreasonably large, it fals to parse",
141 "2019-09-16 19:20:17.12345678910111213141516171819202122233435363738393031323334353637383940414243444546474849505152535455565758596061626364",
142 1568650817'123456ULL,
143 6
144 }
145 }),
146);
147
148
149// TODO: add negative test cases for invalid strings, verifying that error is reported properly
150
151INSTANTIATE_TEST_CASE_P(Basic,
152 DateTime64StringWriteTest,
153 ::testing::ValuesIn(std::initializer_list<DateTime64StringsTestParam>{
154 {
155 "non-zero subsecond part on DateTime64 with scale of 3",
156 "2019-09-16 19:20:17.123",
157 1568650817'123,
158 3
159 },
160 {
161 "non-zero subsecond part on DateTime64 with scale of 5",
162 "2019-09-16 19:20:17.12345",
163 1568650817'12345ULL,
164 5
165 },
166 {
167 "Zero subsecond part is written to string",
168 "2019-09-16 19:20:17.000",
169 1568650817'000ULL,
170 3
171 },
172 {
173 "When scale is 0, subsecond part (and separtor) is missing from string",
174 "2019-09-16 19:20:17",
175 1568650817ULL,
176 0
177 },
178 {
179 "Subsecond part with leading zeroes is written to string correctly",
180 "2019-09-16 19:20:17.001",
181 1568650817'001ULL,
182 3
183 }
184 }),
185);
186
187