Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the DateTime-TimeZone CPAN distribution.

Report information
The Basics
Id: 26438
Status: resolved
Priority: 0/
Queue: DateTime-TimeZone

People
Owner: Nobody in particular
Requestors: isaacson [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.64
Fixed in: 0.66



Subject: /etc/localtime multiple symlinks
DateTime::TimeZone::Local::Unix uses the following logic to try to coerce a timezone name out of /etc/localtime: - if it's a symlink, do a readlink() and look for a timezone name in the result - otherwise, look for a matching file in /usr/share/zoneinfo In our environment, /etc/localtime is a symlink, but not directly to a file in the zoneinfo database. It's a symlink to an intermediate location on a network filesystem that lets us set the timezone centrally on large groups of servers in the same region. Unfortunately, the _Readlink function will only do one readlink() call. And if that doesn't work, it doesn't even attempt the _FindMatchingZoneinfoFile step. Two recommended changes: 1. Change _Readlink to resolve all symlinks. For example: use File::Basename qw(dirname basename); use File::Spec::Functions qw(rel2abs); use Cwd qw(abs_path); sub _Readlink { my $link = $_[1]; while (-l $link) { my $target = readlink $link; $link = rel2abs ($target, dirname $link); } return (abs_path (dirname $link) . basename $link); } 2. Just because /etc/localtime is a symlink doesn't mean the target has a recognizable timezone string in its name. If it is a symlink but _Readlink fails to yield a valid timezone name, you should still fall back on _FindMatchingZoneinfoFile before skipping to the next step. Either one of these changes would fix things in our environment, but for maximum portability, I think it would be best to do both. Thank you!
Erm, sorry, small bug in my _Readlink function. That should read: use File::Basename qw(dirname basename); use File::Spec::Functions qw(rel2abs catfile); use Cwd qw(abs_path); sub _Readlink { my $link = $_[1]; while (-l $link) { my $target = readlink $link; $link = rel2abs ($target, dirname $link); } return catfile (abs_path (dirname $link), basename $link); } Note that this can be done more efficiently using resolve_all from File::Spec::Link, but requiring another non-core module is probably not desirable.
Thanks for the quick turnaround! Sorry to be a pest, but Cwd::abs_path doesn't work on files until perl5.8. With perl5.6 and earlier, that function only works on directories: % perl5.6 -MCwd -le 'print Cwd::abs_path("/etc/localtime")' opendir(/etc/localtime/..): Not a directory at -e line 1 Hence the rather more convoluted version I proposed. Unfortunately, we still use perl5.6 quite heavily. (If you choose not to support it anymore, you should at least include a "use 5.008" at the top.) Also, it would still be nice to fall back on _FindMatchingZoneinfoFile if _Readlink fails. For example: my $real_name; if ( -l $lt_file ) { # The _Readlink sub exists so the test suite can mock it. $real_name = $class->_Readlink( $lt_file ); } else <------ { $real_name = $class->_FindMatchingZoneinfoFile( $lt_file ); } Replace that "else" with: if ( not defined $real_name ) Thanks for your help!
On Sun Apr 22 13:40:43 2007, ISAACSON wrote: Show quoted text
> Thanks for the quick turnaround! Sorry to be a pest, but Cwd::abs_path > doesn't work on files until perl5.8. With perl5.6 and earlier, that > function only works on directories: > > % perl5.6 -MCwd -le 'print Cwd::abs_path("/etc/localtime")' > opendir(/etc/localtime/..): Not a directory at -e line 1 > > Hence the rather more convoluted version I proposed. Unfortunately, we > still use perl5.6 quite heavily. (If you choose not to support it > anymore, you should at least include a "use 5.008" at the top.)
Cwd 3.x is available from CPAN, so it probably makes more sense to just require it, which I will do in today's release. Show quoted text
> Also, it would still be nice to fall back on _FindMatchingZoneinfoFile > if _Readlink fails. For example: > > my $real_name; > if ( -l $lt_file ) > { > # The _Readlink sub exists so the test suite can mock it. > $real_name = $class->_Readlink( $lt_file ); > } > else <------ > { > $real_name = $class->_FindMatchingZoneinfoFile( $lt_file ); > } > > Replace that "else" with: > > if ( not defined $real_name )
Done.