1 package DateTime::TimeZone::Local::Unix;
6 use base 'DateTime::TimeZone::Local';
20 sub EnvVars { return 'TZ' }
26 my $lt_file = '/etc/localtime';
28 return unless -r $lt_file && -s _;
33 # The _Readlink sub exists so the test suite can mock it.
34 $real_name = $class->_Readlink( $lt_file );
37 $real_name ||= $class->_FindMatchingZoneinfoFile( $lt_file );
39 if ( defined $real_name )
41 my ( $vol, $dirs, $file ) = File::Spec->splitpath( $real_name );
44 grep { defined && length } File::Spec->splitdir( $dirs ), $file;
46 foreach my $x ( reverse 0..$#parts )
50 join '/', @parts[$x..$#parts] :
58 $tz = eval { DateTime::TimeZone->new( name => $name ) };
71 # Using abs_path will resolve multiple levels of link indirection,
72 # whereas readlink just follows the link to the next target.
73 return Cwd::abs_path($link);
76 # for systems where /etc/localtime is a copy of a zoneinfo file
77 sub _FindMatchingZoneinfoFile
80 my $file_to_match = shift;
82 return unless -d '/usr/share/zoneinfo';
84 require File::Basename;
85 require File::Compare;
88 my $size = -s $file_to_match;
100 if ( ! defined $real_name
104 # This fixes RT 24026 - apparently such a
105 # file exists on FreeBSD and it can cause a
107 && File::Basename::basename($_) ne 'posixrules'
108 && File::Compare::compare( $_, $file_to_match ) == 0
113 # File::Find has no mechanism for bailing in the
120 '/usr/share/zoneinfo',
126 return $real_name if ref $@ && $@->{found};
135 my $tz_file = '/etc/timezone';
137 return unless -f $tz_file && -r _;
141 or die "Cannot read $tz_file: $!";
142 my $name = join '', <TZ>;
145 $name =~ s/^\s+|\s+$//g;
147 return unless $class->_IsValidName($name);
151 return eval { DateTime::TimeZone->new( name => $name ) };
158 my $tz_file = '/etc/TIMEZONE';
160 return unless -f $tz_file && -r _;
164 or die "Cannot read $tz_file: $!";
167 while ( defined( $name = <TZ> ) )
169 if ( $name =~ /\A\s*TZ\s*=\s*(\S+)/ )
178 return unless $class->_IsValidName($name);
182 return eval { DateTime::TimeZone->new( name => $name ) };
186 sub FromEtcSysconfigClock
190 return unless -r "/etc/sysconfig/clock" && -f _;
192 my $name = $class->_ReadEtcSysconfigClock();
194 return unless $class->_IsValidName($name);
198 return eval { DateTime::TimeZone->new( name => $name ) };
201 # this is a sparate function so that it can be overridden in the test
203 sub _ReadEtcSysconfigClock
208 open CLOCK, '</etc/sysconfig/clock'
209 or die "Cannot read /etc/sysconfig/clock: $!";
214 return $1 if /^(?:TIME)?ZONE="([^"]+)"/;
218 sub FromEtcDefaultInit
222 return unless -r "/etc/default/init" && -f _;
224 my $name = $class->_ReadEtcDefaultInit();
226 return unless $class->_IsValidName($name);
230 return eval { DateTime::TimeZone->new( name => $name ) };
233 # this is a separate function so that it can be overridden in the test
235 sub _ReadEtcDefaultInit
240 open INIT, '</etc/default/init'
241 or die "Cannot read /etc/default/init: $!";
246 return $1 if /^TZ=(.+)/;
257 DateTime::TimeZone::Local::Unix - Determine the local system's time zone on Unix
261 my $tz = DateTime::TimeZone->new( name => 'local' );
263 my $tz = DateTime::TimeZone::Local->TimeZone();
267 This module provides methods for determining the local time zone on a
270 =head1 HOW THE TIME ZONE IS DETERMINED
272 This class tries the following methods of determining the local time
279 It checks C<< $ENV{TZ} >> for a valid time zone name.
281 =item * F</etc/localtime>
283 If this file is a symlink to an Olson database time zone file (usually
284 in F</usr/share/zoneinfo>) then it uses the target file's path name to
285 determine the time zone name. For example, if the path is
286 F</usr/share/zoneinfo/America/Chicago>, the time zone is
289 Some systems just copy the relevant file to F</etc/localtime> instead
290 of making a symlink. In this case, we look in F</usr/share/zoneinfo>
291 for a file that has the same size and content as F</etc/localtime> to
292 determine the local time zone.
294 =item * F</etc/timezone>
296 If this file exists, it is read and its contents are used as a time
299 =item * F</etc/TIMEZONE>
301 If this file exists, it is opened and we look for a line starting like
302 "TZ = ...". If this is found, it should indicate a time zone name.
304 =item * F</etc/sysconfig/clock>
306 If this file exists, it is opened and we look for a line starting like
307 "TIMEZONE = ..." or "ZONE = ...". If this is found, it should indicate
310 =item * F</etc/default/init>
312 If this file exists, it is opened and we look for a line starting like
313 "TZ=...". If this is found, it should indicate a time zone name.
319 Dave Rolsky, <autarch@urth.org>
321 =head1 COPYRIGHT & LICENSE
323 Copyright (c) 2003-2008 David Rolsky. All rights reserved. This
324 program is free software; you can redistribute it and/or modify it
325 under the same terms as Perl itself.
327 The full text of the license can be found in the LICENSE file included