perl/Git.pm: fix get_tz_offset to properly handle DST boundary cases

When passed a local time that was on the boundary of a DST change, get_tz_offset returned a GMT offset that was incorrect (off by one hour). This is because the time was converted to GMT and then back to a time stamp via timelocal() which cannot disambiguate boundary cases as noted in its documentation. Modify this algorithm, using an approach suggested in http://article.gmane.org/gmane.comp.version-control.git/213871 to first convert the timestamp in question to two broken down forms with localtime() and gmtime(), and then compute what timestamps these two broken down forms would represent in GMT (i.e. a timezone that does not have DST issues) by applying timegm() on them. The difference between the resulting timestamps is the timezone offset. This avoids the ambigious conversion and allows a correct time to be returned on every occassion. Signed-off-by: Ben Walton <bdwalton@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ben Walton committed Feb 9, 2013 at 21:46 UTC 75f7b5dfc47a9c764207559934b1c550423a243a
1 file changed +3 -3
perl/Git.pm
+3 -3
@@ -103,7 +103,7 @@ use Error qw(:try);
103 use Cwd qw(abs_path cwd);
104 use IPC::Open2 qw(open2);
105 use Fcntl qw(SEEK_SET SEEK_CUR);
106 -use Time::Local qw(timelocal);
106 +use Time::Local qw(timegm);
107 }
108
109
@@ -528,8 +528,8 @@ If TIME is not supplied, the current local time is used.
528 sub get_tz_offset {
529 # some systmes don't handle or mishandle %z, so be creative.
530 my $t = shift || time;
531 - my $gm = timelocal(gmtime($t));
532 - my $sign = qw( + + - )[ $t <=> $gm ];
531 + my $gm = timegm(localtime($t));
532 + my $sign = qw( + + - )[ $gm <=> $t ];
533 return sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
534 }
535