From 51364e251232ecd4d44160c78cb9747ab539028a Mon Sep 17 00:00:00 2001 From: Kaia Leahy Date: Sat, 21 Nov 2020 18:40:46 -0500 Subject: [PATCH] Fix timestamp reading issue Also adds a couple of basic tests for ResultSet Should fix #63 --- README.md | 2 +- spec/result_set_spec.cr | 57 +++++++++++++++++++++++++++++++++++++++ src/sqlite3.cr | 3 ++- src/sqlite3/result_set.cr | 15 +++++++++-- src/sqlite3/statement.cr | 2 +- 5 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 spec/result_set_spec.cr diff --git a/README.md b/README.md index af14447..eeef632 100644 --- a/README.md +++ b/README.md @@ -46,5 +46,5 @@ end ### DB::Any -* `Time` is implemented as `TEXT` column using `SQLite3::DATE_FORMAT` format. +* `Time` is implemented as `TEXT` column using `SQLite3::DATE_FORMAT_SUBSECOND` format (or `SQLite3::DATE_FORMAT_SECOND` if the text does not contain a dot). * `Bool` is implemented as `INT` column mapping `0`/`1` values. diff --git a/spec/result_set_spec.cr b/spec/result_set_spec.cr new file mode 100644 index 0000000..8fec471 --- /dev/null +++ b/spec/result_set_spec.cr @@ -0,0 +1,57 @@ +require "./spec_helper" + +describe SQLite3::ResultSet do + it "reads integer data types" do + with_db do |db| + db.exec "CREATE TABLE test_table(test_int integer)" + db.exec "INSERT INTO test_table(test_int) values(?)", 42 + db.query("SELECT test_int FROM test_table") do |rs| + rs.each do + rs.read.should eq(42) + end + end + end + end + + it "reads string data types" do + with_db do |db| + db.exec "CREATE TABLE test_table(test_text text)" + db.exec "INSERT INTO test_table(test_text) VALUES (?), (?)", "abc", "123" + db.query("SELECT test_text FROM test_table") do |rs| + rs.each do + rs.read.should match(/abc|123/) + end + end + end + end + + it "reads time data types" do + with_db do |db| + db.exec "CREATE TABLE test_table(test_date datetime)" + timestamp = Time.utc + db.exec "INSERT INTO test_table(test_date) values(current_timestamp)" + db.query("SELECT test_date FROM test_table") do |rs| + rs.each do + rs.read(Time).should be_close(timestamp, 1.second) + end + end + end + end + + it "reads time stored in text fields, too" do + with_db do |db| + db.exec "CREATE TABLE test_table(test_date text)" + timestamp = Time.utc + # Try 3 different ways: our own two formats and using SQLite's current_timestamp. + # They should all work. + db.exec "INSERT INTO test_table(test_date) values(?)", timestamp.to_s SQLite3::DATE_FORMAT_SUBSECOND + db.exec "INSERT INTO test_table(test_date) values(?)", timestamp.to_s SQLite3::DATE_FORMAT_SECOND + db.exec "INSERT INTO test_table(test_date) values(current_timestamp)" + db.query("SELECT test_date FROM test_table") do |rs| + rs.each do + rs.read(Time).should be_close(timestamp, 1.second) + end + end + end + end +end diff --git a/src/sqlite3.cr b/src/sqlite3.cr index d1c973b..4ba5365 100644 --- a/src/sqlite3.cr +++ b/src/sqlite3.cr @@ -2,7 +2,8 @@ require "db" require "./sqlite3/**" module SQLite3 - DATE_FORMAT = "%F %H:%M:%S.%L" + DATE_FORMAT_SUBSECOND = "%F %H:%M:%S.%L" + DATE_FORMAT_SECOND = "%F %H:%M:%S" # :nodoc: TIME_ZONE = Time::Location::UTC diff --git a/src/sqlite3/result_set.cr b/src/sqlite3/result_set.cr index 38e669f..48416b6 100644 --- a/src/sqlite3/result_set.cr +++ b/src/sqlite3/result_set.cr @@ -64,11 +64,22 @@ class SQLite3::ResultSet < DB::ResultSet end def read(t : Time.class) : Time - Time.parse read(String), SQLite3::DATE_FORMAT, location: SQLite3::TIME_ZONE + text = read(String) + if text.includes? "." + Time.parse text, SQLite3::DATE_FORMAT_SUBSECOND, location: SQLite3::TIME_ZONE + else + Time.parse text, SQLite3::DATE_FORMAT_SECOND, location: SQLite3::TIME_ZONE + end end def read(t : Time?.class) : Time? - read(String?).try { |v| Time.parse(v, SQLite3::DATE_FORMAT, location: SQLite3::TIME_ZONE) } + read(String?).try { |v| + if v.includes? "." + Time.parse v, SQLite3::DATE_FORMAT_SUBSECOND, location: SQLite3::TIME_ZONE + else + Time.parse v, SQLite3::DATE_FORMAT_SECOND, location: SQLite3::TIME_ZONE + end + } end def read(t : Bool.class) : Bool diff --git a/src/sqlite3/statement.cr b/src/sqlite3/statement.cr index 06289fb..368dfe3 100644 --- a/src/sqlite3/statement.cr +++ b/src/sqlite3/statement.cr @@ -70,7 +70,7 @@ class SQLite3::Statement < DB::Statement end private def bind_arg(index, value : Time) - bind_arg(index, value.in(SQLite3::TIME_ZONE).to_s(SQLite3::DATE_FORMAT)) + bind_arg(index, value.in(SQLite3::TIME_ZONE).to_s(SQLite3::DATE_FORMAT_SUBSECOND)) end private def bind_arg(index, value)