Description
Steps to reproduce
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails"
gem "sqlite3"
end
require "active_record/railtie"
require "minitest/autorun"
# This connection will do for database-independent bug reports.
ENV["DATABASE_URL"] = "sqlite3::memory:"
class TestApp < Rails::Application
config.load_defaults Rails::VERSION::STRING.to_f
config.eager_load = false
config.logger = Logger.new($stdout)
config.secret_key_base = "secret_key_base"
config.active_record.encryption.primary_key = "primary_key"
config.active_record.encryption.deterministic_key = "deterministic_key"
config.active_record.encryption.key_derivation_salt = "key_derivation_salt"
config.time_zone = "Asia/Tokyo"
config.active_record.default_timezone = :utc
end
Rails.application.initialize!
ActiveRecord::Schema.define do
create_table :users, force: true do |t|
t.time :created_at
end
end
class User < ActiveRecord::Base
end
class BugTest < ActiveSupport::TestCase
def test_time_attribute_changed
user = User.create! created_at: "08:00"
user.reload
user.created_at
# => 2000-01-02 08:00:00.000000000 JST +09:00
user.created_at = "08:00"
user.created_at
# => 2000-01-01 08:00:00.000000000 JST +09:00
assert_equal [], user.changed_attribute_names_to_save
# => ["created_at"] ← unexpected!
end
end
Although the same "08:00"
value is assigned again, Active Record considers the attribute as changed.
Expected behavior
The created_at
attribute should not be marked as changed when assigning the same time string ("08:00"
) again.
This should hold true even when the application and database time zones differ, and internal Time
objects have different date parts.
Actual behavior
Assigning "08:00"
again results in :created_at
being marked as changed.
This happens because:
user.created_at.to_s
# => "2000-01-01 08:00:00 +0900"
user.created_at.utc.to_s
# => "1999-12-31 23:00:00 UTC"
Due to the timezone conversion, the internal Time
object has a different date, triggering false positives in dirty tracking.
Proposal
I propose introducing a config option such as:
config.active_record.use_fixed_date_for_time_attributes = true
When enabled, Active Record would normalize time
-typed values to a fixed date (e.g., 2000-01-01
) internally.
This would eliminate unnecessary dirty tracking caused by timezone-induced date mismatches, while preserving timezone support and default behavior unless explicitly enabled.
Would the Rails team be open to such a feature?
System configuration
Rails version: 7.x.x
Ruby version: 3.x.x