From a1667802722c5aaec0b87c805cb4bed99ca88099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20V=C3=A9ron?= Date: Thu, 1 Oct 2015 18:08:17 +0200 Subject: [PATCH] Bug 14936 - Add validation for date strings to Koha::DateUtils To test: - Apply patch - prove t/DateUtils.t Bonus test: Apply patch(es) from Bug(s) that are blocked by this Bug and test as appropriate. --- Koha/DateUtils.pm | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++- t/DateUtils.t | 25 +++++++++++- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/Koha/DateUtils.pm b/Koha/DateUtils.pm index 7a98120..405e529 100644 --- a/Koha/DateUtils.pm +++ b/Koha/DateUtils.pm @@ -26,7 +26,7 @@ use base 'Exporter'; use version; our $VERSION = qv('1.0.0'); our @EXPORT = ( - qw( dt_from_string output_pref format_sqldatetime ) + qw( dt_from_string output_pref format_sqldatetime is_formatted_date_string ) ); =head1 DateUtils @@ -273,4 +273,112 @@ sub format_sqldatetime { return q{}; } + +=head2 is_formatted_date_string + +$dt = is_formatted_date_string($date_string, [$format]); + +Passed a date string returns 1 if it is formatted following fhe format passed +(default to the system preferences, otherwise 0. If the date string is empty +or starts with 0000-0, 0 is returned. + +=cut + +sub is_formatted_date_string { + my ( $date_string, $date_format ) = @_; + + return 0 if $date_string and $date_string =~ m|^0000-0|; + return 0 unless $date_string; + + $date_format = C4::Context->preference('dateformat') unless $date_format; + + 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 or sql format are yyyy-dd-mm[ hh:mm:ss]"; + $regex = qr| + (?\d{4}) + - + (?\d{2}) + - + (?\d{2}) + |xms; + } + else { + warn "Invalid dateformat parameter ($date_format)"; + return 0; + } + + # Add the faculative time part [hh:mm[:ss]] + my $time_re .= qr| + ( + \s* + (?\d{2}) + : + (?\d{2}) + ( + : + (?\d{2}) + )? + )? + |xms; + $regex .= $time_re; + + my %dt_params; + if ( $date_string =~ $regex ) { + %dt_params = ( + year => $+{year}, + month => $+{month}, + day => $+{day}, + hour => $+{hour}, + minute => $+{minute}, + second => $+{second}, + ); + } + else { + return 0; + } + + # 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 $tz = C4::Context->tz; + my $dt = eval { + DateTime->new( + %dt_params, + # No TZ for dates 'infinite' => see bug 13242 + ( $dt_params{year} < 9999 ? ( time_zone => $tz->name ) : () ), + ); + }; + + return 1 if $dt; #Date format is OK an dwe have a valid date + + return 0; +} + 1; diff --git a/t/DateUtils.t b/t/DateUtils.t index 4f7c773..6b18b7e 100755 --- a/t/DateUtils.t +++ b/t/DateUtils.t @@ -3,7 +3,7 @@ use DateTime; use DateTime::TimeZone; use C4::Context; -use Test::More tests => 55; +use Test::More tests => 71; use Test::MockModule; use Time::HiRes qw/ gettimeofday /; use t::lib::Mocks; @@ -215,3 +215,26 @@ is( output_pref( { dt => $dt, dateonly => 1 } ), '01/01/1900', 'dt_from_string s # fallback $dt = dt_from_string('2015-01-31 01:02:03'); is( output_pref( {dt => $dt} ), '31/01/2015 01:02', 'dt_from_string should fallback to sql format' ); + +# test formatting of date strings + +is( is_formatted_date_string('0000-00-00', 'iso'), 0, 'is_formatted_date_string should return 0 for 0000-00-00'); +is( is_formatted_date_string('', 'iso'), 0, 'is_formatted_date_string should return 0 for empty string'); +is( is_formatted_date_string('baddate', 'iso'), 0, 'is_formatted_date_string should return 0 for baddate'); +is( is_formatted_date_string('11/06/1955', 'badformat'), 0, 'is_formatted_date_string should return 0 for unknown format'); + +is( is_formatted_date_string('1955-06-11', 'iso'), 1, 'is_formatted_date_string should return 1 for 1955/06/11 iso'); +is( is_formatted_date_string('1955-06-11 12:00', 'iso'), 1, 'is_formatted_date_string should return 1 for 1955/06/11 12:00 iso'); +is( is_formatted_date_string('11/06/1955', 'iso'), 0, 'is_formatted_date_string should return 0 for 11/06/1955 iso'); + +is( is_formatted_date_string('06/11/1955', 'us'), 1, 'is_formatted_date_string should return 1 for 06/11/1955 us'); +is (is_formatted_date_string('06/11/1955 12:00', 'us'), 1, 'is_formatted_date_string should return 1 for 06/11/1955 12:00 us'); +is( is_formatted_date_string('06-11-1955', 'us'), 0, 'is_formatted_date_string should return 0 for 06-11-1955 us'); + +is( is_formatted_date_string('11/06/1955', 'metric'), 1, 'is_formatted_date_string should return 1 for 06/11/1955 metric'); +is (is_formatted_date_string('11/06/1955 12:00', 'metric'), 1, 'is_formatted_date_string should return 1 for 11/06/1955 12:00 us'); +is( is_formatted_date_string('06-11-1955', 'metric'), 0, 'is_formatted_date_string should return 0 for 06-11-1955 metric'); + +is( is_formatted_date_string('32-01-1970', 'metric'), 0, 'is_formatted_date_string should return 0 for 32-01-1970 metric'); +is( is_formatted_date_string('01-15-1970', 'metric'), 0, 'is_formatted_date_string should return 0 for 01-15-1970 metric'); +is( is_formatted_date_string('99-99-1970', 'metric'), 0, 'is_formatted_date_string should return 0 for 99-99-1970 metric'); -- 1.7.10.4