Sexy Time Zones in Ruby on Rails with Timezone_Fu

As easy as has_timezone :fields => [ :start_datetime, :end_datetime].

Working with Timezones in Rails even after you have installed the TZInfo plugin is still a pain in the you know what. (You are using TZInfo, right?) World TimezonesI never liked having to jump through hoops converting from UTC to localtime when displaying times to Users. I suppose I could have used Javascript to do the conversion but I prefer to deal with the conversion on the back end.

Adding support for time zones to our apps wasn’t very fun. While adding timezones to another model model for the third or forth time I decided there had to be a better way. I pointed my browser to the agile web development plug in site and did a quick search. I found a couple plug-ins that helped with timezones or helped with date time pickers; but nothing that did quite what I wanted.Ideally I wanted a plugin similar to attachment_fu. I wanted to specify within a model a single method that handled all of the setup. So when I accessed a datetime field the local time would be returned. I also wanted to be able to set the field to local time and have that value converted to UTC before being saved to the database. And the plugin had to do all of this with minimal setup and effort on the part of the developer. Not finding anything that satisfied my requirements I decided to write something. After an hour or two I put together something that I think is worth sharing. Included at the bottom of this post are links to grab the plug-in.

While building ThriveSmart Sprout! we used plugins from various developers. The availability of plugins was on the the great strengths of the Rails comunity. In honor of techno weenie and the attachment_fu plug-in our plug-in was christened timezone_fu.

Timezone_fu makes it really easy to deal with datetime fields in your models. It adds a method to your models has_timezone that takes a hash of options. the README for the plugin describes all of the options but below is an example:

  class Event < ActiveRecord::Base
    has_timezone :fields => [ :start_datetime, :end_datetime]
  end

Our Event model has three fields a start_datetime and end_datetime and a timezone. Adding has_timezone to the model changes the behavior of the two datetime attributes.
Notice below that calling event.start_datetime shows the time in local time (“America/New York”).

>> event = Event.find(:first)
=> #<Event:0x1b71b40 @attributes={
      "end_datetime"   => "2007-11-21 15:00:00",
      "start_datetime" => "2007-11-21 14:15:00",
      "timezone"       => "America/New_York"}  
>> event.start_datetime
=> Wed Nov 21 09:15:00 UTC 2007  
>> event.display_start_datetime  
=> "Nov. 21, 2007 09:15 AM"  
>> event.utc_start_datetime
=> Wed Nov 21 14:15:00 UTC 2007

You can also set the value of the field to a local date/time and it will be converted to UTC automatically:

>> event.start_datetime = Time.now
=> Sun Nov 25 06:26:35 +0000 2007
=> #<Biz::Event:0x1b71b40 @attributes={
  "end_datetime"    =>  "2007-11-21 15:00:00"
  "start_datetime"  =>  Sun Nov 25 11:26:35 +0000 2007,
  "timezone"        =>  "America/New_York" }

If you want to access the fields and retrieve the database value you can use the utc_field_name method to access the UTC value of the attribute. The plugin makes a couple of core assumptions. One it assumes that the default rails timezone is UTC:

ENV['TZ'] = 'UTC'

Second it assumes that the model has a timezone attribute named timezone:

t.column :timezone, :string, :default => ''

The plug-in depends on TZInfo and has been tested under Rails 1.2.3

You can grab a copy of the plug-in here: http://www.thrivesmart.com/open_source/timezone_fu.
Or if zipped is more your style: http://www.thrivesmart.com/open_source/timezone_fu.zip

If you love and/ or hate the plug-in feel free to leave your comments below.

Explore posts in the same categories: rails

Tags: , , ,

You can comment below, or link to this permanent URL from your own site.

