Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

hello monks!
I want to set the timezone in windows, so i can use localtime() according to it. This script works fine in linux, but in windows localtime() not honors TZ variable.
$ENV{TZ}= "GMT -3"; print "GMT -3 ",localtime(),$/;
It uses the timezone setted by the OS, I want to change it from my script. Thanks.

Replies are listed 'Best First'.
Re: setting timezone in windows
by BrowserUk (Patriarch) on Jan 19, 2006 at 20:40 UTC

    Seems to be a limitation of Perl that it only honours the setting at startup. If you change it within your script and spawn a child, then the spawned Perl sees and honours the change.

    P:\test>perl -le"print scalar localtime; $ENV{TZ}='PST8PDT'; print sca +lar localtime; system 'perl -le \"print scalar localtime\"' " Thu Jan 19 19:52:12 2006 Thu Jan 19 19:52:12 2006 Thu Jan 19 11:52:12 2006

    According the CRT docs, it has an equivalent of the POSIX tzset() call (_tzset), but neither the tzset() exported by the POSIX module, nor calling the CRT _tzset() after having set the TZ within the program, has any affect upon the times returned by localtime within the current process.

    However, running this

    #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <time.h> DWORD main(int argc, char *argv[]) { struct tm *newtime; char am_pm[] = "AM"; __time64_t long_time; _putenv( argv[1] ); _time64( &long_time ); /* Get time as long integer. + */ newtime = _localtime64( &long_time ); /* Convert to local time. */ if( newtime->tm_hour > 12 ) /* Set up extension. */ strcpy( am_pm, "PM" ); if( newtime->tm_hour > 12 ) /* Convert from 24-hour */ newtime->tm_hour -= 12; /* to 12-hour clock. */ if( newtime->tm_hour == 0 ) /*Set hour to 12 if midnight. * +/ newtime->tm_hour = 12; printf( "%.19s %s\n", asctime( newtime ), am_pm ); return 0; }

    seems to do the appropriate thing,

    P:\test>ltime TZ=PST8PDT Thu Jan 19 12:20:55 AM

    so it seems Perl is not doing so under Win32.

    Theoretically, you could do the same steps shown in the C code above from within your perl script using Win32::API to import the CRT functions directly from the MS CRT DLL, but it is simpler to conditionally set the TZ and then exec yourself like this:

    #! perl -slw use strict; unless( $ENV{TZ} ) { $ENV{TZ} = 'PST8PDT'; exec "$^X $0"; } print scalar localtime; __END__ P:\test>524251 P:\test>Thu Jan 19 12:32:30 2006

    but that's not so useful if you want to change the TZ many times. In that case it's probably easier to just spawn a perl one-liner and retrieve the values that way:

    #! perl -slw use strict; print "default:" . localtime(); $ENV{TZ} = 'GST1GDT'; print `perl -le"print 'Germany: ' . localtime()"`; $ENV{TZ} = 'PST8PDT'; print `perl -le"print 'Pacific: ' . localtime()"`; __END__ P:\test>524251-2 default:Thu Jan 19 20:41:24 2006 Germany: Thu Jan 19 19:41:24 2006 Pacific: Thu Jan 19 12:41:24 2006

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Me again, now logged (I was in other machine and I din't remember my passwd).

      What I have finally do was to override the localtime method so it honors the TZ env variable.

      package myModule::Localtime; use strict; require Exporter; use vars qw(@ISA @EXPORT); @ISA = qw(Exporter); @EXPORT = qw(localtime timelocal); sub localtime{ my $time = defined $_[0] ? shift : time(); return CORE::localtime($time) if $^O ne 'MSWin32'; my $offset = 0; if(defined $ENV{TZ}){ $offset = (split /\s+/,$ENV{TZ})[1] || 0; } return gmtime($time - ($offset*60*60) ); }
      Then in my script I use:
      use myModule::Localtime; $ENV{TZ}="GMT -2"; print "localtime -2:".localtime($time).$/;
      and woks fine.

      The only promblem is that it don't recognizes timezones like America/Argentina/Buenos_Aires. Timzeones has to be acoording \w+\s+\d+, but this could be fixed easily.

      Thanks a lot for yours answers.
      Nicolás.

Re: setting timezone in windows
by svenXY (Deacon) on Jan 19, 2006 at 15:47 UTC
    Hi,
    >perl -e "print scalar localtime()" Thu Jan 19 16:45:15 2006 >perl -e "print scalar localtime(time() - (3*60*60))" Thu Jan 19 13:45:23 2006

    Regards,
    svenXY
Re: setting timezone in windows
by ww (Archbishop) on Jan 19, 2006 at 15:50 UTC
    Oops. Bad on me; somehow convinced myself - despite a clear-enough question - that OP wanted to set system time:
    This is really a question for a w32 forum; note that svenXY's method gives a conversion... but does NOT set system TZ.
    However, if setting TZ is an acceptable alternate:

    Diligent search here (or with ppm, if you're using Active State) for WIN::API may help you.

    or, otherwise,

    ppm's WIN32API-File-Time may (I have not checked it) also provide some guidance/clues.

      Me again...

      localtime is a built in perl function, I'm trying to modify its behaviour, so I think this question is a valid Perl question.Neverthless, maybe I'd have to ask first to ActiveState...

      Substract a constant to time() every time I call it IMHO it's not the answer. It's not the porpouse of localtime, if it were we only need gmtime.

      WIN32API-File-Time is related to file's times. Not work here.

      The only way to change the behaviour is to set from the DOS shell the TZ variable, that is:

      > set TZ=GTM 3

      and then run the script. Setting it by system("set TZ=..") doesn't seems to work neigther.

      I think it's a fail in the localtime implementation that only check TZ before the interpreter compile the code (setting TZ by system or %ENV in a BEGIN block doesn't work).

      Sorry, I'm know that win$ sucks. It's the real problem here.I'll try to solve it for a little while more before give up.

      Thanks for the answers.