This is a proof of concept for presenting datetime stamps on a website in the correct time for all users where correct means the moment in 4D it happened (according to their system clock, anyway).
For this to work we assume the datetime stamps in the DB records are normalized to UTC. The demo uses Template so perl can write the JS and Time::Piece for date objects b/c it's a convention when using Class::DBI (not used here but where I'll be applying this concept myself after breaking things into their proper constituents).
Eg, user in PST posts something at 1pm. The DB marks it as posted at 8pm (all times in DB are normalized to UTC). Then when the date is fetched for a webpage, it is wrapped in a JavaScript function to print it as local time. The user in EST now sees the time as 4pm which is really the time it was in EST when the original post was made.
Aren't dates fun? I hope I'm not breaching etiquette too much by posting a largely JavaScript solution to this problem but it's only really addressed by client side information. This was prompted by User's time.
# Declarations #================================================================= use warnings; no warnings 'uninitialized'; use strict; use CGI (); use Template; use Time::Piece; # Program proper #================================================================= my $template = Template->new(); my $utc_date_from_a_db = '2004-10-18 03:32:11'; my $data = { utc_date => Time::Piece->strptime($utc_date_from_a_db, "%Y-%m-%d %k:%M:%S"), }; print CGI::header(), CGI::start_html('Localized Date with JavaScript via Perl'); $template->process(\*DATA, $data); __END__ <script type="text/javascript"> Date.prototype.siteFormat = function() { var date = new Array ( this.getMonth() ,this.getDate() ,this.getFullYear() ); var seconds = this.getSeconds() > 10 ? this.getSeconds() : '0'+this. +getSeconds() var minutes = this.getMinutes() > 10 ? this.getMinutes() : '0'+this. +getMinutes() var hours = this.getHours() > 10 ? this.getHours() : '0'+this.getHou +rs() var time = new Array ( hours ,minutes ,seconds ); return date.join('/') + ' ' + time.join(':') }; // This isn't used here but it's useful enough to include Date.prototype.getMonthName = function() { var Months = new Array( 'January' ,'February' ,'March' ,'April' ,'May' ,'June' ,'July' ,'August' ,'September' ,'October' ,'November' ,'December' ); var month = this.getMonth(); return Months[month] || '' }; </script> <h3>Perl date object from DB: [% utc_date.mdy('/') %] [% utc_date.hms() %]</h3> <h3> [% date_parts = utc_date.datetime.split(' ') %] [% date = date_parts.0.split('\D') %] <script type="text/javascript"> document.write ('JavaScript adjusted for TZ: ') var now = new Date () var offset = Math.floor( now.getTimezoneOffset() / 60 ) var offset_milliseconds = offset * 60 * 60 * 1000 var utc_date = new Date( [% date.join(',') %] ) var local_time = utc_date.getTime() - offset_milliseconds var local_date = new Date ( local_time ) document.write( local_date.siteFormat() ) </script> <noscript> JavaScript is off so same, same: [% utc_date.mdy('/') %] [% utc_date.hms() %] UTC </noscript> </h3>
Also, you might find this additional snippet helpful for datetime normalization if you're using Class::DBI like I am. In a table with a 'created' field it automatically stamps the time to UTC when a new object (record) is created (MySQL version shown). You'll need to see the Class::DBI docs if you don't know them already.
my $utc_offset = abs( ( Date::Calc::Timezone() )[3] ); __PACKAGE__->set_sql(MakeNewObj => <<"SQL"); INSERT INTO __TABLE__ (created, \%s) VALUES (DATE_ADD(NOW(), INTERVAL $utc_offset HOUR), \%s) SQL
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |