diff --git a/lib/db/postgres/native/types.rb b/lib/db/postgres/native/types.rb index 9d5d12d..f1d5b3a 100644 --- a/lib/db/postgres/native/types.rb +++ b/lib/db/postgres/native/types.rb @@ -99,12 +99,16 @@ def initialize(name = "TIMESTAMP") attr :name def parse(string) - if match = string.match(/(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)([\+\-].*)?/) - parts = match.captures - parts[6] ||= "UTC" - - return Time.new(*parts) + return nil unless match = string.match(/\A(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+(?:\.\d+)?)([-+].*)?\z/) + parts = match.captures + parts[5] = BigDecimal(parts[5]) + if parts[6].nil? + parts[6] = '+00:00' + elsif /^[-+]\d\d$/ === parts[6] + parts[6] += ':00' end + + Time.new(*parts) end end diff --git a/spec/db/postgres/connection_spec.rb b/spec/db/postgres/connection_spec.rb index a865579..782aa4c 100644 --- a/spec/db/postgres/connection_spec.rb +++ b/spec/db/postgres/connection_spec.rb @@ -66,6 +66,51 @@ ensure connection.close end + + it "can get timestamps with microseconds and tz" do + [{ + # PG produces: "2022-11-11 12:38:59.123456+00" + zone: 'UTC', + time: '2022-11-11 23:38:59.123456+11', + result: Time.new(2022, 11, 11, 23, 38, BigDecimal('59.123456'), '+11:00'), + }, { + # PG produces: "2022-11-11 12:38:59+00" + zone: 'UTC', + time: '2022-11-11 23:38:59+11', + result: Time.new(2022, 11, 11, 23, 38, BigDecimal('59'), '+11:00'), + }, { + # PG produces: "2022-11-11 23:38:59.123456+00" + zone: 'UTC', + time: '2022-11-11 23:38:59.123456', + result: Time.new(2022, 11, 11, 23, 38, BigDecimal('59.123456'), '+00:00'), + }, { + # PG produces: "2022-11-11 23:38:59+11" + zone: 'Australia/Sydney', + time: '2022-11-11 23:38:59', + result: Time.new(2022, 11, 11, 23, 38, BigDecimal('59'), '+11:00'), + }, { + # PG produces: "2022-11-12 06:08:59.123456+11" + zone: 'Australia/Sydney', + time: '2022-11-11 23:38:59.123456+04:30', + result: Time.new(2022, 11, 11, 23, 38, BigDecimal('59.123456'), '+04:30'), + }, { + # PG produces: "2000-01-01 05:30:00+05:30" + zone: 'Asia/Kolkata', + time: '2000-01-01 00:00:00+00', + result: Time.new(2000, 1, 1, 5, 30, 0, '+05:30'), + }].each do |spec| + + connection.send_query("SET TIME ZONE '#{spec[:zone]}'"); + connection.send_query("SELECT '#{spec[:time]}'::TIMESTAMPTZ AS TS") + + result = connection.next_result + row = result.to_a.first + + expect(row.first).to be == spec[:result] + end + ensure + connection.close + end describe '#append_string' do it "should escape string" do