hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Showing Perfect Time #

by why in inspect

What timezone do your site’s timestamps show? Or have you opted to just say three months ago? Or you could offer the user a timezone setting, right? These are tough issues, with a worldwide audience which could be anywhere and a server which could end up moving anywhere as well.

Hold up. Before you start writing anything, ecmanaut has a very convincing rant on not interfering at all. Just pass UNIX time straight out of the database and format the time in Javascript. (Read his article and trounce back here for the slight Ruby code.)

Pitching Timestamps to Javascript

Can’t you see your web framework abstracting this away nicely?

  Posted by <%= user.name %> on <%=js_time post.created_at %>

I’ve added this in Hoodwink’d like so:

 def js_time( unixt, fmt = "%d %b %Y at %I:%M:%S %p" )
   %{
      <script type="text/javascript">
        document.write((new Date(#{unixt.to_i * 1000})).strftime(#{fmt.dump}));
      </script>
      <noscript>
        #{Time.at(unixt.to_i).strftime(fmt)}
      </noscript>
   }
 end

Notice of caution: The above method uses a pretty tacky script/noscript scenario. Mike West has approached the situation better on his blog. You can use this code instead:

 def js_time( unixt, fmt = "%d %b %Y at %I:%M:%S %p" )
   %{
      <span class='PerfectTime' gmt_time='#{unixt.to_i}' format='#{fmt}'>
        #{Time.at(unixt.to_i).strftime(fmt)}
      </span>
   }
 end

This method takes a unix timestamp and writes out a script tag which will format the date according to the user’s locale. If no Javascript is available, time according to the server’s timezone will be reported.

Unity of Time Formatting

This is also dependant upon a Javascript version of Ruby’s own strftime.

 /* strftime formats */
 var strftime_funks = {
   zeropad: function( n ){ return n>9 ? n : '0'+n; },
   a: function(t) { return ['Sun','Mon','Tue','Wed',
      'Thu','Fri','Sat'][t.getDay()] },
   A: function(t) { return ['Sunday','Monday','Tuedsay',
      'Wednesday','Thursday','Friday','Saturday'][t.getDay()] },
   b: function(t) { return ['Jan','Feb','Mar','Apr',
      'May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'
      ][t.getMonth()] },
   B: function(t) { return ['January','February','March','April',
      'May','June', 'July','August','September','October',
      'November','December'][t.getMonth()] },
   c: function(t) { return t.toString() },
   d: function(t) { return this.zeropad(t.getDate()) },
   H: function(t) { return this.zeropad(t.getHours()) },
   I: function(t) { return this.zeropad((t.getHours() + 12) % 12) },
   m: function(t) { return this.zeropad(t.getMonth()+1) }, // month-1
   M: function(t) { return this.zeropad(t.getMinutes()) },
   p: function(t) { return this.H(t) < 12 ? 'AM' : 'PM'; },
   S: function(t) { return this.zeropad(t.getSeconds()) },
   w: function(t) { return t.getDay() }, // 0..6 == sun..sat
   y: function(t) { return this.zeropad(this.Y(t) % 100); },
   Y: function(t) { return t.getFullYear() },
  '%': function(t) { return '%' }
 };

 Date.prototype.strftime = function (fmt) {
    var t = this;
    for (var s in strftime_funks) {
        if (s.length == 1 )
            fmt = fmt.replace('%' + s, strftime_funks[s](t));
    }
    return fmt;
 };

Download the full strftime.js.

The Best?

Is this the best option? Is this even a good option? Obviously, the biggest weakness is whether your users’ clocks are right or not. And whether Javascript is on or not. With Hoodwink’d, users have to use Javascript. Even the MouseHole version requires it.

I buy ecmanaut’s argument completely, along with his recommendation for backing it all up with a timezone selector.

As you are pioneering this field of end-user usability, you may want to state that times and dates are indeed given in the visitor’s frame of reference, as people have generally come to expect to see times given in some random and hence typically fairly useless time zone. This can be seen as a not entirely bad reason for actually providing a time zone configuration option, should you want one. I would suggest defaulting it to “auto” or “local time” using the above method, though, as that is most likely what the user would want to see, anyway. This way, the configuration option actually doubles as documentation of what times the site shows, in a place a visitor is likely to look for it. To make it extra apparent that you render proper times, you might equip the page with the setting with a printout of present time, which the visitor will easily recognize as the time her computer clock shows (since they are in fact one and the same).

Okay, things were broken in Safari, which is missing JavaScript replace callback support. I’ve changed to a loop in the above code and also updated the strftime.js file.

said on 14 Jan 2006 at 19:58

I believe Typo uses this method.

said on 14 Jan 2006 at 21:36

That’s quite brilliant actually.

said on 14 Jan 2006 at 21:59

Agh, thank goodness. A sane fix for this timestamp thing. Thanks for bringing this to our attention~

said on 14 Jan 2006 at 22:07

Incidentally, that strftime implementation is quite beautiful.

said on 15 Jan 2006 at 07:04

That’s just beautiful.

Speaking about dates, though, there’s a bug in your comment field. At least for me, on Firefox 1.5, it says said on 00 Jan 2006. U, at least, am not living in some weirdo land where months have a day number zero. Since you just served us this brilliant solution for dates, I believe you’d have no problem fixing this as well.

said on 15 Jan 2006 at 07:47

It’s a wonderful idea, I’m amazed that nobody has thought of this earlier. It’s so much more useful most of the time than some arbitrary random timezone. And setting timezone settings on each website is a pain… what if you move from one timezone to another? You have to change all of them. And of course you forget one. And that’s the one that matters. This is exactly the sort of thing that’s best done on the client side, and that’s what javascript is for after all.

said on 15 Jan 2006 at 08:57

I’m not going to claim close familiarity with the Ruby strftime method, but I would believe that your regexp in the javascript strftime() implementation should lose the plus sign. And thanks for downstating the rantiness of my version to a level of mere pragmatism. :-)

said on 15 Jan 2006 at 10:25

Pan: I’ve just moved RedHanded over to this just now. Any better?

ecmanaut: Good and healed.

said on 15 Jan 2006 at 15:32

See my comment at ecmanaut’s. Also, if you’re really going for javascript time localization, please remove the AM/PM that I’m still seeing… We have a 24-hour clock in my time zone…

said on 15 Jan 2006 at 15:42

Safari starts to print function (str, .... :(

said on 15 Jan 2006 at 17:31

12 or 24 hour time isn’t time zone but locale bound; that is entirely another issue, and a data point which unfortunately doesn’t get exposed to browser javascript. It’s just the actual time reading we can present in visitor time, not the formatting of it.

said on 15 Jan 2006 at 18:19

I want to echo what jix said, just in case _why doesn’t follow it. When reading this site in Safari, the javascript for formatting dates is showing as text. That is, on all the comments posted it reads, “So_and_so said on function (str, p1, offset, s) { if …”

said on 15 Jan 2006 at 20:38

Hmm, I don’t really like the seconds in the time on Redhanded now. Are they needed?

Also I get a funny and very distracting flickering as the time updates. If you didn’t display seconds you could get away with updating it a lot less frequently…

said on 15 Jan 2006 at 23:38

soory Safari users, it was absence of the replace callback. I knew it was too good to be true!

I’ve updated strftime to use a plain, old loop.

said on 16 Jan 2006 at 13:02

It works now. I wouldn’t have noticed, but when you named your post “Showing perfect time”, I couldn’t resist looking for errors.

said on 18 Jan 2006 at 21:23

Hey why, excellent script! One thing: I() returns 00 for 00 instead of 12, and 12 returns 00. Here’s an alternative for those of us using AM/PM:

mil2reg: function(h) { return (h == 0 || h > 12) ? Math.abs(h – 12) : h }, I: function(t) { return this.mil2reg(t.getHours()) },

said on 09 Feb 2006 at 08:54

I did something like this for an ad-free world clock webpage—English world clock, French: Fuseaux Horaires—made in PHP with a dash of JavaScript. I think its best to do as much of this sort of thing as possible on the server end, and always have a backup in case JavaScript is not available, or the user has not set their clock correctly.

said on 10 Feb 2006 at 17:02

The Scooby project at OSAF has a complete JavaScript implemenation of Date.strftime, as well as Date.add and Date.diff is its date.js library.

Great work, BTW . I replaced our irritating string evals with anonymous functions based on how nice your stuff looked.

said on 12 Feb 2006 at 14:13

I love this concept.

I took some of the ideas in this post, and put together an unobtrusive and light JavaScript class that makes this entire process fairly transparent, and maintains the semantic quality of the HTML code (I’m simply not a fan of NOSCRIPT tags, nor of inline document.write).

The class is avaliable on my blog, here

Thanks for the inspiration!

said on 13 Feb 2006 at 15:55

Or this for %I, which is a little shorter and in one function:

I: function(t) { h = (t.getHours() + 12) % 12; return this.zeropad(h == 0 ? 12 : h) }

Why does strftime have to zero-pad 12-hour hours? I would be a fan of making %i mean the hours without zero-padding and perhaps something similar for the day of the month.

said on 13 Feb 2006 at 15:56

“12-hour hours” indeed!

said on 13 Feb 2006 at 19:08

[img-timeline]

said on 03 Mar 2006 at 07:02

There are several problems (well not really problems, but shortcomings) with this method:

1. As another user mentioned on ecmanaut’s blog, this does not handle the case when the time from one user (such as the timestamp on a forum post) is displayed to another user.

2. This does not handle the case of a user sending a time as form data. It would come in the user’s local time, but the server would not convert it.

3. This does not handle the case when you might be sending the user an e-mail or exporting XML or outputting data in some medium other than the web browser.

I think all of this taken together means that (other than some limited circumstances) one really has no choice except to handle timezones properly.

I do not mean to demean this article. It was very interesting and useful, but its application is limited. People should not think that this “solves” the timezone problem once and for all.

Comments are closed for this entry.