Skip to content

Commit 067b04d

Browse files
authored
Merge pull request #70 from Enmk/DateTime_and_DateTime64_timezone
Timezone support for DateTime (artpaul#120)
2 parents 59b2b36 + e36b78e commit 067b04d

File tree

14 files changed

+259
-55
lines changed

14 files changed

+259
-55
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ C++ client for [Yandex ClickHouse](https://clickhouse.yandex/)
88
* Array(T)
99
* Date
1010
* DateTime, DateTime64
11+
* DateTime([timezone]), DateTime64(N, [timezone])
1112
* Decimal32, Decimal64, Decimal128
1213
* Enum8, Enum16
1314
* FixedString(N)

clickhouse/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#define DBMS_MIN_REVISION_WITH_SERVER_DISPLAY_NAME 54372
3737
#define DBMS_MIN_REVISION_WITH_VERSION_PATCH 54401
3838
#define DBMS_MIN_REVISION_WITH_LOW_CARDINALITY_TYPE 54405
39+
#define DBMS_MIN_REVISION_WITH_TIME_ZONE_PARAMETER_IN_DATETIME_DATA_TYPE 54337
3940

4041
namespace clickhouse {
4142

clickhouse/columns/date.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ ColumnDateTime::ColumnDateTime()
6565
{
6666
}
6767

68+
ColumnDateTime::ColumnDateTime(std::string timezone)
69+
: Column(Type::CreateDateTime(std::move(timezone)))
70+
, data_(std::make_shared<ColumnUInt32>())
71+
{
72+
}
73+
6874
void ColumnDateTime::Append(const std::time_t& value) {
6975
data_->Append(static_cast<uint32_t>(value));
7076
}
@@ -73,6 +79,10 @@ std::time_t ColumnDateTime::At(size_t n) const {
7379
return data_->At(n);
7480
}
7581

82+
std::string ColumnDateTime::Timezone() const {
83+
return type_->As<DateTimeType>()->Timezone();
84+
}
85+
7686
void ColumnDateTime::Append(ColumnRef column) {
7787
if (auto col = column->As<ColumnDateTime>()) {
7888
data_->Append(col->data_);
@@ -117,6 +127,10 @@ ColumnDateTime64::ColumnDateTime64(size_t precision)
117127
: ColumnDateTime64(Type::CreateDateTime64(precision), std::make_shared<ColumnDecimal>(18ul, precision))
118128
{}
119129

130+
ColumnDateTime64::ColumnDateTime64(size_t precision, std::string timezone)
131+
: ColumnDateTime64(Type::CreateDateTime64(precision, std::move(timezone)), std::make_shared<ColumnDecimal>(18ul, precision))
132+
{}
133+
120134
ColumnDateTime64::ColumnDateTime64(TypeRef type, std::shared_ptr<ColumnDecimal> data)
121135
: Column(type),
122136
data_(data),
@@ -137,6 +151,10 @@ Int64 ColumnDateTime64::At(size_t n) const {
137151
return data_->At(n);
138152
}
139153

154+
std::string ColumnDateTime64::Timezone() const {
155+
return type_->As<DateTime64Type>()->Timezone();
156+
}
157+
140158
void ColumnDateTime64::Append(ColumnRef column) {
141159
if (auto col = column->As<ColumnDateTime64>()) {
142160
data_->Append(col->data_);

clickhouse/columns/date.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class ColumnDate : public Column {
3131

3232
/// Clear column data .
3333
void Clear() override;
34-
34+
3535
/// Returns count of rows in the column.
3636
size_t Size() const override;
3737

@@ -50,13 +50,18 @@ class ColumnDate : public Column {
5050
class ColumnDateTime : public Column {
5151
public:
5252
ColumnDateTime();
53+
explicit ColumnDateTime(std::string timezone);
5354

5455
/// Appends one element to the end of column.
5556
void Append(const std::time_t& value);
5657

5758
/// Returns element at given row number.
5859
std::time_t At(size_t n) const;
5960

61+
/// Timezone associated with a data column.
62+
std::string Timezone() const;
63+
64+
public:
6065
/// Appends content of given column to the end of current one.
6166
void Append(ColumnRef column) override;
6267

@@ -87,7 +92,8 @@ class ColumnDateTime : public Column {
8792
/** */
8893
class ColumnDateTime64 : public Column {
8994
public:
90-
explicit ColumnDateTime64(size_t);
95+
explicit ColumnDateTime64(size_t precision);
96+
ColumnDateTime64(size_t precision, std::string timezone);
9197

9298
/// Appends one element to the end of column.
9399
void Append(const Int64& value);
@@ -98,6 +104,9 @@ class ColumnDateTime64 : public Column {
98104
/// Returns element at given row number.
99105
Int64 At(size_t n) const;
100106

107+
/// Timezone associated with a data column.
108+
std::string Timezone() const;
109+
101110
public:
102111
/// Appends content of given column to the end of current one.
103112
void Append(ColumnRef column) override;

clickhouse/columns/factory.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,20 @@ static ColumnRef CreateTerminalColumn(const TypeAst& ast) {
6464
return std::make_shared<ColumnFixedString>(ast.elements.front().value);
6565

6666
case Type::DateTime:
67-
return std::make_shared<ColumnDateTime>();
67+
if (ast.elements.empty()) {
68+
return std::make_shared<ColumnDateTime>();
69+
} else {
70+
return std::make_shared<ColumnDateTime>(ast.elements[0].value_string);
71+
}
6872
case Type::DateTime64:
69-
return std::make_shared<ColumnDateTime64>(ast.elements.front().value);
73+
if (ast.elements.empty()) {
74+
return nullptr;
75+
}
76+
if (ast.elements.size() == 1) {
77+
return std::make_shared<ColumnDateTime64>(ast.elements[0].value);
78+
} else {
79+
return std::make_shared<ColumnDateTime64>(ast.elements[0].value, ast.elements[1].value_string);
80+
}
7081
case Type::Date:
7182
return std::make_shared<ColumnDate>();
7283

@@ -120,10 +131,11 @@ static ColumnRef CreateColumnFromAst(const TypeAst& ast) {
120131
case TypeAst::Enum: {
121132
std::vector<Type::EnumItem> enum_items;
122133

123-
enum_items.reserve(ast.elements.size());
124-
for (const auto& elem : ast.elements) {
134+
enum_items.reserve(ast.elements.size() / 2);
135+
for (size_t i = 0; i < ast.elements.size(); i += 2) {
125136
enum_items.push_back(
126-
Type::EnumItem{elem.name, (int16_t)elem.value});
137+
Type::EnumItem{ast.elements[i].value_string,
138+
(int16_t)ast.elements[i + 1].value});
127139
}
128140

129141
if (ast.code == Type::Enum8) {
@@ -153,8 +165,10 @@ static ColumnRef CreateColumnFromAst(const TypeAst& ast) {
153165
return CreateTerminalColumn(ast.elements.back());
154166
}
155167

168+
case TypeAst::Assign:
156169
case TypeAst::Null:
157170
case TypeAst::Number:
171+
case TypeAst::String:
158172
break;
159173
}
160174

clickhouse/types/type_parser.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ bool TypeParser::Parse(TypeAst* type) {
108108
{
109109
type_->meta = TypeAst::Terminal;
110110
if (token.value.length() < 1)
111-
type_->name = {};
111+
type_->value_string = {};
112112
else
113-
type_->name = token.value.substr(1, token.value.length() - 2).to_string();
113+
type_->value_string = token.value.substr(1, token.value.length() - 2).to_string();
114114
type_->code = Type::String;
115115
break;
116116
}
@@ -123,6 +123,10 @@ bool TypeParser::Parse(TypeAst* type) {
123123
type_->meta = TypeAst::Number;
124124
type_->value = std::stol(token.value.to_string());
125125
break;
126+
case Token::String:
127+
type_->meta = TypeAst::String;
128+
type_->value_string = std::string(token.value);
129+
break;
126130
case Token::LPar:
127131
type_->elements.emplace_back(TypeAst());
128132
open_elements_.push(type_);
@@ -132,6 +136,7 @@ bool TypeParser::Parse(TypeAst* type) {
132136
type_ = open_elements_.top();
133137
open_elements_.pop();
134138
break;
139+
case Token::Assign:
135140
case Token::Comma:
136141
type_ = open_elements_.top();
137142
open_elements_.pop();
@@ -160,10 +165,8 @@ TypeParser::Token TypeParser::NextToken() {
160165
case '\t':
161166
case '\0':
162167
continue;
163-
164168
case '=':
165-
continue;
166-
169+
return Token{Token::Assign, StringView(cur_++, 1)};
167170
case '(':
168171
return Token{Token::LPar, StringView(cur_++, 1)};
169172
case ')':
@@ -190,6 +193,16 @@ TypeParser::Token TypeParser::NextToken() {
190193
default: {
191194
const char* st = cur_;
192195

196+
if (*cur_ == '\'') {
197+
for (st = ++cur_; cur_ < end_; ++cur_) {
198+
if (*cur_ == '\'') {
199+
return Token{Token::String, StringView(st, cur_++ - st)};
200+
}
201+
}
202+
203+
return Token{Token::Invalid, StringView()};
204+
}
205+
193206
if (isalpha(*cur_) || *cur_ == '_') {
194207
for (; cur_ < end_; ++cur_) {
195208
if (!isalpha(*cur_) && !isdigit(*cur_) && *cur_ != '_') {

clickhouse/types/type_parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ namespace clickhouse {
1212
struct TypeAst {
1313
enum Meta {
1414
Array,
15+
Assign,
1516
Null,
1617
Nullable,
1718
Number,
19+
String,
1820
Terminal,
1921
Tuple,
2022
Enum,
@@ -31,6 +33,7 @@ struct TypeAst {
3133
/// Value associated with the node,
3234
/// used for fixed-width types and enum values.
3335
int64_t value = 0;
36+
std::string value_string;
3437
/// Subelements of the type.
3538
/// Used to store enum's names and values as well.
3639
std::vector<TypeAst> elements;
@@ -47,8 +50,10 @@ class TypeParser {
4750
struct Token {
4851
enum Type {
4952
Invalid = 0,
53+
Assign,
5054
Name,
5155
Number,
56+
String,
5257
LPar,
5358
RPar,
5459
Comma,

clickhouse/types/types.cpp

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
namespace clickhouse {
66

7-
Type::Type(const Code code) : code_(code) {
8-
}
7+
Type::Type(const Code code) : code_(code) {}
98

109
std::string Type::GetName() const {
1110
switch (code_) {
@@ -44,7 +43,9 @@ std::string Type::GetName() const {
4443
case IPv6:
4544
return "IPv6";
4645
case DateTime:
47-
return "DateTime";
46+
{
47+
return As<DateTimeType>()->GetName();
48+
}
4849
case DateTime64:
4950
return As<DateTime64Type>()->GetName();
5051
case Date:
@@ -79,12 +80,12 @@ TypeRef Type::CreateDate() {
7980
return TypeRef(new Type(Type::Date));
8081
}
8182

82-
TypeRef Type::CreateDateTime() {
83-
return TypeRef(new Type(Type::DateTime));
83+
TypeRef Type::CreateDateTime(std::string timezone) {
84+
return TypeRef(new DateTimeType(std::move(timezone)));
8485
}
8586

86-
TypeRef Type::CreateDateTime64(size_t precision) {
87-
return TypeRef(new DateTime64Type(precision));
87+
TypeRef Type::CreateDateTime64(size_t precision, std::string timezone) {
88+
return TypeRef(new DateTime64Type(precision, std::move(timezone)));
8889
}
8990

9091
TypeRef Type::CreateDecimal(size_t precision, size_t scale) {
@@ -183,7 +184,7 @@ std::string EnumType::GetName() const {
183184
result = "Enum16(";
184185
}
185186

186-
for (auto ei = value_to_name_.begin();;) {
187+
for (auto ei = value_to_name_.begin(); ei != value_to_name_.end();) {
187188
result += "'";
188189
result += ei->second;
189190
result += "' = ";
@@ -225,10 +226,36 @@ EnumType::ValueToNameIterator EnumType::EndValueToName() const {
225226
return value_to_name_.end();
226227
}
227228

229+
230+
namespace details
231+
{
232+
TypeWithTimeZoneMixin::TypeWithTimeZoneMixin(std::string timezone)
233+
: timezone_(std::move(timezone)) {
234+
}
235+
236+
const std::string & TypeWithTimeZoneMixin::Timezone() const {
237+
return timezone_;
238+
}
239+
}
240+
241+
/// class DateTimeType
242+
DateTimeType::DateTimeType(std::string timezone)
243+
: Type(DateTime), details::TypeWithTimeZoneMixin(std::move(timezone)) {
244+
}
245+
246+
std::string DateTimeType::GetName() const {
247+
std::string datetime_representation = "DateTime";
248+
const auto & timezone = Timezone();
249+
if (!timezone.empty())
250+
datetime_representation += "('" + timezone + "')";
251+
252+
return datetime_representation;
253+
}
254+
228255
/// class DateTime64Type
229256

230-
DateTime64Type::DateTime64Type(size_t precision)
231-
: Type(DateTime64), precision_(precision) {
257+
DateTime64Type::DateTime64Type(size_t precision, std::string timezone)
258+
: Type(DateTime64), details::TypeWithTimeZoneMixin(std::move(timezone)), precision_(precision) {
232259

233260
if (precision_ > 18) {
234261
throw std::runtime_error("DateTime64 precision is > 18");
@@ -240,6 +267,12 @@ std::string DateTime64Type::GetName() const {
240267
datetime64_representation.reserve(14);
241268
datetime64_representation += "DateTime64(";
242269
datetime64_representation += std::to_string(precision_);
270+
271+
const auto & timezone = Timezone();
272+
if (!timezone.empty()) {
273+
datetime64_representation += ", '" + timezone + "'";
274+
}
275+
243276
datetime64_representation += ")";
244277
return datetime64_representation;
245278
}
@@ -262,8 +295,9 @@ TupleType::TupleType(const std::vector<TypeRef>& item_types) : Type(Tuple), item
262295
/// class LowCardinalityType
263296
LowCardinalityType::LowCardinalityType(TypeRef nested_type) : Type(LowCardinality), nested_type_(nested_type) {
264297
}
265-
LowCardinalityType::~LowCardinalityType()
266-
{}
298+
299+
LowCardinalityType::~LowCardinalityType() {
300+
}
267301

268302
std::string TupleType::GetName() const {
269303
std::string result("Tuple(");

0 commit comments

Comments
 (0)