As I was coding Too Many Secrets (your one-stop shop for anonymous confessions), I decided I wanted to show the times that secrets were added. No real reason, other than I was sick of having to write down the ID of the last secret I read on sites like Cave Canum. Post-it notes, while cheap, are a pain in the ass to keep successfully organized. It’s much easier to remember “Well, the last time I looked at it was around 2AM on Sunday.”

Seemed easy enough to just write out the value of created_at, and let it go at that. Unfortunately, that displays the time on the server, not the client. Since DreamHost is located in California, and I’m in Texas, it’s a 2 hour difference, and I had to find a way to display it in the client’s local time.

Err The Blog had a good article back in March called A Zoned Defense, that covered just this problem. Of course, if JavaScript or cookies were turned off, it wouldn’t work, but it’s difficult to have a fancy Web-2.0-type site without JS or cookies enabled, so let’s assume they are.

The problem with Err’s solution is that I’d like to cache the shit out of everything. If I’m taking a cookie from the user and outputting the time just for them, there’s no way I can cache it. Well, I could if I wanted to cache the fragment for every possible timezone offset. Turns out there’s a much easier way, and it requires no plugins or cookies, but still requires JavaScript.

To make things simple, I’m using MooTools, though it should be doable with Prototype, or just plain old handcrafted JS.

The secret is to use Time::to_i, which returns the value of the given time as an integer number of seconds since epoch. JavaScript’s Date class has a method that allows you to set the time using the number of milliseconds since epoch. All you have to do is multiply your timestamp as an integer by 1000.

Since most people can’t read epoch times, we’ll put it in an invisible span, like so:

<span class="hide dt">
  <%= secret.created_at.to_i * 1000 %><br/>
</span>

The CSS classes just hide the span from view using display: none;. What we’ll do is grab every span on the page that has the “dt” (datetime) class, read it’s text (which should be the milliseconds since epoch), use that to set the time of a JS Date object, and replace the span’s text with the new date, then drop it’s “hide” class. Add the following to your application.js in Rails.

var Timezone = {

  replaceEpochWithLocalDate : function() {
        var date = new Date();
        var dateSpans = $$(’span.dt’)

        dateSpans.each(function(dt){

            var msFromEpoch = dt.getText();
            date.setTime(msFromEpoch);
            dt.empty().setHTML(date.toLocaleString() + ‘<br/>’);
            dt.removeClass(‘hide’);
            dt.removeClass(‘dt’);
        });
  }
};

window.addEvent(‘domready’, Timezone.replaceEpochWithLocalDate);

The “domready” event is only available in MooTools, I believe, but either way, you just need to call replaceEpochWithLocalDate() when the page loads. The “DOM Ready” event just does it as soon as the DOM structure is available, before all of the images have even finished loading, so you don’t have the somewhat jarring effect of all of the dates popping up after the page loads completely. There are probably ways to do it in Prototype or plain JS, but it’s done for me in MooTools, so I could give a crap.

The real magic in this method comes from JavaScript’s Date::toLocaleString, which prints out the date using whatever locale your browser it set to, in your local time. No more screwing around behind the scenes trying to figure out how they want it printed. The work is already done for you by their browser, and they’re either using the default, or they have it configured how they want it. Either way, they see the time they prefer. All you have to do is save and display your timestamps like normal, and stop worrying about timezones.

The only downfall to this method is that you have to have JavaScript enabled, same as the three methods provided in Err’s article. If not, you just won’t see the dates, unless you’re using a browser like Lynx, which is text-only, and no CSS or JavaScript. In that case, you’ll just see the epoch time, plain as day. But if you’re cool enough to use Lynx, you’re cool enough to decode epoch times, so it’s a win-win for everyone involved.

One Response to “Easier Timezone support in Rails”

  1. baNdit: Sex and the City edition (0.5.5) released « baNdit addon for banniNation Says:

    [...] the existing timestamp on comments with what time they were added in your local time. I wrote an article on this very subject for Ruby on Rails last [...]

Leave a Reply