houseabsolute / DateTime-TimeZone

Time zone object base class and factory
https://metacpan.org/release/DateTime-TimeZone/
Other
9 stars 25 forks source link

`US/Indiana-Starke` Missing TimeZone from IANA List #51

Closed HoldOffHunger closed 2 years ago

HoldOffHunger commented 2 years ago

Hi, all,

I've been writing unit tests for various timezones, and I found that US/Indiana-Starke is not considered a valid timezone. It is listed here, on the IANA wiki page: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

It is an alias to the timezone America/Indiana/Knox, which is fine and supported by DateTime::TimeZone just fine. US/East-Indiana is also an alias (to America/Indiana/Indianapolis), but both the alias and the alias'd timezone are supported by DT::TZ, while Starke is missing.

Maybe I'm missing some magical geographical fact? Let me know, thank you.

autarch commented 2 years ago

I'm a bit confused. When you say it's "missing", I'm not sure from what. If you download the latest IANA tz release, 2021, and take a look in the backward file (for backwards compatibility), it has this line:

Link    America/Indiana/Knox    US/Indiana-Starke

This is what DT-TZ uses to generate its links.

HoldOffHunger commented 2 years ago

Ah, thanks for getting back so quick!

If I run the following...

print "$DateTime::TimeZone::VERSION\n";

print Dumper (
DateTime::TimeZone->all_names
);

I see I am on version 2.51, but I don't see -Starke in this list. And when I try to do timezone conversions, it seems to ignore setting of US/Indiana-Starke. Half of the other US/... one's I have tested with and all seem to work.

autarch commented 2 years ago

Ah, I see. In the future, please make sure that your initial bug report includes the code you ran, what your expectations were, and what you got instead.

The all_names method only includes names of actual zones, not link names from one zone to another. If you want the links, there's a links method. But in general, you should use the current name, not an older now-deprecated name. The links are there for backwards compatibility over time.

HoldOffHunger commented 2 years ago

In the future, please make sure that your initial bug report includes the code you ran, what your expectations were, and what you got instead.

Oops, sigh, you're right.

Okay, so, that solves the problem with all_names. The calculations are still incorrect.

US/Eastern −05:00 −04:00 US/Indiana-Starke −06:00 −05:00 (Source: The wiki link, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#KNOX )

So, converting US/Eastern to US/Indiana-Starke should be less by one hour. "5:00:00" Us/Eastern to US/Indiana-Starke is "5:00:00", when it should be "4:00:00".

Here is the code, with the broken case and a working-case:

use DateTime;

sub convertTimeZonesForTime {
        my ($args) = @_;

        my $time = $args->{time};
        my $date = $args->{date};
        my $datetime = $args->{datetime};

        if(!$time && !$date && $datetime) {
                ($date, $time) = split(' ', $datetime);
        }

        die 'Must pass a time value to convertTimeZonesForTime().' if !$time;

        my $timezone = $args->{timezone};       # AKA: This is the "to" timezone, as opposed to the "from" timezone (which defaults to Eastern/US)

        die 'Must pass a timezone to convertTimeZonesForTime().' if !$timezone;

        my $format = $args->{format} || '%H:%M:%S';

        my ($year, $month, $day) = map {int $_} split('-', $date);
        my ($hour, $minute, $second) = map {int $_} split(':', $time);

        $year ||= 1999 if !defined $year;
        $month ||= 1 if !defined $month;
        $day ||= 1 if !defined $day;
        $hour ||= 12 if !defined $hour;
        $minute ||= 30 if !defined $minute;
        $second ||= 0 if !defined $second;

        my $dt = DateTime->new(
                year=>$year,
                month=>$month,
                day=>$day,
                hour=>$hour,
                minute=>$minute,
                second=>$second,
                time_zone => 'US/Eastern',
        );
        my $formatter = new DateTime::Format::Strptime(pattern => $format);
        $dt->set_formatter($formatter);
        $dt->set_time_zone($timezone);

        return "$dt";
}

print convertTimeZonesForTime({
    'datetime'=>'2000-01-01 21:00:00',
    timezone=>'US/Indiana-Starke',
});

# Expected output: 20:00:00

print("\n\n");

print convertTimeZonesForTime({
    'datetime'=>'2000-01-01 21:00:00',
    timezone=>'US/Mountain',
});
autarch commented 2 years ago

This is a quite long code example. Can you boil this down to maybe 2-5 lines of code?

HoldOffHunger commented 2 years ago

Of course:

my $dt = DateTime->new(
    year=>1999,
    month=>1,
    day=>1,
    hour=>5,
    minute=>0,
    second=>0,
    time_zone => 'US/Eastern',
);
my $formatter = new DateTime::Format::Strptime(pattern => '%H:%M:%S');
$dt->set_formatter($formatter);
$dt->set_time_zone('US/Indiana-Starke');

print "$dt";

Output is 5:00, but US/Eastern and Us/Indiana-Starke aren't the same TZ's.

autarch commented 2 years ago

In 1999, America/Indiana/Knox (which US/Indiana-Starke is a link to) was in perma-Eastern time with no DST shift, and was at UTC-5. See this site for a history of time changes there. And see the history America/New_York as well. On 1999-01-01 it was not in DST, and so was also at UTC-5.

So this all looks right to me.