I wrote:
>Attached is a patch that revises timezone name parsing in DT::TZ.

Except that I forgot to attach it.  D'oh.  Really attached this time.

After applying the patch you'll need to do a "./tools/parse_olson --clean".

-zefram
diff -ur dttz-0.59/Build.PL dttz-mod0/Build.PL
--- dttz-0.59/Build.PL  2007-01-18 15:57:56.000000000 +0000
+++ dttz-mod0/Build.PL  2007-02-10 18:04:20.000000000 +0000
@@ -27,6 +27,8 @@
                     requires    => { 'Params::Validate' => 0.72,
                                      'Class::Singleton' => 1.03,
                                      'Pod::Man'    => 1.14,
+                                     'DateTime::TimeZone::SystemV' => 0.000,
+                                     'DateTime::TimeZone::Tzfile' => 0.000,
                                    },
                     build_requires => { 'Module::Build' => 0 },
                    sign        => 1,
diff -ur dttz-0.59/lib/DateTime/TimeZone/Local.pm 
dttz-mod0/lib/DateTime/TimeZone/Local.pm
--- dttz-0.59/lib/DateTime/TimeZone/Local.pm    2007-01-18 15:57:56.000000000 
+0000
+++ dttz-mod0/lib/DateTime/TimeZone/Local.pm    2007-02-11 11:01:58.246367883 
+0000
@@ -237,7 +237,7 @@
     return 0 unless defined $_[0];
     return 0 if $_[0] eq 'local';
 
-    return $_[0] =~ m,^[\w/\-\+]+$, ? 1 : 0;
+    return 1;
 }
 
 
diff -ur dttz-0.59/lib/DateTime/TimeZone/OlsonDB.pm 
dttz-mod0/lib/DateTime/TimeZone/OlsonDB.pm
--- dttz-0.59/lib/DateTime/TimeZone/OlsonDB.pm  2007-01-18 15:57:56.000000000 
+0000
+++ dttz-mod0/lib/DateTime/TimeZone/OlsonDB.pm  2007-02-11 10:48:55.524500689 
+0000
@@ -111,8 +111,6 @@
         $name = shift @items;
     }
 
-    return if $name =~ /[WCME]ET/ && ! $self->{backwards_compat};
-
     @obs{ qw( gmtoff rules format until ) } = @items;
 
     if ( $obs{rules} =~ /\d\d?:\d\d/ )
diff -ur dttz-0.59/lib/DateTime/TimeZone.pm dttz-mod0/lib/DateTime/TimeZone.pm
--- dttz-0.59/lib/DateTime/TimeZone.pm  2007-01-18 15:57:56.000000000 +0000
+++ dttz-mod0/lib/DateTime/TimeZone.pm  2007-02-11 11:00:36.820591025 +0000
@@ -6,10 +6,6 @@
 $VERSION = '0.59';
 
 use DateTime::TimeZoneCatalog;
-use DateTime::TimeZone::Floating;
-use DateTime::TimeZone::Local;
-use DateTime::TimeZone::OffsetOnly;
-use DateTime::TimeZone::UTC;
 use Params::Validate qw( validate validate_pos SCALAR ARRAYREF BOOLEAN );
 
 use constant INFINITY     =>       100 ** 1000 ;
@@ -24,8 +20,6 @@
 use constant IS_DST      => 5;
 use constant SHORT_NAME  => 6;
 
-my %SpecialName = map { $_ => 1 } qw( EST MST HST EST5EDT CST6CDT MST7MDT 
PST8PDT );
-
 sub new
 {
     my $class = shift;
@@ -33,66 +27,81 @@
                       { name => { type => SCALAR } },
                     );
 
-    if ( exists $DateTime::TimeZone::LINKS{ $p{name} } )
+    if ( $p{name} eq 'local' )
     {
-        $p{name} = $DateTime::TimeZone::LINKS{ $p{name} };
+        require DateTime::TimeZone::Local;
+        return DateTime::TimeZone::Local::local_time_zone();
     }
-    elsif ( exists $DateTime::TimeZone::LINKS{ uc $p{name} } )
+
+    my $name = $p{name};
+    $name =~ s/\A://;
+
+    if ( exists $DateTime::TimeZone::LINKS{ $name } )
     {
-        $p{name} = $DateTime::TimeZone::LINKS{ uc $p{name} };
+        $name = $DateTime::TimeZone::LINKS{ $name };
     }
-
-    unless ( $p{name} =~ m,/,
-             || $SpecialName{ $p{name} }
-           )
+    elsif ( exists $DateTime::TimeZone::LINKS{ uc $name } )
     {
-        if ( $p{name} eq 'floating' )
-        {
-            return DateTime::TimeZone::Floating->new;
-        }
-
-        if ( $p{name} eq 'local' )
-        {
-            return DateTime::TimeZone::Local::local_time_zone();
-        }
-
-        if ( $p{name} eq 'UTC' || $p{name} eq 'Z' )
-        {
-            return DateTime::TimeZone::UTC->new;
-        }
-
-        return DateTime::TimeZone::OffsetOnly->new( offset => $p{name} );
+        $name = $DateTime::TimeZone::LINKS{ uc $name };
     }
 
