views:

359

answers:

3

I need to get the UTC offset of the current time zone in Perl in a cross platform (Windows and various flavors of Unix) way. It should meet this format:

zzzzzz, which represents ±hh:mm in relation to UTC

It looks like I should be able to get it via strftime(), but it doesn't appear to be consistent.

Unix:

Input: perl -MPOSIX -e "print strftime(\"%z\", localtime());"
Output: -0700

Windows:

Input: perl -MPOSIX -e "print strftime(\"%z\", localtime());"
Output: Mountain Standard Time

While it appears that Unix is giving me what I want (or at least something close), Windows is not. I'm pretty sure I can do it with Date::Time or similar, but I'd really like to not have any dependencies that I can't guarantee a user will have due to our wide install base.

Am I missing something obvious here? Thanks in advance.

A: 

A portable way is to compare the output of localtime with gmtime

    $t = time;
    @a = localtime($t);
    @b = gmtime($t);

    $hh = $a[2] - $b[2];
    $mm = $a[1] - $b[1];
    # in the unlikely event that localtime and gmtime are in different years
    if ($a[5]*366+$a[4]*31+$a[3] > $b[5]*366+$b[4]*31+$b[3]) {
      $hh += 24;
    } elsif ($a[5]*366+$a[4]*31+$a[3] < $b[5]*366+$b[4]*31+$b[3]) {
      $hh -= 24;
    }
    if ($hh < 0 && $mm > 0) {
      $hh++;
      $mm = 60-$mm;
    }
    printf "%+03d:%02d\n", $hh, $mm;

Someone pointing out that this is already implemented in a module somewhere in 5, 4, 3, ...

mobrule
Can you explain the calculation in the if statment -- why multiply the year by the number of days in a leap year? why Month * 31? Why add the day? Thanks.
Kevin Friedheim
@Kevin Friedman - it is a test to see if `@a[5,4,3]` and `@b[5,4,3]` represent the same day. I combine year, month, and day into a single number so I can get by with one comparison instead of three. You could use larger numbers than 366 and 31 and you would still get the right result.
mobrule
A: 

"I'd really like to not have any dependencies that I can't guarantee a user will have due to our wide install base"

How about including a custom copy of Date::Time (we'll call it My::Date::Time) in your installation? For example,

use lib 'my-module/dependencies/';

use My::Date::Time;
molecules
+2  A: 

Time::Local should do the trick

use Time::Local;
@t = localtime(time);
$gmt_offset_in_seconds = timegm(@t) - timelocal(@t);
mobrule
`Time::Local` is a core module in all Perl 5 distributions.
mobrule
This is a great solution; only gave it a rudimentary test, but it seems to work just as it should. Thanks.
Morinar