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.

Inconsistant time zone handling

#2479

I'm storing datetimes in MySQL in UTC. I want to retrieve them based on date.

Here's the problem. Time objects ignore the time zone in to_s(:db).


>> Time.local( 2009,1,1, 0,0,0 )
=> Thu Jan 01 00:00:00 -0600 2009
>> Time.local( 2009,1,1, 0,0,0 ).to_s(:db)
=> "2009-01-01 00:00:00"

TimeWithZone objects preserve the time zone in to_s(:db).


>> Time.zone.local( 2009,1,1, 0,0,0 )
=> Thu, 01 Jan 2009 00:00:00 CST -06:00
>> Time.zone.local( 2009,1,1, 0,0,0 ).to_s(:db)
=> "2009-01-01 06:00:00"

All the to_time helpers return Time objects which have their time zone thrown away when used in an ActiveRecord query. This leads to problems when using to_time

Example:


>> Date.today.beginning_of_day
=> Fri Apr 10 00:00:00 -0500 2009
>> Time.zone.now.beginning_of_day
=> Fri, 10 Apr 2009 00:00:00 CDT -05:00
>> Date.today.beginning_of_day == Time.zone.now.beginning_of_day
=> true

>> Person.all :conditions => [ "created_at > ?", Date.today.beginning_of_day ]
Generated SQL => SELECT * FROM "people" WHERE (created_at > '2009-04-10 00:00:00')

Person.all :conditions => [ "created_at > ?", Time.zone.now.beginning_of_day ]
Generated SQL => SELECT * FROM "people" WHERE (created_at > '2009-04-10 05:00:00')

It would seem to me that Time#to_s(:db) should convert the time zone and/or to_time should return a TimeWithZone.

Reported by JackC · April 10th, 2009 @ 08:39 PM

State: wontfix
Milestone: 3.0.2
Assigned to: Geoff Buesing Geoff Buesing
Importance: Medium

Activity

  1. Geoff Buesing
    Geoff Buesing
    • Milestone cleared.
    • Assigned user set to Geoff Buesing
    • State changed from new to open

    I agree this is inconsistent -- TimeWithZone and Time.utc instances report themselves to the database as UTC, whereas Time.local instances report themselves in system local time.

    Ideally, all of these objects would intelligently report themselves to the database either as UTC or system local time, depending upon the ActiveRecord::Base.default_timezone setting.

    to_time isn't the place to add this kind of logic, given that it's used in contexts other than database serialization -- #to_s(:db) would be a better choice.

    Another possibility -- maybe the cleanest choice -- would be to add logic to quoted_date to coerce the time object to utc or local before the #to_s(:db) call.

    Of course, this kind of change could break some apps, if the app, say, uses Time.local instances as container objects to send utc values to the db -- kind of wonky, but there's nothing stopping you from doing this now.

    Interested in hearing thoughts on this.

    April 11th, 2009 @ 02:27 PM

  2. JackC
    JackC

    I added this line in an initializer and it seems to solve the problem.

    
    ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS[:db] = lambda { |time| time.utc.strftime( "%Y-%m-%d %H:%M:%S" ) }
    

    This gives Time and TimeWithZone the same behavior and the behavior exists in to_s(:db) for both of them.

    April 13th, 2009 @ 04:28 PM

  3. Geoff Buesing
    Geoff Buesing

    That would work. Though you should use the non-destructive #getutc instead of #utc, otherwise you might get some unexpected behavior.

    April 14th, 2009 @ 12:32 AM

  4. JackC
    JackC

    I've been running this code with the change to #getutc for the last month in production without any issues. Any chance of getting this made the default?

    May 22nd, 2009 @ 02:43 PM

  5. Fernando Guillen
    Fernando Guillen

    The @JackC fix doesn't work for me.

    I had to define a new time_format:

    Time::DATE_FORMATS[:m] = "%Y-%m-%d %H:%M:%S"
    

    And use it on my views

    July 31st, 2009 @ 12:02 PM

  6. JackC
    JackC

    I don't see how just a format string would would also change a local time to UTC time.

    Anyway this is the code I'm loading in an initializer and it is definitely sending UTC to the database.

    ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS[:db] = lambda { |time| time.getutc.strftime( "%Y-%m-%d %H:%M:%S" ) }
    

    July 31st, 2009 @ 02:07 PM

  7. Geoff Buesing
    Geoff Buesing
    • State changed from open to wontfix

    We're going to go forward with a different approach: quoted_date will do the conversion, instead of TimwWithZone#to_s(:db), see https://rails.lighthouseapp.com/projects/8994/tickets/2946-quoted_d...

    July 31st, 2009 @ 02:34 PM

  8. Jeremy Kemper
    Jeremy Kemper
    • Milestone set to 3.0.2
    • Importance changed from to Medium

    October 15th, 2010 @ 11:01 PM