Showing Perfect Time #
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.
jlong
I believe Typo uses this method.
PJ
That’s quite brilliant actually.
MenTaLguY
Agh, thank goodness. A sane fix for this timestamp thing. Thanks for bringing this to our attention~
MenTaLguY
Incidentally, that strftime implementation is quite beautiful.
Pan
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.
crzwdjk
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.
ecmanaut
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. :-)
why
Pan: I’ve just moved RedHanded over to this just now. Any better?
ecmanaut: Good and healed.
Danny
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…
jix
Safari starts to print function (str, .... :(
ecmanaut
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.
placey
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 …”
MenTaLguY
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…
why
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.
Pan
It works now. I wouldn’t have noticed, but when you named your post “Showing perfect time”, I couldn’t resist looking for errors.
skirch
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()) },
Eoin
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.
mde
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.
Mike West
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!
mdaines
Or this for %I, which is a little shorter and in one function:
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.
mdaines
“12-hour hours” indeed!
nick
[img-timeline]
pjstadig
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.