What you want to do is actually very straightforward. But, first let me congratulate you on using UTC as the "standard time" that your system operates in. This is the right way to go. Also, using a text based representation of this time for transfer between systems is better than using the epoch time since the representation of the epoch can vary from system to system.
Epoch time (in seconds), like a GMT or UTC time is unambiguous, it is a steady onward marching time - there isn't any jump forward or backward stuff - well except for leap seconds). Exactly what date/time is considered to be "zero" - the beginning of time varies from system to system. Jan 1, 1970 is a common value. You convert the UTC time to this system specific number and then convert that number into the local time.
Time::Local will already be on every system. The normal time functions would not have the precision implied by your time format. However, all you really need to do is figure out how the time zone change will affect the date and the hour - minutes,seconds and fraction of seconds will remain the same. So:
-convert date/hour into the system's "epoch" time using timegm().
-convert that epoch time into local time zone using localtime()
code is shown below...
I am in the PST time zone - this date/time was during PDST and the local time is -7 hours from UTC. If I change the month to 12 instead of 09- the time difference will be -8 hours and localtime() automatically adjusts for that. Note that both gmtime and localtime() handle leap years.
Anyway, your code doesn't need to know what time zone this code is actually running in or whether it is DST or not - localtime() will take care of that.
Note: I did have to consider some quirky historical stuff about the std I/F to the time functions - years are 1900 based and month numbering starts at zero instead of 1 because that makes it easier to use the month value as an index into some text describing the month. But this is easily handled once you know about it.
Update:I didn't talk about going in the other direction (local->UTC) or doing "time math". There is some weirdness about that..for example when we leap backwards, at say 2AM, the time 1:15AM will occur twice on that local day although these two occurrences of 1:15AM represent 2 different UTC times. And you have to decide which of the 2 possible UTC times you mean (or accept default of the earliest one). Similarly, when we leap forward, one hour of local times "go missing" and the day only has 23 local hours. Important to note is that UTC time always has 24 hour days. And there is a direct mapping between the systems idea of "epoch time" and UTC. Do duration calculations using UTC. Anyway if your application is sensitive to these special 23 and 25 hour local days, you have some thinking to do - that's true whether you use some module or not.
#!/usr/bin/perl -w
use strict;
use Time::Local; #needed for timegm()
my $t = '2010-09-06 15:06:53.512999'; #( it is UTC time )
my $newstring = UTC2LocalString($t);
print " UTC: $t\n";
print "Local: $newstring\n";
=PRINTS:
UTC: 2010-09-06 15:06:53.512999
Local: 2010-09-06 08:06:53.512999
=cut
sub UTC2LocalString
{
my $t = shift;
my ($datehour, $rest) = split(/:/,$t,2);
my ($year, $month, $day, $hour) =
$datehour =~ /(\d+)-(\d\d)-(\d\d)\s+(\d\d)/;
# proto: $time = timegm($sec,$min,$hour,$mday,$mon,$year);
my $epoch = timegm (0,0,$hour,$day,$month-1,$year);
# proto: ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
# localtime(time);
my ($lyear,$lmonth,$lday,$lhour,$isdst) =
(localtime($epoch))[5,4,3,2,-1];
$lyear += 1900; # year is 1900 based
$lmonth++; # month number is zero based
#print "isdst: $isdst\n"; #debug flag day-light-savings time
return ( sprintf("%04d-%02d-%02d %02d:%s",
$lyear,$lmonth,$lday,$lhour,$rest) );
}
|