Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Dirty datetime attributes lose time zone info

#3658

When modifying a datetime attribute of an ActiveRecord object, its history is saved but without time zone information. Example:

>> q.start_at
=> Tue, 24 Nov 2009 11:21:08 CET +01:00
>> q.start_at_was
=> Tue, 24 Nov 2009 11:21:08 CET +01:00
>> q.start_at = Time.zone.now
=> Tue, 05 Jan 2010 16:09:52 CET +01:00
>> q.start_at
=> Tue, 05 Jan 2010 16:09:52 CET +01:00
>> q.start_at_was
=> Tue Nov 24 10:21:08 UTC 2009

I would expect q.start_at_was to return the old CET value, also after the change.

q is an instance of a normal ActiveRecord object, start_at is a datetime column (served from a PostgreSQL 8.3 server with all timezones turned off). Using Rails 2.3.4, Ruby 1.8.6, on Mac OS X.

Reported by Jens · January 5th, 2010 @ 03:15 PM

State: resolved
Milestone: 2.3.6
Assigned to: Jeremy Kemper Jeremy Kemper
Importance: Medium

Activity

  1. Matthew Bass
    Matthew Bass

    +1, this this has bitten me as well

    February 22nd, 2010 @ 03:01 AM

  2. Darnell Brawner
    Darnell Brawner

    I am having the same issue using dirty attributes to create a change log.

    March 5th, 2010 @ 04:06 PM

  3. Jeremy Kemper
    Jeremy Kemper
    • State changed from new to open
    • Assigned user set to Jeremy Kemper
    • Milestone set to 2.3.6

    March 5th, 2010 @ 04:34 PM

  4. Kristopher Murata
    Kristopher Murata

    This is happening because when storing the old attributes on @changed_attributes hash the datetime attributes were being transformed from ActiveSupport::TimeWithZone to just Time:

    irb(main):007:0> p.created_at.class
    => ActiveSupport::TimeWithZone
    irb(main):009:0> p.created_at = Time.now
    => Thu Apr 08 00:59:46 -0400 2010
    irb(main):011:0> p.created_at.class
    => ActiveSupport::TimeWithZone
    irb(main):012:0> p.created_at_was.class
    => Time
    

    The funny part is that is that ActiveSupport::TimeWithZone objects are created on-the-fly by the module ActiveRecord::AttributeMethods::TimeZoneConversion which overwrite read/write methods for datetime/timestamp columns when ActiveRecord::Base.time_zone_aware_attributes = true, so when cloning (ActiveRecord::Base.clone_attribute_value) datetime attributes it was getting the object Time and not ActiveSupport::TimeWithZone.

    My approach on this patch is storing on @changed_attributes the actual ActiveSupport::TimeWithZone object when ActiveRecord::Base.time_zone_aware_attributes = true and the attribute is not on skip_time_zone_conversion_for_attributes hash.

    I don't know if it's the right way to attack this issue, it's just a try.. :)

    April 8th, 2010 @ 07:36 AM

  5. Kristopher Murata
    Kristopher Murata

    Ops, noticed that I broke one test by note keeping my time zone configurations inside my test scope. Attached another patch with tests written right.

    April 8th, 2010 @ 08:41 AM

  6. Repository
    Repository
    • State changed from open to resolved

    April 8th, 2010 @ 06:28 PM

  7. StuFF mc
    StuFF mc
    • Importance changed from to Medium

    Turns out the problem is the same (2.3.8) when accessing .attributes (http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M001863). Eg:

    >> User.first.attributes["created_at"]
    => Sun Oct 04 22:36:44 UTC 2009
    >> User.first.created_at
    => Mon, 05 Oct 2009 00:36:44 CEST 02:00
    >> User.first.attributes_before_type_cast["created_at"]
    => "2009-10-04 22:36:44"
    
    >> User.first.created_at.class
    => ActiveSupport::TimeWithZone
    >> User.first.attributes["created_at"].class
    => Time
    >> User.first.attributes_before_type_cast["created_at"].class
    => String
    

    How can I "keep" the timezone when accessing ".attributes"?

    July 17th, 2010 @ 09:50 AM

  8. bingbing