Skip to main content

22 Forum Series Part 7: Time Zones using the local_time Gem

Episode 31 · November 6, 2014

A look into times, timezones, and how to handle it better with javascript

Gems Javascript Frontend


Transcripts

In today's episode, we're going to talk about time zones a little bit, then use the local time gem to handle displaying local times based upon the dates and times that we have saved in our application. What happens with your Rails application, when you save a date time, you always want to save that in your database as a UTC time stamp, because that's sort of the de-facto standard. All of the time zones are based off of UTC, plus or minus a certain number of hours. So if we set that as the default in the database, we'll be able to just calculate the difference based on the time zone that the user's in.

Let's go to our app/views/forum_posts/_forum_post.html.erb, the time stamps for each of those forum posts that we created. I'm going to paste this in here

on <%= forum_post.created_at.strftime("%B %d, %Y at %l:%M %p") %>

Which displays the month, the date, the year, the hour, the minute and the AM/PM operator. So you can refresh the page and you'll see that these posts were posted by me on October 23, 2014 at 2:47 PM, and this is going to be a UTC time stamp. So one thing that's really useful for writing these time formats out is the website strfti.me, all of the different formatting options that you have and use that to manipulate what you display, so you can even include the %Z and display the time zone there, and it's probably useful is we show that here, when we refresh our forum, you can see that it's definitely the UTC time stamp, so definitely bookmark strfti.me because it's almost invaluable and you don't have to do much and you can double check to make sure that your format is correct really easily in its own application.

Coming back to our forum, you can see that we have a prettier time, but it's still in UTC format, so how do we go change that?

Inside your application.rb, there's usually this line:

# config.time_zone = 'Central Time (US & Canada)'