19 Comments on “Sexy Time Zones in Ruby on Rails with Timezone_Fu”

  1. Neeraj Says:

    1) Does it work with Rails 1.2.x ? Or I need Rails2.0?
    2) What are the other plugins it is dependent on?

  2. Nilesh Says:

    This looks awesome. How easy is it to modify the time values? Do I have to ensure that the new value of start_datetime is always UTC time when I update that field?

    One more problem that I see — what about other models which have datetime fields and those fields are to be displayed in the User model’s timezone? Is that possible?

  3. Dan Says:

    @Neeraj the plugin is for Rails 1.2 tested under 1.2.3. It depends on TZInfo.

    @Nilesh – It’s pretty easy to modify. If you wanted to use an environment timezone besides UTC you could, you would need to modify the plugin methods get_utc_date_time(timezone, local_datetime) and get_local_date_time(timezone, utc_datetime) to do the appropriate conversion.

    You set the value of start_datetime to local time it will be converted automatically by the plugin. I will update the example to reflect that.

    For the case of displaying times based on a timezone in a User or other model checkout Jamis Buck’s TzTime (http://weblog.jamisbuck.org/2007/2/2/introducing-tztime) plugin.

  4. Bran Says:

    is this suitable to plugin to created/updated_at ? for example:

    has_timezone :fields => [ :created_at, :updated_at ]

  5. Dan Says:

    @Bran, it should work. I haven’t specifically tested it. Would you mind trying it out and letting me know if there are any issues?


  6. Sexy Time Zones in Ruby on Rails with Timezone_Fu

    A collection of cool ideas we come across while building ThriveSmart


  7. [...] Sexy Time Zones in Ruby on Rails with Timezone_Fu [...]


  8. Have dirtied my hands with timezone code a few times

    the problems i faced was with most ruby and other libraries use linux style timezone files, with 100’s of entries and my client wanted a shorter version like with microsoft timezone select box.

    i eventually had to built a hash table over tzinfo and get things in order.

    will surely test your stuff and report.

  9. Dan Says:

    Just posted an updated version of the plugin that fixed a couple bugs and also makes it compatible with string date/times of the form ‘Nov 27 2007 00:00:00′. Useful if you want to use the plugin in conjunction with a date time picker like calendar_date_select

  10. Stephen Says:

    This sounds nice, but is there a way to centralize the timezone setting to the user?

  11. Dan Says:

    @stephen – I think you might want Jamis Buck’s TzTime (http://weblog.jamisbuck.org/2007/2/2/introducing-tztime) plugin.


  12. Hola! Any plans to use a svn-repository instead of normal downloads in the near future? Miight make things a tad easier :)

    But awesome work and thanks for opernsourcing it!

  13. Dan Says:

    @Joerg, I my put the code into svn at some point. We use git for all of the thrivesmart projects and I haven’t had a chance to setup any SVN repositories.

  14. Mario T. Lanza Says:

    Well. I like the idea of timezone_fu, but I am confused about it’s design and use. Maybe you can clarify.

    It looks like we are supposed to store “timezone” (e.g. “US/Eastern”) on the model. So, if I have an Event table, I need a column called “timezone”. Not sure why this is. First, as far as I can tell, this value isn’t automatically being stored when a record is created as I assumed it would be. Second, even if it was being stored, I don’t see the value as it appears all date/times are being stored as of UTC time. Upon recalling the date/time for display, don’t we want to convert to the user’s locale (which we don’t know until the user logs in).

    Here’s another source of confusion. Most date/time values (from my perspective) are more useful in their original local timezone format. I think an events table is more useful and better queried using local time arguments, not ones I had to manipulate. That is, if I want to query events for a certain month I want to use a BETWEEN clause that reflects the true start and end of the month based on local times, not times that were fudged from UTC. Here’s where I see the valid use of the timezone attribute. Store date/times using local values and accompany them with the name of the timezone from where the record originated, so that if need be we can make the appropriate conversion.

    Could you provide a practical example in the documentation of how you made use of timezone_fu? Right now, you just show how to extend the ActiveRecord model.

  15. Justin Says:

    You could do something like http://pastie.caboo.se/129287 to make it work with TzTime (untested), or to use a timezone attribute from a related model.


  16. [...] easy as: has_timezone :fields => [ :start_datetime, :end_datetime] Timezone_fu makes it really easy to deal with datetime fields in your models. It adds a method to your models, [...]

  17. rick Says:

    Rails 2.1 will have timezone stuff built into activesupport, with tzinfo either bundled or used by Rails. You can see the progress in the latest trunk version. Just FYI…

  18. Madhu Nallamani Says:

    Hi ,

    Is there anyway I can display time in “US-Central” or “US-Eastern” format instead of “ America-New york” or “ Europe-Athens” format at the top of drop down list using Tzinfo .

    I am presently using “time_zone_select” which displays US zones on top of drop down in “ America-New york” format .

    TZInfo::Timezone, :default => “America/Chicago” )%>

    I can display US zones in “ (GMT – 06:00 ) CentralTime (US & Canada)” by using standard rails TimeZone ( ) . But , I will have problem when I use “utc_to_local” method .

    Thanks,
    Madhu Nallamani

  19. Dan Says:

    @Madhu, yes just create a helper that maps the more friendly time_zone_select names to tzinfo names.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: