diff options
-rw-r--r-- | activemodel/lib/active_model/type/date_time.rb | 8 | ||||
-rw-r--r-- | activemodel/lib/active_model/type/helpers/time_value.rb | 13 | ||||
-rw-r--r-- | activerecord/CHANGELOG.md | 20 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb | 18 | ||||
-rw-r--r-- | activerecord/test/cases/type/date_time_test.rb | 13 | ||||
-rw-r--r-- | activerecord/test/schema/schema.rb | 30 |
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| |