0

I beat my head against this for a day and a half before it finally all came together, partly because I was fundamentally misunderstanding some things because of my assumptions of how things should work, and so the results I was getting seemed so completely irrational and confusing that I was starting to think things must be horribly broken in the libraries. They weren't.

This thread is to share my question, and what worked, partly because it seems to me that a lot of times a datetime record should be tied to the localtime of its location, and a lot of times when applications behave strangely with respect to timezone changes I think it's because they don't do that. So here's how to do that.

The easy part was using the TimeZone gem to get the location's timezone (I already have the latitude/longitude from using the GMaps4Rails gem), as described here: Ruby gem for finding timezone of location

Initially, I hoped each datetime could have an intrinsic and persistent time zone, even after being saved and retrieved from the DB, since we only care about the local time zone for each event. For the unusual and rare case where the datetime was needed in other-than-local-zone, I could convert. But I guess that doesn't exist since MySQL seem to have no concept of timezone with its datetime, and just stores everything as a plain ol' date and time.

So, that's fine -- the DB stores in UTC and I must do the conversions each direction.

How? (See below.)

Community
  • 1
  • 1
Scott W
  • 202
  • 2
  • 12

1 Answers1

0

User enters a datetime (year, month, day, hour, min, sec) in the appropriate local time for the event location. To save this as the correct UTC time in your DB, and retrieve later retrieve it and use it with its correct timezone, here's the recipe:

  1. You must first set Time.zone = timezone -- where timezone is a named timezone string, like "America/Toronto" (GeoKit/TimeZone style) or "Eastern Time (US & Canada)" (Rails 3 style):

    1.9.3p200 :001 > Time.zone = "America/Toronto"
     => "America/Toronto"
    

    Note that it is nontrivial to map between the GeoKit/TimeZone style and the Rails 3 style. There is some support for that, but many of the mappings that GeoKit returns are not in the Rails mappings, so I'm sticking with the GeoKit/TimeZone names because they seem to work just fine, and I don't need user-selectable timezones because I'm taking care of all of that automagically for the user using the locations.

  2. Now you can parse and save your datetime variable using ActiveSupport::TimeWithZone, and it will be encoded properly using the Time.zone parameter:

    1.9.3p200 :002 > t = Time.zone.local( 2012, 8, 1, 12, 0, 0 )
     => Wed, 01 Aug 2012 12:00:00 EDT -04:00 
    

    i.e. In my case, for my Event model with instance event:

    event.start = Time.zone.local( year, month, day, hour, min, sec )
    event.save
    

    When the data is saved to the DB, it is in UTC: 2012-08-01 16:00:00.000000

    (Simulated below with call to t.utc)

    1.9.3p200 :003 > t = t.utc
     => 2012-08-01 16:00:00 UTC
    
    1.9.3p200 :004 > t.zone
     => "UTC" 
    
  3. Now, when you read it back from the DB, you need to again use the TimeWithZone support to tell the DateTime what timezone it is in.

    Note that the in_time_zone function does not change the underlying datetime variable's timezone:

    1.9.3p200 :005 > t.in_time_zone( "America/Toronto" )
     => Wed, 01 Aug 2012 12:00:00 EDT -04:00 
    
    1.9.3p200 :006 > t
     => 2012-08-01 16:00:00 UTC 
    
    1.9.3p200 :007 > t.zone
     => "UTC" 
    

    so you really need to assign the function output back to your datetime variable:

    1.9.3p200 :008 > t = t.in_time_zone( "America/Toronto" )
     => Wed, 01 Aug 2012 12:00:00 EDT -04:00 
    
    1.9.3p200 :009 > t
     => Wed, 01 Aug 2012 12:00:00 EDT -04:00 
    
    1.9.3p200 :010 > t.zone
     => "EDT" 
    
  4. Finally, a warning -- do not use t.time! It's output is completely useless and irrational, with the time and it's timezone out of sync:

    1.9.3p200 :011 > t.time
     => 2012-08-01 12:00:00 UTC 
    
Scott W
  • 202
  • 2
  • 12