Subject: | DateTime: Bugs in documentation and strftime |
#!/usr/local/bin/perl
use strict;
use warnings;
use DateTime;
my $dt = DateTime->new(year => 2004, month => 8, day => 16, hour => 15,
minute => 30
, nanosecond => 123456789, locale => 'en');
# Should print '%{day_name}', prints '30onday'!
print $dt->strftime('%%{day_name}%n');
# Should print '%6N', prints '123456'
print $dt->strftime('%%6N%n');
__END__
The included patch fixes some bugs in the documentation. It does
not fix the strftime bugs. Rather, here are some propositions
to fix strftime.
The problem is that the string is scanned three times and the 2nd
scan is done after the first substitution, the 3rd scan is done
after the first and second substitutions.
$f =~ s/%{(\w+)}/1st subs/sgex;
# regex from Date::Format - thanks Graham!
$f =~ s/%([%a-zA-Z])/2nd subst/sgex;
# %3N
$f =~ s/%(\d+)N/3rd subst/sgex;
There are three ways to fix it. The first comes from
DT::C::FrenchRevolutionary
the recently released version 0.07, which deals with
C<%{method}> specifiers. I have simplified it to discard the
specifiers like C<%EY> and C<%Oj> and I have not introduced
nanosecond support.
$f =~ s/
%([%a-zA-Z]) # $1 stores plain specifier (possibly "%%")
| %{(\w+)} # $2 stores method name
/
$2 ? ($self->can($2) ? $self->$2() : "\%{$2}")
: ($formats{$1} ? $formats{$1}->($self) : $1)
/sgex;
With this method, the string is scanned once and the substitution string
does the dispatch. Supporting nanoseconds is left as an exercise...
The second way uses a gimmick inspired from DT::F::Strptime, version
1.05
my $str =
'some#peculiar@string=which$does:not!appear-in+the+format';
$f =~ s/%%/$str/g;
$f =~ s/%{(\w+)}/1st subs/sgex; # %{method}
$f =~ s/%([a-zA-Z])/2nd subst/sgex; # %x, *not* including %%
$f =~ s/%(\d+)N/3rd subst/sgex; # %3N
$f =~ s/$str/%/g;
A third version, not tested, would be:
$f = join '%', map {
s/%{(\w+)}/1st subs/sgex; # %{method}
s/%([a-zA-Z])/2nd subst/sgex; # %x
s/%(\d+)N/3rd subst/sgex; # %3N
} split /%%/, $f;
Note that if somebody writes a
DateTime::Locale::Fiction::Rodenburry::Klingon
module in which Monday is "krf%Ysq!" and January is "psek%3Nvtdl#",
the second and third versions will still give wrong results
with a format such as "%{day_name} %B". I have not found any
pathological
situation which would give wrong results with the first version.
I have even imagined a paranoid variant of the second way:
my $str;
do { $str = generate_random_string() } until index($f, $str) < 0
&& index($str, '%') < 0;
# etc.
to fix the obvious bug of the "sane" variant of the second way, but it
fails in funny ways, depending on which random string is
generated. Example:
$f = 'xy%%xy%%xy%%' and $str = 'xyxyxy'
Anyhow, both the paranoid and the sane versions fails with the
"Klingon locale".
Jean Forget
--- /home/p80/modules/DateTime-0.22/lib/DateTime.pm Tue Jul 20 22:47:46 2004
+++ /var/tmp/DateTime.new Thu Sep 9 21:19:53 2004
@@ -1695,10 +1694,6 @@
Invalid parameter types (like an array reference) will cause the
constructor to die.
-DateTime does not check if second values greater than 59 are valid
-based on current leap seconds, and invalid values simply cause an
-overflow.
-
All of the parameters are optional except for "year". The "month" and
"day" parameters both default to 1, while the "hour", "minute", and
"second", and "nanosecond" parameters all default to 0.
@@ -2456,7 +2451,7 @@
There are other subtract/delta methods in DateTime.pm to generate
different types of durations. These methods are
C<subtract_datetime()>, C<subtract_datetime_absolute()>,
-C<delta_md()>, L<delta_days()>, and C<delta_ms()>.
+C<delta_md()>, C<delta_days()>, and C<delta_ms()>.
=head2 Overloading
@@ -2532,7 +2527,7 @@
The ISO 8601 year with century as a decimal number. The 4-digit year
corresponding to the ISO week number (see %V). This has the same
-format and value as %y, except that if the ISO week number belongs to
+format and value as %Y, except that if the ISO week number belongs to
the previous or next year, that year is used instead. (TZ)
=item * %g