clickhouse-native 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99a3891e30d35144bd50c299fe5ee814e4a491860c7a7d066043982563960c39
4
- data.tar.gz: 3c0dbaff2bc6db96ff5e1a8c5dbab67d6b4233571834e127f5a115570ef70346
3
+ metadata.gz: 1754175716e40a4b42c958e5234273724e03875eb1e466d8dd8d9855d45f6b6e
4
+ data.tar.gz: 28c8d5d65fbf242476d8dc6ffd2f5a53b93f920b5087955757530e5e0fb2dd6e
5
5
  SHA512:
6
- metadata.gz: b16891dc09f48773e9bdb59fffa07a645c4ab90dbc6723f58c48369eb18d81995f7df584b097048b751eb7737717015aacc838fd3884bb7f17aca94220ed7d20
7
- data.tar.gz: 94db3b9126927989d8a875a21a8ca096c9bb5f37eb859546295a15c891f1832f50f31e7a35971f94e57a9b5e876d0040a626fc130113269a3658110fa10eb3e5
6
+ metadata.gz: ec993a65a632c6fe56f4f07e6c9fde1762bd85671039dbca10b31fb93278a42a00284d5b72d119bc7335b21ab0adb58ce8b7b1bb9ab61a8b4231df8f94f6bade
7
+ data.tar.gz: 6e0424481fe45ed1be20b3684090cad6cbdb6a488b563482ab58728d451f3760208170f813b0a95ab34922b884ee53795d574abf8212aec4a91871beed0c03ce
@@ -174,16 +174,24 @@ static VALUE value_at(const ColumnRef& col, size_t idx) {
174
174
  }
175
175
 
176
176
  case Type::Date: {
177
- auto t = col->As<ColumnDate>()->At(idx);
178
- return rb_time_new(t, 0);
177
+ // Return a Ruby Date so as_json/to_s gives "YYYY-MM-DD" instead of
178
+ // a full ISO8601 timestamp. CH Date is days since 1970-01-01 UTC.
179
+ std::time_t t = col->As<ColumnDate>()->At(idx);
180
+ long days = static_cast<long>(t / 86400);
181
+ VALUE rb_cDate = rb_const_get(rb_cObject, rb_intern("Date"));
182
+ return rb_funcall(rb_cDate, rb_intern("jd"), 1, LONG2NUM(days + 2440588));
179
183
  }
180
184
  case Type::Date32: {
181
- auto t = col->As<ColumnDate32>()->At(idx);
182
- return rb_time_new(t, 0);
185
+ std::time_t t = col->As<ColumnDate32>()->At(idx);
186
+ long days = static_cast<long>(t / 86400);
187
+ VALUE rb_cDate = rb_const_get(rb_cObject, rb_intern("Date"));
188
+ return rb_funcall(rb_cDate, rb_intern("jd"), 1, LONG2NUM(days + 2440588));
183
189
  }
184
190
  case Type::DateTime: {
185
191
  auto t = col->As<ColumnDateTime>()->At(idx);
186
- return rb_time_new(t, 0);
192
+ VALUE rb_t = rb_time_new(t, 0);
193
+ rb_funcall(rb_t, rb_intern("utc"), 0);
194
+ return rb_t;
187
195
  }
188
196
  case Type::DateTime64: {
189
197
  auto ct = col->As<ColumnDateTime64>();
@@ -194,7 +202,9 @@ static VALUE value_at(const ColumnRef& col, size_t idx) {
194
202
  int64_t frac = ticks % denom;
195
203
  int64_t usec = (prec <= 6) ? frac * pow10_i64(6 - prec)
196
204
  : frac / pow10_i64(prec - 6);
197
- return rb_time_new(secs, usec);
205
+ VALUE rb_t = rb_time_new(secs, usec);
206
+ rb_funcall(rb_t, rb_intern("utc"), 0);
207
+ return rb_t;
198
208
  }
199
209
 
200
210
  case Type::Array: {
@@ -340,6 +350,28 @@ static bool has_trailing_tz(const char* s, size_t len) {
340
350
  return false;
341
351
  }
342
352
 
353
+ // For Date columns: normalise the value to its UTC-midnight epoch so the
354
+ // calendar day is preserved regardless of the process's local timezone.
355
+ // Handles Date, DateTime, Time, String ("YYYY-MM-DD" etc.), Integer epoch.
356
+ static int64_t coerce_to_date_epoch(VALUE value) {
357
+ if (RB_INTEGER_TYPE_P(value)) return NUM2LL(value);
358
+ VALUE date_src = value;
359
+ if (RB_TYPE_P(value, T_STRING)) {
360
+ VALUE rb_cDate = rb_const_get(rb_cObject, rb_intern("Date"));
361
+ date_src = rb_funcall(rb_cDate, rb_intern("parse"), 1, value);
362
+ } else if (rb_respond_to(value, rb_intern("hour"))) {
363
+ // Time / DateTime: take the wall-clock date portion as UTC midnight.
364
+ // We intentionally ignore the TZ offset here — callers who need
365
+ // strict moment semantics should use DateTime64 instead.
366
+ date_src = value;
367
+ }
368
+ VALUE y = rb_funcall(date_src, rb_intern("year"), 0);
369
+ VALUE m = rb_funcall(date_src, rb_intern("month"), 0);
370
+ VALUE d = rb_funcall(date_src, rb_intern("day"), 0);
371
+ VALUE utc = rb_funcall(rb_cTime, rb_intern("utc"), 3, y, m, d);
372
+ return NUM2LL(rb_funcall(utc, rb_intern("to_i"), 0));
373
+ }
374
+
343
375
  // Accepts a Time, a String (parsed via Time.parse), or a numeric (epoch
344
376
  // seconds). Mirrors how the HTTP gem's JSONEachRow coerced date cells.
345
377
  //
@@ -429,16 +461,8 @@ static void append_value(const ColumnRef& col, VALUE value) {
429
461
  return;
430
462
  }
431
463
 
432
- case Type::Date: {
433
- VALUE t = coerce_to_time(value);
434
- col->As<ColumnDate>()->Append(static_cast<std::time_t>(NUM2LL(rb_funcall(t, rb_intern("to_i"), 0))));
435
- return;
436
- }
437
- case Type::Date32: {
438
- VALUE t = coerce_to_time(value);
439
- col->As<ColumnDate32>()->Append(static_cast<std::time_t>(NUM2LL(rb_funcall(t, rb_intern("to_i"), 0))));
440
- return;
441
- }
464
+ case Type::Date: col->As<ColumnDate>()->Append(static_cast<std::time_t>(coerce_to_date_epoch(value))); return;
465
+ case Type::Date32: col->As<ColumnDate32>()->Append(static_cast<std::time_t>(coerce_to_date_epoch(value))); return;
442
466
  case Type::DateTime: {
443
467
  VALUE t = coerce_to_time(value);
444
468
  col->As<ColumnDateTime>()->Append(static_cast<std::time_t>(NUM2LL(rb_funcall(t, rb_intern("to_i"), 0))));
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClickhouseNative
4
- VERSION = "0.1.3"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "time"
4
+ require "date"
4
5
  require "bigdecimal"
5
6
  require "clickhouse_native/version"
6
7
  require "clickhouse_native/errors"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clickhouse-native
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuri Smirnov