aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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|