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.
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
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.
I hope you enjoyed this episode, and I will see you in the next one.
Transcript written by Miguel
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.
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.
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.
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.
Thanks Chris! I will try the 2nd approach since everybody says it's better to keep (server,app,db) everything in UTC.
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 ?
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?
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.
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.
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"