aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBogdan Gusiev <agresso@gmail.com>2015-09-22 12:48:50 +0300
committerBogdan Gusiev <agresso@gmail.com>2015-09-23 13:29:08 +0300
commitd03f5196657bf466d7576cd6cbd4886db030723b (patch)
tree29b1c6b120ebe89878905c0b112892d5300f6585
parent8842ce239562d3fbc82198ac3c4618935134ff39 (diff)
downloadrails-d03f5196657bf466d7576cd6cbd4886db030723b.tar.gz
rails-d03f5196657bf466d7576cd6cbd4886db030723b.tar.bz2
rails-d03f5196657bf466d7576cd6cbd4886db030723b.zip
Fixed taking precision into count when assigning a value to timestamp attribute
Timestamp column can have less precision than ruby timestamp In result in how big a fraction of a second can be stored in the database. m = Model.create! m.created_at.usec == m.reload.created_at.usec # => false # due to different seconds precision in Time.now and database column If the precision is low enough, (mysql default is 0, so it is always low enough by default) the value changes when model is reloaded from the database. This patch fixes that issue ensuring that any timestamp assigned as an attribute is converted to column precision under the attribute.
-rw-r--r--activemodel/lib/active_model/type/date_time.rb8
-rw-r--r--activemodel/lib/active_model/type/helpers/time_value.rb13
-rw-r--r--activerecord/CHANGELOG.md20
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb18
-rw-r--r--activerecord/test/cases/type/date_time_test.rb13
-rw-r--r--activerecord/test/schema/schema.rb30
6 files changed, 67 insertions, 35 deletions
diff --git a/activemodel/lib/active_model/type/date_time.rb b/activemodel/lib/active_model/type/date_time.rb
index b068cfc672..2f2df4320f 100644
--- a/activemodel/lib/active_model/type/date_time.rb
+++ b/activemodel/lib/active_model/type/date_time.rb
@@ -12,11 +12,11 @@ module ActiveModel
private
- def cast_value(string)
- return string unless string.is_a?(::String)
- return if string.empty?
+ def cast_value(value)
+ return apply_seconds_precision(value) unless value.is_a?(::String)
+ return if value.empty?
- fast_string_to_time(string) || fallback_string_to_time(string)
+ fast_string_to_time(value) || fallback_string_to_time(value)
end
# '0.123456' -> 123456
diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb
index 0a0e58654b..63993c0d93 100644
--- a/activemodel/lib/active_model/type/helpers/time_value.rb
+++ b/activemodel/lib/active_model/type/helpers/time_value.rb
@@ -5,11 +5,7 @@ module ActiveModel
module Helpers
module TimeValue # :nodoc:
def serialize(value)
- if precision && value.respond_to?(:usec)
- number_of_insignificant_digits = 6 - precision
- round_power = 10 ** number_of_insignificant_digits
- value = value.change(usec: value.usec / round_power * round_power)
- end
+ value = apply_seconds_precision(value)
if value.acts_like?(:time)
zone_conversion_method = is_utc? ? :getutc : :getlocal
@@ -34,6 +30,13 @@ module ActiveModel
end
end
+ def apply_seconds_precision(value)
+ return value unless precision && value.respond_to?(:usec)
+ number_of_insignificant_digits = 6 - precision
+ round_power = 10 ** number_of_insignificant_digits
+ value.change(usec: value.usec / round_power * round_power)
+ end
+
def type_cast_for_schema(value)
"'#{value.to_s(:db)}'"
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8befdfcdba..1d937fa0b6 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,23 @@
+* Fixed taking precision into count when assigning a value to timestamp attribute
+
+ Timestamp column can have less precision than ruby timestamp
+ In result in how big a fraction of a second can be stored in the
+ database.
+
+
+ m = Model.create!
+ m.created_at.usec == m.reload.created_at.usec
+ # => false
+ # due to different precision in Time.now and database column
+
+ If the precision is low enough, (mysql default is 0, so it is always low
+ enough by default) the value changes when model is reloaded from the
+ database. This patch fixes that issue ensuring that any timestamp
+ assigned as an attribute is converted to column precision under the
+ attribute.
+
+ *Bogdan Gusiev*
+
* Ensure `select` quotes aliased attributes, even when using `from`.
Fixes #21488
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
index 2c04c46131..424769f765 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
@@ -4,18 +4,14 @@ module ActiveRecord
module OID # :nodoc:
class DateTime < Type::DateTime # :nodoc:
def cast_value(value)
- if value.is_a?(::String)
- case value
- when 'infinity' then ::Float::INFINITY
- when '-infinity' then -::Float::INFINITY
- when / BC$/
- astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
- super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
- else
- super
- end
+ case value
+ when 'infinity' then ::Float::INFINITY
+ when '-infinity' then -::Float::INFINITY
+ when / BC$/
+ astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
+ super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
else
- value
+ super
end
end
end
diff --git a/activerecord/test/cases/type/date_time_test.rb b/activerecord/test/cases/type/date_time_test.rb
new file mode 100644
index 0000000000..62d3405be1
--- /dev/null
+++ b/activerecord/test/cases/type/date_time_test.rb
@@ -0,0 +1,13 @@
+require "cases/helper"
+require "models/task"
+
+module ActiveRecord
+ module Type
+ class IntegerTest < ActiveRecord::TestCase
+ def test_datetime_seconds_precision_applied_to_timestamp
+ p = Task.create!(starting: ::Time.now)
+ assert_equal p.starting.usec, p.reload.starting.usec
+ end
+ end
+ end
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index d318f3a968..ae183ade81 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -251,10 +251,10 @@ ActiveRecord::Schema.define do
create_table :developers, force: true do |t|
t.string :name
t.integer :salary, default: 70000
- t.datetime :created_at
- t.datetime :updated_at
- t.datetime :created_on
- t.datetime :updated_on
+ t.datetime :created_at, precision: 6
+ t.datetime :updated_at, precision: 6
+ t.datetime :created_on, precision: 6
+ t.datetime :updated_on, precision: 6
end
create_table :developers_projects, force: true, id: false do |t|
@@ -353,7 +353,7 @@ ActiveRecord::Schema.define do
create_table :invoices, force: true do |t|
t.integer :balance
- t.datetime :updated_at
+ t.datetime :updated_at, precision: 6
end
create_table :iris, force: true do |t|
@@ -503,7 +503,7 @@ ActiveRecord::Schema.define do
create_table :owners, primary_key: :owner_id, force: true do |t|
t.string :name
- t.column :updated_at, :datetime
+ t.column :updated_at, :datetime, precision: 6
t.column :happy_at, :datetime
t.string :essay_id
end
@@ -521,10 +521,10 @@ ActiveRecord::Schema.define do
t.column :color, :string
t.column :parrot_sti_class, :string
t.column :killer_id, :integer
- t.column :created_at, :datetime
- t.column :created_on, :datetime
- t.column :updated_at, :datetime
- t.column :updated_on, :datetime
+ t.column :created_at, :datetime, precision: 0
+ t.column :created_on, :datetime, precision: 0
+ t.column :updated_at, :datetime, precision: 0
+ t.column :updated_on, :datetime, precision: 0
end
create_table :parrots_pirates, id: false, force: true do |t|
@@ -567,15 +567,15 @@ ActiveRecord::Schema.define do
create_table :pets, primary_key: :pet_id, force: true do |t|
t.string :name
t.integer :owner_id, :integer
- t.timestamps null: false
+ t.timestamps null: false, precision: 6
end
create_table :pirates, force: true do |t|
t.column :catchphrase, :string
t.column :parrot_id, :integer
t.integer :non_validated_parrot_id
- t.column :created_on, :datetime
- t.column :updated_on, :datetime
+ t.column :created_on, :datetime, precision: 6
+ t.column :updated_on, :datetime, precision: 6
end
create_table :posts, force: true do |t|
@@ -685,7 +685,7 @@ ActiveRecord::Schema.define do
create_table :ship_parts, force: true do |t|
t.string :name
t.integer :ship_id
- t.datetime :updated_at
+ t.datetime :updated_at, precision: 6
end
create_table :prisoners, force: true do |t|
@@ -778,7 +778,7 @@ ActiveRecord::Schema.define do
t.string :parent_title
t.string :type
t.string :group
- t.timestamps null: true
+ t.timestamps null: true, precision: 6
end
create_table :toys, primary_key: :toy_id, force: true do |t|