Permalink
Browse files

quoted_date converts time-like objects to ActiveRecord::Base.default_…

…timezone before serialization. This allows you to use Time.now in find conditions and have it correctly be serialized as the current time in UTC when default_timezone == :utc [#2946 state:resolved]
  • Loading branch information...
gbuesing committed Aug 4, 2009
1 parent 9b68877 commit 6f97ad07ded847f29159baf71050c63f04282170
View
@@ -1,5 +1,7 @@
*Edge*
+* quoted_date converts time-like objects to ActiveRecord::Base.default_timezone before serialization. This allows you to use Time.now in find conditions and have it correctly be serialized as the current time in UTC when default_timezone == :utc. #2946 [Geoff Buesing]
+
* SQLite: drop support for 'dbfile' option in favor of 'database.' #2363 [Paul Hinze, Jeremy Kemper]
* Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha]
@@ -60,7 +60,12 @@ def quoted_false
end
def quoted_date(value)
- value.to_s(:db)
+ if value.acts_like?(:time)
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
+ value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
+ else
+ value
+ end.to_s(:db)
end
def quoted_string_prefix
@@ -464,6 +464,60 @@ def test_preserving_time_objects
end
end
+ def test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :utc do
+ time = Time.local(2000)
+ topic = Topic.create('written_on' => time)
+ saved_time = Topic.find(topic.id).written_on
+ assert_equal time, saved_time
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
+ assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
+ end
+ end
+ end
+
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :utc do
+ Time.use_zone 'Central Time (US & Canada)' do
+ time = Time.zone.local(2000)
+ topic = Topic.create('written_on' => time)
+ saved_time = Topic.find(topic.id).written_on
+ assert_equal time, saved_time
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
+ assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
+ end
+ end
+ end
+ end
+
+ def test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local
+ with_env_tz 'America/New_York' do
+ time = Time.utc(2000)
+ topic = Topic.create('written_on' => time)
+ saved_time = Topic.find(topic.id).written_on
+ assert_equal time, saved_time
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
+ assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
+ end
+ end
+
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :local do
+ Time.use_zone 'Central Time (US & Canada)' do
+ time = Time.zone.local(2000)
+ topic = Topic.create('written_on' => time)
+ saved_time = Topic.find(topic.id).written_on
+ assert_equal time, saved_time
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
+ assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
+ end
+ end
+ end
+ end
+
def test_custom_mutator
topic = Topic.find(1)
# This mutator is protected in the class definition
@@ -2115,4 +2169,19 @@ def test_create_with_custom_timestamps
def test_dup
assert !Minimalistic.new.freeze.dup.frozen?
end
+
+ protected
+ def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ end
+
+ def with_active_record_default_timezone(zone)
+ old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
+ yield
+ ensure
+ ActiveRecord::Base.default_timezone = old_zone
+ end
end
@@ -423,6 +423,42 @@ def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
assert_equal customers(:david), found_customer
end
+ def test_condition_utc_time_interpolation_with_default_timezone_local
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :local do
+ topic = Topic.first
+ assert_equal topic, Topic.find(:first, :conditions => ['written_on = ?', topic.written_on.getutc])
+ end
+ end
+ end
+
+ def test_hash_condition_utc_time_interpolation_with_default_timezone_local
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :local do
+ topic = Topic.first
+ assert_equal topic, Topic.find(:first, :conditions => {:written_on => topic.written_on.getutc})
+ end
+ end
+ end
+
+ def test_condition_local_time_interpolation_with_default_timezone_utc
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :utc do
+ topic = Topic.first
+ assert_equal topic, Topic.find(:first, :conditions => ['written_on = ?', topic.written_on.getlocal])
+ end
+ end
+ end
+
+ def test_hash_condition_local_time_interpolation_with_default_timezone_utc
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :utc do
+ topic = Topic.first
+ assert_equal topic, Topic.find(:first, :conditions => {:written_on => topic.written_on.getlocal})
+ end
+ end
+ end
+
def test_bind_variables
assert_kind_of Firm, Company.find(:first, :conditions => ["name = ?", "37signals"])
assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!"])
@@ -1087,4 +1123,18 @@ def bind(statement, *vars)
ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
end
end
+
+ def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ end
+
+ def with_active_record_default_timezone(zone)
+ old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
+ yield
+ ensure
+ ActiveRecord::Base.default_timezone = old_zone
+ end
end

1 comment on commit 6f97ad0

@rsim

This comment has been minimized.

Show comment Hide comment
@rsim

rsim Aug 7, 2009

Contributor

quoted_date will not change DateTime values local time zone if ActiveRecord::Base.default_timezone == :local
because DateTime objects do not have getlocal method (just getutc method).

Either this implementation should be changed or maybe ActiveSupport could add getlocal method to DateTime class (so that it would behave similar to Time class in this aspect).

Because of this issue test_saves_both_date_and_time in date_time_test.rb is not failing but should be failing (because DateTime UTC value is stored in database and then compared with local time zone Time value).

Contributor

rsim commented on 6f97ad0 Aug 7, 2009

quoted_date will not change DateTime values local time zone if ActiveRecord::Base.default_timezone == :local
because DateTime objects do not have getlocal method (just getutc method).

Either this implementation should be changed or maybe ActiveSupport could add getlocal method to DateTime class (so that it would behave similar to Time class in this aspect).

Because of this issue test_saves_both_date_and_time in date_time_test.rb is not failing but should be failing (because DateTime UTC value is stored in database and then compared with local time zone Time value).

Please sign in to comment.