and you can delete this (# symbol) and set it up so that by default, your application will run in that time, so then Active Record, as the comment says here, will convert times to this zone for you.

If we save this and we restart our Rails server, and then we go back to Chrome and refresh our page, you'll see that now we have 7:47 CDT, so this is automatically converted the time zone to central time for us, and that will be global. The problem is when you have users that are in different time zones, so for us this is great and handles things, but you know what, it's the internet and people come from all around the world, and need to see times that are more convenient to them, they need to actually know that it's in their time zone. So this global format is fine but it's not the best thing that we need to make our application really really nice. So I'm going to comment this back out, restart our Rails server again, and we're going to take a look at the local time gem.

The local_time gem is written by Basecamp, and what it does, is it generates times for you and then when you print them out in your views, it will generate a time tag, so these time tags, rather than what I've written here, which is just text on the page in the html, these will actually render html tags called time tags, and then it will set a bunch of different attributes here.

So it sets a JavaScript parsable datetime string so that JavaScript can parse it, and the you can see that it has a data-local and a data-format, so this format will be used by the JavaScript to render out this string inside that the user will be able to see it. So we'll be able to add these data attributes that are hidden in the html, and basically control what gets displayed. They also have lots of awesome features here, such as formatting, that you can override, as well as time_ago which if your familiar with something like Twitter, you'll see that this tweet was sent one second ago, 32 seconds ago, yesterday, Tuesday and so on. So as time goes further away, you can see that these posts are a little bit less accurate, so if it's the current year, you can save the year, you don't need to display it, but as time goes on, you can do all of that.

This gem is incredibly useful for this, and it allows us to set these up so that when you view this in your browser, depending on what your browser's timestamp is, you'll get the right date.

If we go to our Gemfile and add the gem 'local_time', here, we can see that I've got a few gems from previous things so you can ignore those, or check out those episodes, but the gem local_time currently the version is 1.0.0 and we'll install that and run bundle and restart our Rails server. Once this is done, we'll refresh the page and make sure that we get the UTC timestamps again, and we'll be able to verify that I've reset the global application time and that has happened, so we're back at 12:47 PM UTC and with this local time gem, it's really really simple.

If we scroll down to the installation section here, all you have to do is add the gem, run bundle install, and then add //= require local_time into your application.js file. Let's go down below Turbolinks and paste that in, and now we can refresh our page. Nothing changed, the JavaScript is now loaded, though, as we can see in the source here, we'll see the local time JavaScript and you can see exactly what they do as well if you're interested in the gem or the JavaScript and how it works.

If we come back here, I really prefer the local time_ago, and we'll play with a few of these different features.

If we go back to our app/views/forum_posts/_forum_post.html.erb, we can change this to:

<%= local_time_ago forum_post.created_at %>

Now when we refresh the page, we'll get the on inside of our texts now so we don't need to include that anymore, and we'll get on October 3rd, because that was a few days ago. Now if we create a new post, we can see that it says now: "a second ago", if we wait a little bit, we can start to see that it changes to be little bit more specific. This will continue updating and be pretty accurate as well as allowing you to hover over it and see the exact time in browser tool tips of when it was posted. And also, if you noticed there, it said CST, which means that it knew that our local time zone is central time because our application is still running in UTC time. This is really neat, it's created this time html tag, we've passed in the time as the datetime parameter, and then this title is what you saw in the hover state here, as well as the text on the page being a little bit different. So this doesn't update in real time, of course, but when you refresh the page, it loads and then runs through every single time tag on the page that fits that format, and we'll continue updating that for you. That's really really neat.

If you want regular times, you can change this to

<%= local_time forum_post.created_at %>

and leave out the ago part, and you'll see that it gets basically the same format that we had before. It leaves out the time zones because they're not really that relevant when you actually have correct times like this, based upon your browser, and you can actually trust that the time is what you expect it to be, so that's something that can become very very useful when your teams are spread across the world, everybody will know what time relative to them it is, and then they can figure out the translation of what that is in a different time zone if they need to. But most of the time, you're concerned about you and your time of where it's at and where you are.

Now the last thing that I want to point out here is that if you inspect this page you'll see that the time tag here is generated but it gets tweaked by the JavaScript like we mentioned before. Now the original output of this is designed so that the JavaScript can always look at these attributes, so it knows what style you want to display, (time_ago), and then it also knows the time. So this title is what gets added dynamically as well as the text inside of the time tag and those two aspects will never show up in the html, (the JavaScript will generate it).

What happens is your output of this time tag will show a generic thing, such as the exact time in UTC time, and then it allows your Rails application to cache this, so you might save it in a file, you might save it in a fragment or in memcache d or whatever, and it doesn't matter because the JavaScript is the one actually looking at this time and converting it to the correct time. So this is really neat, in that sense that you can save this, and it can be cached, and you don't have to worry about it because this will always display the right thing, and you don't have to break the cache, which is a big problem when it comes to keeping caches in sync. So imagine if you had two different users and you weren't using this and you wanted to display the time in the time zone, you would have to break the cache every time someone else viewed that page or you'd have to save multiple caches for different time zones which is overwhelming, so since you wouldn't get benefit out of that, the design here is to save a generic html tag, and then use JavaScript to manipulate it for the user. That is something that is a very wise choice here, and something you can probably take and implement in application in other areas and say which ones are good cases where this needs to be manipulated specifically of the user, and if we keep the html generate generic, then we can generate a version that the user needs to see with JavaScript, so that is a little tip on caching that just comes along with talking about time zones.

I hope you enjoyed this episode, and I will see you in the next one.

Transcript written by Miguel

Discussion


Gravatar

strfti.me is the time zone website recommended in the video.

foragoodstrftime.com is another one to check out.


Gravatar

How to establish Russian?

Gravatar

As @excid3:disqus said before, see Rails/I18n for local_time gem documentation here: https://github.com/basecamp...


Gravatar

How to use Russian lang? help

Gravatar

Check out the gem documentation for some more info on I18n https://github.com/basecamp...

Gravatar

Gravatar
Wilson Peng on

Is this the last one of the series?

Gravatar

Yeah it was not a very good series. I didn't show the whole thing and just emphasized certain parts. Are you interested in seeing the whole thing? I might do a revised series on it or something similar.

Gravatar

I thought it was a very good series, you gave out all of the pieces necessary to build a decent forum. With a solid base like this anyone can expand on it through their own research and add extra features as necessary.

Gravatar

Hi am very interested in this series I am building something similar.
Thanks!


Gravatar
Sz M (2,710 XP) on

Nice tutorial Chris! Could you tell me how you would use this gem in a rails form where the date is selected? So let's say I wanna see and choose the datetime in PST in the form (w/ datetime_select or bootstrap datetimepicker), but wanna have it saved to db in UTC.

Gravatar

ActiveRecord should always save UTC to the database unless you changed that. You can set the default time_zone in Rails to be PST and keep the activerecord timezone as UTC in order to have all form submitted times process in PST and then get saved as UTC. http://stackoverflow.com/qu...

The other thing is you could add some code either to your controller or model to set the timezone of the submitted attributes. Either approach should do the trick for you. Any form datetimes that you submit have to get cast to an actual Time object in Rails in anyways so you'd just be adjusting that process.

Gravatar
Sz M (2,710 XP) on

Thanks Chris! I will try the 2nd approach since everybody says it's better to keep (server,app,db) everything in UTC.


Gravatar

My use case is not to ask users about their location, but automatically set timezone based on their GeoLocation. Any suggestions wrto gem out there will be really helpful ?

Gravatar

That's what local_time does. :)

Gravatar

Interesting, Let me play with it...


Gravatar

Hey Chris! This is great. One question: How would you go about inputting time? Lets say the user is making an event or appointment. They are presented with date/time input fields. How would you handle that if different users are in different time zones? Would that require storing a time zone on the user/account?

Gravatar

Looks like this question was answered below. My use case is a multi-tenant app. All the users would be in the same place. So maybe I store the time zone on the account. Wonder if there is a way to specify a dynamic time zone like you would in application.rb based on what the account has selected.

Gravatar

So in the end, still a PITA, haha. I think I'd prefer your first approach in the answer below...but somehow set the time zone based on the account. The feel like the best solution would be to not have to litter models/controllers with time parsing zone parsing code and to just allow the account to set their time zone and have it just work.

Gravatar

This looks like a good strategy: https://robots.thoughtbot.c...


Gravatar

Hi Chris,

How do I add the local-time.js to my app using the local_time gem, but not using the asset pipeline? I see an option to add it using the npm package (see below) but I am not sure how this works and where to put it.

import LocalTime from "local-time"
LocalTime.start()

Please advise.

MDR


Gravatar
I can't seem to get this to work. I just get: undefined method 'local_time'.

Do you know if there's a problem with this gem and Rails 5.2? I can see the Gem is installed and in the asset pipeline. 

Login or create an account to join the conversation.