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