mod_perl 2 has an annoying…feature. Because the system environ
struct is not thread safe, mod_perl’s perl-script
handler unties the %ENV
hash from the actual environment. That means, anything that uses the C getenv
/setenv
/unsetenv
functions to read the environment will not see changes that were made to %ENV
.
An obvious example is Perl’s localtime
function. It actually calls the system localtime
function, which uses the C getenv
to check the current value of the timezone environment variable TZ
. If you try to change the timezone in a mod_perl2 program by assigning to $ENV{TZ}
, localtime
won’t know it.
The solution is to use the Env::C
module and it’s getenv
/setenv
/unsetenv
wrappers. It works fine, but it’s a bit cumbersome. But a simple module, loaded at server-startup time, can wrap the system localtime
in a function that takes care of the environment.
package Apache2::Localtime; use Env::C; use Exporter; use strict; our @ISA = qw(Exporter); our @EXPORT = qw(localtime); sub import { my $class = shift; $class->export('CORE::GLOBAL', 'localtime'); } sub localtime { my $time = shift || time; return localtime($time) unless $ENV{TZ}; my $orig_tz = Env::C::getenv('TZ'); Env::C::setenv('TZ', $ENV{TZ}, 1); my(@ret, $ret); if(wantarray) { @ret = CORE::localtime($time); } else { $ret = CORE::localtime($time); } if(defined $orig_tz) { Env::C::setenv('TZ', $orig_tz, 1); } else { Env::C::unsetenv('TZ'); } return wantarray ? @ret : $ret; } 1;
Put that in your @INC
path at Apache2/Localtime.pm
and then add use Apache2::Localtime
to a PerlRequire .../initialize.pl
script or something similar. The new function should override the built-in localtime
and keep your timeonzes in sync.
The code was mostly taken from here.