51

I am using rails 3.0.5 and I have created_at and updated_at stored in UTC. Now I want to display the created_at time in users' timezone. I believe it is possible to pick user's timezone from the browser and then convert time to user's timezone.

I am sure rails will have a gem/plugin to take care of something like this. Is there something?

Nick Vanderbilt
  • 36,724
  • 29
  • 83
  • 106

8 Answers8

73

Rails by default converts every date into UTC before storing the value into the database. This means that, regardless the server timezone, you always have UTC dates in your database.

In order to convert the dates into your user's timezone you have basically two possibilities:

  1. server-side approach
  2. client-side approach

Server-side approach

If your site allows registration, you can store the user timezone as user preference. In the user table, store the user timezone. Then create a custom helper you can use to format any date/time into the proper timezone using the in_time_zone method.

> t = Time.current
# => Mon, 23 Dec 2013 18:25:55 UTC +00:00 
> t.zone
# => "UTC" 
> t.in_time_zone("CET")
# => Mon, 23 Dec 2013 19:25:55 CET +01:00 

Your helper may looks like

def format_time(time, timezone)
  time.in_time_zone(timezone)
end

I normally also like to output a standard format, using the I18n.l helper

def format_time(time, timezone)
  I18n.l time.to_time.in_time_zone(timezone), format: :long
end

Client-side approach

If your site has no registration or you don't want to ask your users for their timezone or you simply want to use the user system timezone, then you can use JavaScript.

My suggestion is to create a custom helper that will print out every time in a proper way so that you can create a generic JavaScript function to convert the values.

def format_time(time, timezone)
  time = time.to_time
  content_tag(:span, I18n.l(time, format: :long), data: { timezone: timezone, time: time.iso8601 })
end

Now, create a JavaScript function that is performed on DOM load and that will select all the HTML tags with data-time attribute. Loop them and update the value inside the span tag with the proper time in the given timezone.

A simple jQuery example would be

$(function() {
    $("span[data-time]").each(function() {
        // get the value from data-time and format according to data-timezone
        // write the content back into the span tag
    });
});

I'm not posting the full code here, since there are plenty of JavaScript time formatters available with a simple search. Here's a few possible solutions

Community
  • 1
  • 1
Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
  • 1
    Sorry the OP hasn't mark any answer as the right one; yours explains everything so it deserves reward :/ So (in plus of the upvote...) __THANKS YOU__ ! – gfd Dec 07 '16 at 12:07
  • This answer looks good for my application, but how would I append the updated Javascript time to each network request in order to read the timezone in my controller? – alex Feb 07 '19 at 13:50
26

There is a nice gem by Basecamp called local_time for client side rendering - https://github.com/basecamp/local_time. It's great for applications where user is not signed in and it's caching friendly.

Strika
  • 634
  • 7
  • 12
9

You can add this to your application controller to convert all times to the User's timezone:

class ApplicationController < ActionController::Base

  around_filter :user_time_zone, :if => :current_user
  def user_time_zone(&block)
    Time.use_zone(current_user.timezone_name, &block)
  end
end

You just have to capture the user's timezone

John Naegle
  • 8,077
  • 3
  • 38
  • 47
  • Thanks heaps! If you want to use the server timezone, just change `current_user.timezone_name` to `Rails.application.config.time_zone`. – XtraSimplicity Aug 20 '20 at 07:12
3

Assuming that the value you want displayed is coming from the database, :ie started_at and is (as is the default) stored in UTC.

If you have the user's timezone as an offset you can also localize the time by doing:

started_at.in_time_zone(-2)
=> Mon, 24 Feb 2014 23:07:56 GST -02:00

Which then can be munged in all sorts of way to get the parts you want:

started_at.in_time_zone(-2).yesterday
=> Sun, 23 Feb 2014 23:07:56 GST -02:00

started_at.in_time_zone(-2) + 3.days
=> Thu, 27 Feb 2014 23:07:56 GST -02:00
Richard Luck
  • 640
  • 7
  • 15
1

http://api.rubyonrails.org/classes/Time.html#method-c-use_zone

This is what you're looking for :

Time.use_zone(@user.timezone) do
  blah blah blah
end
Trip
  • 26,756
  • 46
  • 158
  • 277
  • 1
    You need to allow your user to choose what their timezones are. I guess you could "sniff" these sort of things out, but its best just asking them because people a lot of the times manage assets from other timezones then the ones they are living in. So @user is your User, and timezone is an attribute that you let them update in their profile. Then you use the method `use_zone` to instantiate that zone. Alternatively you can just update your environment to save all timezones :local – Trip Jan 14 '13 at 14:28
  • Question was worded differently. Force the user to fill out forms everyone is able from textbooks. – antiqe Jan 15 '13 at 03:43
1

If you'd like to convert your date to a specific Timezone:

deadline.in_time_zone(time_zone)

Here deadline is a date.

In addition, you can find Universal time through your local machine Timezone plus local time and vice verse, like in Karachi - +05:00, you can simply add it to value in Universal time to find time in your time zone or get Universal time from your local time by subtraction of Timezone difference (05:00 in our case) from your local time

1

My jquery is a rusty, so it took me a little while to figure out how to implement the client-side approach of the accepted answer above.

Here's my solution:

HTML:

<span data-time="<%= last_message_at %>">&nbsp;</span>

Jquery/Javascript:

<script type="text/javascript">
$(document).ready($(function() {
    $("span[data-time]").each(function() {
        var timestamp = $(this).attr("data-time");
        var localeTimestamp = new Date(timestamp).toLocaleString();
        $(this).html(localeTimestamp);
    });
}));
</script>
Dagmar
  • 2,968
  • 23
  • 27
0

You might want to take a look at these links in addition to the one Ben posted:

Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
aNoble
  • 7,033
  • 2
  • 37
  • 33