From c23abea8354008f0bffcd55b0dc9febfb13fe8af Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Tue, 20 Jan 2015 15:28:56 +0100 Subject: [PATCH] Bug 13601: Make dt_from_string not using DateTime::Format::DateParse MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit For a couple of reasons, dt_from_string should not use DateTime::Format::DateParse: 1/ It does not manage date < 1900, certainly caused by l.47 of this module: $p{ year } = $year ? $year + 1900 : DateTime->now->year; 2/ It considers 31/01/2015 as a valid us date, which is not. Test plan: 1/ Verify that prove t/DateUtils.t returns green 2/ Play with dates in Koha (yes I know, it's vague...) 3/ Try to find a regression with dates 4/ Create a date with year <= 1900 and confirm it works QA comment: Why the sql format switch was: - $date_string =~ -s/(\d{4})(\d{2})(\d{2})\s+(\d{2})(\d{2})(\d{2})/$1-$2-$3T$4:$5:$6/; From where a date like "yyyymmdd hhmmss" can come? Tested patches 1 - 3 together. Worked as expected. Signed-off-by: Marc Véron Signed-off-by: Katrin Fischer Signed-off-by: Tomas Cohen Arazi --- Koha/DateUtils.pm | 145 +++++++++++++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 48 deletions(-) diff --git a/Koha/DateUtils.pm b/Koha/DateUtils.pm index 61877b9df5..0a5e15a923 100644 --- a/Koha/DateUtils.pm +++ b/Koha/DateUtils.pm @@ -20,7 +20,6 @@ use strict; use warnings; use 5.010; use DateTime; -use DateTime::Format::DateParse; use C4::Context; use base 'Exporter'; @@ -54,56 +53,106 @@ to the system preferences. If the date string is empty DateTime->now is returned sub dt_from_string { my ( $date_string, $date_format, $tz ) = @_; - # FIXME: see bug 13242 => no TZ for dates 'infinite' - return DateTime::Format::DateParse->parse_datetime($date_string) - if $date_string and $date_string =~ /^9999-/; - - my $dt; - $tz ||= C4::Context->tz; - if ( !$date_format ) { - $date_format = C4::Context->preference('dateformat'); - } - if ($date_string) { - if ( ref($date_string) eq 'DateTime' ) { # already a dt return it - return $date_string; - } - - if ( $date_format eq 'metric' ) { - $date_string =~ s#-#/#g; - $date_string =~ s/^00/01/; # system allows the 0th of the month - $date_string =~ s#^(\d{1,2})/(\d{1,2})#$2/$1#; - } else { - if ( $date_format eq 'iso' ) { - $date_string =~ s/-00/-01/; - if ( $date_string =~ m/^0000-0/ ) { - return; # invalid date in db - } - } elsif ( $date_format eq 'us' ) { - $date_string =~ s#-#/#g; - $date_string =~ s[/00/][/01/]; - } elsif ( $date_format eq 'sql' ) { - $date_string =~ -s/(\d{4})(\d{2})(\d{2})\s+(\d{2})(\d{2})(\d{2})/$1-$2-$3T$4:$5:$6/; - return if ($date_string =~ /^0000-00-00/); - $date_string =~ s/00T/01T/; - } - } - - $dt = eval { - DateTime::Format::DateParse->parse_datetime( $date_string, - $tz->name() ); - }; - if ($@) { - $tz = DateTime::TimeZone->new( name => 'floating' ); - $dt = DateTime::Format::DateParse->parse_datetime( $date_string, - $tz->name() ); - } - } else { - $dt = DateTime->now( time_zone => $tz ); + return if $date_string and $date_string =~ m|^0000-0|; + + $tz = C4::Context->tz unless $tz;; + + return DateTime->now( time_zone => $tz ) unless $date_string; + + $date_format = C4::Context->preference('dateformat') unless $date_format; + + if ( ref($date_string) eq 'DateTime' ) { # already a dt return it + return $date_string; } - return $dt; + my $regex; + if ( $date_format eq 'metric' ) { + # metric format is "dd/mm/yyyy[ hh:mm:ss]" + $regex = qr| + (?\d{2}) + / + (?\d{2}) + / + (?\d{4}) + |xms; + } + elsif ( $date_format eq 'us' ) { + # us format is "mm/dd/yyyy[ hh:mm:ss]" + $regex = qr| + (?\d{2}) + / + (?\d{2}) + / + (?\d{4}) + |xms; + } + elsif ( $date_format eq 'iso' or $date_format eq 'sql' ) { + # iso format is yyyy-dd-mm[ hh:mm:ss]" + $regex = qr| + (?\d{4}) + - + (?\d{2}) + - + (?\d{2}) + |xms; + } + else { + die "Invalid dateformat parameter ($date_format)"; + } + # Add the faculative time part [hh:mm[:ss]] + $regex .= qr| + ( + \s* + (?\d{2}) + : + (?\d{2}) + ( + : + (?\d{2}) + )? + )? + |xms; + + my %dt_params; + if ( $date_string =~ $regex ) { + %dt_params = ( + year => $+{year}, + month => $+{month}, + day => $+{day}, + hour => $+{hour}, + minute => $+{minute}, + second => $+{second}, + ); + } + else { + die "The given date ($date_string) does not match the date format ($date_format)"; + } + + # system allows the 0th of the month + $dt_params{day} = '01' if $dt_params{day} eq '00'; + + # Set default hh:mm:ss to 00:00:00 + $dt_params{hour} = 00 unless defined $dt_params{hour}; + $dt_params{minute} = 00 unless defined $dt_params{minute}; + $dt_params{second} = 00 unless defined $dt_params{second}; + + my $dt = eval { + DateTime->new( + %dt_params, + # No TZ for dates 'infinite' => see bug 13242 + ( $dt_params{year} < 9999 ? ( time_zone => $tz->name ) : () ), + ); + }; + if ($@) { + $tz = DateTime::TimeZone->new( name => 'floating' ); + $dt = DateTime->new( + %dt_params, + # No TZ for dates 'infinite' => see bug 13242 + ( $dt_params{year} < 9999 ? ( time_zone => $tz->name ) : () ), + ); + } + return $dt; } =head2 output_pref -- 2.39.5