-    my $subclass = $p{name};
-    $subclass =~ s/-/_/g;
-    $subclass =~ s{/}{::}g;
-    my $real_class = "DateTime::TimeZone::$subclass";
-
-    die "The timezone '$p{name}' in an invalid name.\n"
-        unless $real_class =~ /^\w+(::\w+)*$/;
-
-    unless ( $real_class->can('instance') )
+    if ( $name eq 'UTC' || $name eq 'Z' )
     {
-        eval "require $real_class";
+        require DateTime::TimeZone::UTC;
+        return DateTime::TimeZone::UTC->new;
+    }
+    elsif ( $name =~ m#\A[-\w]+(?:/[-\w]+)+\z# )
+    {
+        my $subclass = $name;
+        $subclass =~ s/-/_/g;
+        $subclass =~ s{/}{::}g;
+        my $real_class = "DateTime::TimeZone::$subclass";
 
-        if ($@)
+        unless ( $real_class->can('instance') )
         {
-            my $regex = join '.', split /::/, $real_class;
-            $regex .= '\\.pm';
+            eval "require $real_class";
 
-            if ( $@ =~ /^Can't locate $regex/i )
-            {
-                die "The timezone '$p{name}' could not be loaded, or is an 
invalid name.\n";
-            }
-            else
+            if ($@)
             {
-                die $@;
+                my $regex = join '.', split /::/, $real_class;
+                $regex .= '\\.pm';
+
+                if ( $@ =~ /^Can't locate $regex/i )
+                {
+                    die "The timezone '$name' could not be loaded, or is an 
invalid name.\n";
+                }
+                else
+                {
+                    die $@;
+                }
             }
         }
+        return $real_class->instance( name => $name, is_olson => 1 );
+    }
+    elsif ( $name =~ /\A(?:[A-Za-z]{3,}|\<[-+0-9A-Za-z]{3,}\>)[-+]?\d/ )
+    {
+        require DateTime::TimeZone::SystemV;
+        return DateTime::TimeZone::SystemV->new( $name );
+    }
+    elsif ( $name =~ m#\A/# )
+    {
+        require DateTime::TimeZone::Tzfile;
+        return DateTime::TimeZone::Tzfile->new( $name );
+    }
+    elsif ( $name =~ /\A[-+]?(?:\d\d?:\d\d(?::\d\d)?|\d{4}(?:\d\d)?)\z/ )
+    {
+        require DateTime::TimeZone::OffsetOnly;
+        return DateTime::TimeZone::OffsetOnly->new( offset => $name );
+    }
+    elsif ( $name eq 'floating' )
+    {
+        require DateTime::TimeZone::Floating;
+        return DateTime::TimeZone::Floating->new;
+    }
+    else
+    {
+        die "The timezone name '$name' was not understood.\n";
     }
-
-    return $real_class->instance( name => $p{name}, is_olson => 1 );
 }
 
 sub _init
@@ -523,29 +532,71 @@
 
 =item * new( name => $tz_name )
 
-Given a valid time zone name, this method returns a new time zone
-blessed into the appropriate subclass.  Subclasses are named for the
-given time zone, so that the time zone "America/Chicago" is the
-DateTime::TimeZone::America::Chicago class.
-
-If the name given is a "link" name in the Olson database, the object
-created may have a different name.  For example, there is a link from
-the old "EST5EDT" name to "America/New_York".
+Parses the given timezone name, and returns a timezone object representing
+the appropriate zone.  The following types of name are understood:
+
+=over
+
+=item *
+
+If the name parameter is "local", then the module attempts to determine
+the local time zone for the system.  This is described in more detail
+below.
+
+=item *
+
+If the name begins with a ":" character, that character is dropped and the
+rest of the name is parsed without it.  This is for compatibility with
+parsing of the B<TZ> environment variable on many systems.  The special
+meaning of "local" does not apply to ":local".
+
+=item *
+
+If the name is an alias established by L<DateTime::TimeZone::Alias>,
+or a "link" in the Olson database, or one of a small number of built-in
+aliases, then the target of the alias is parsed instead.  An alias name
+that is all uppercase will be recognised in any case, but any other alias
+name must be given exactly.  The target of an alias is not itself subject
+to ":" removal or alias interpretation, and "local" is not a valid target.
+
+=item *
 
-There are also several special values that can be given as names.
+If the name is "UTC" or "Z", then a C<DateTime::TimeZone::UTC>
+object is returned.
+
+=item *
+
+If the name looks like a multipart name in the Olson database,
+an appropriate subclass is used.  For example, the time zone
+"America/Chicago" is the DateTime::TimeZone::America::Chicago class.
+
+=item *
+
+If the name looks like a System V style timezone string,
+including one of the POSIX extended form, it is parsed as such by
+C<DateTime::TimeZone::SystemV>.
+
+=item *
+
+If the name begins with a "/" character, it is interpreted as the name
+of a L<tzfile(5)> file, which is parsed by C<DateTime::TimeZone::Tzfile>.
+
+=item *
 
-If the "name" parameter is "floating", then a
+If the name looks like a fixed offset in hours and minutes
+(and optional seconds), it is converted to a number, and a
+C<DateTime::TimeZone::OffsetOnly> object is returned.
+
+=item *
+
+If the name is "floating", then a
 C<DateTime::TimeZone::Floating> object is returned.  A floating time
 zone does have I<any> offset, and is always the same time.  This is
 useful for calendaring applications, which may need to specify that a
 given event happens at the same I<local> time, regardless of where it
 occurs.  See RFC 2445 for more details.
 
-If the "name" parameter is "UTC", then a C<DateTime::TimeZone::UTC>
-object is returned.
-
-If the "name" is an offset string, it is converted to a number, and a
-C<DateTime::TimeZone::OffsetOnly> object is returned.
+=back
 
 =back
 
diff -ur dttz-0.59/tools/parse_olson dttz-mod0/tools/parse_olson
--- dttz-0.59/tools/parse_olson 2007-01-18 15:57:56.000000000 +0000
+++ dttz-mod0/tools/parse_olson 2007-02-11 10:54:42.960770193 +0000
@@ -151,6 +151,7 @@
 
     foreach my $zone_name ( sort $odb->zone_names )
     {
+        next unless $zone_name =~ m{/};
         if ( $opts{name} )
         {
             next unless $zone_name eq $opts{name};
@@ -161,18 +162,11 @@
 
         my $name;
         my @dir;
-        if ( $zone_name =~ m{/} )
-        {
-            my $category;
-            ( $category, $name ) = split /\//, $zone_name, 2;
-            push @{ $categories{$category} }, $name;
+        my $category;
+        ( $category, $name ) = split /\//, $zone_name, 2;
+        push @{ $categories{$category} }, $name;
 
-            ($dir[0] = $category) =~ tr/-/_/;
-        }
-        else
-        {
-            $name = $zone_name;
-        }
+        ($dir[0] = $category) =~ tr/-/_/;
 
         (my $outfile1 = $name) =~ tr/-/_/;
 
@@ -377,27 +371,12 @@
 
 sub clean_links
 {
-    # override some links and add others
-    %links =
-        ( %links,
-          'Etc/GMT'       => 'UTC',
-          'Etc/GMT+0'     => 'UTC',
-          'Etc/Universal' => 'UTC',
-          'Etc/UCT'       => 'UTC',
-          'Etc/UTC'       => 'UTC',
-          'Etc/Zulu'      => 'UTC',
-          'GMT0'          => 'UTC',
-          'GMT'           => 'UTC',
-          'AKST9AKDT'     => 'America/Anchorage',
-          'JST-9'         => 'Asia/Tokyo',
-        );
-
-    delete $links{UTC};
-
-    # This links to America/Indiana in the "backward" file
-    delete $links{EST};
-    # And this one is for America/Phoenix
-    delete $links{MST};
+    # Remove links that look like System V timezone strings.  (We do
+    # System V stuff properly.)
+    foreach ( keys %links )
+    {
+        delete $links{$_} if /\A[A-Za-z]+[-+]?\d/;
+    }
 
     # Some links resolve to other links - chase them down until they point
     # to a real zone.
@@ -408,6 +387,32 @@
             $links{$k} = $links{ $links{$k} };
         }
     }
+
+    # Anything linking to Etc/UTC, Etc/UCT, or Etc/GMT we want to link to
+    # our UTC.
+    foreach ( keys %links )
+    {
+        $links{$_} = 'UTC' if $links{$_} =~ m#\AEtc/(?:U(?:TC|CT)|GMT)\z#;
+    }
+
+    # The UTC-equivalent zones themselves need to be links to our UTC.
+    # We also support some other Etc/ links, even though we don't provide
+    # the full set of Etc/ names.
+    foreach (qw(Etc/UTC Etc/UCT Etc/GMT Etc/GMT+0 Etc/Universal Etc/Zulu GMT))
+    {
+        $links{$_} = 'UTC'
+    }
+
+    # UTC itself must not be a link: for us it's the canonical name of a zone.
+    delete $links{UTC};
+
+    # These three names are first-class zones in the Olson database, but as
+    # they're just fixed offsets we can handle them as links to System V
+    # timezone strings.  We don't support the other single-part non-SysV
+    # timezone names (the European ones).
+    $links{EST} = 'EST5';
+    $links{MST} = 'MST7';
+    $links{HST} = 'HST10';
 }
 
 sub make_catalog_pm

Reply via email to