From ffd257aa09d61ba9ac20a49c815d0f14ef32d05a Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Thu, 9 Oct 2014 14:07:15 +0200 Subject: [PATCH] DatesRole: fix and expand relative dates. - Add grouping to regex for proper |ing. - Add 'year' to relative dates. (See Chinese zodiac) - Add more tests. --- lib/DDG/GoodieRole/Dates.pm | 67 ++++++++++++++++++++----------------- t/00-roles.t | 33 +++++++++++------- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/lib/DDG/GoodieRole/Dates.pm b/lib/DDG/GoodieRole/Dates.pm index 244ba0bbf..fa21502e6 100755 --- a/lib/DDG/GoodieRole/Dates.pm +++ b/lib/DDG/GoodieRole/Dates.pm @@ -39,7 +39,7 @@ my $month_regex = qr#$full_month|$short_month#; my $time_24h = qr#(?:(?:[0-1][0-9])|(?:2[0-3]))[:]?[0-5][0-9][:]?[0-5][0-9]#i; my $time_12h = qr#(?:(?:0[1-9])|(?:1[012])):[0-5][0-9]:[0-5][0-9]\s?(?:am|pm)#i; my $date_number = qr#[0-3]?[0-9]#; -my $relative_days = qr#now|today|tomorrow|yesterday|(?:current|previous day)|(?:next|last week|month)#i; +my $relative_days = qr#now|today|tomorrow|yesterday|(?:(?:current|previous|next) day)|(?:next|last) (?:week|month|year)#i; # Covering the ambiguous formats, like: # DMY: 27/11/2014 with a variety of delimiters # MDY: 11/27/2014 -- fundamentally non-sensical date format, for americans @@ -244,8 +244,8 @@ my $descriptive_datestring = qr{ (?:(?:$month_regex)\s(?:[0-9]{4})) | # Jan 2014, August 2000 (?:(?:$date_number)\s?$number_suffixes?\s(?:$month_regex)) | # 18th Jan, 01 October (?:(?:$month_regex)\s(?:$date_number)\s?$number_suffixes?) | # Dec 25, July 4th - (?:$month_regex) | # February, Aug - (?:$relative_days) + (?:$month_regex) | # February, Aug + (?:$relative_days) # next week, last year }ix; # Used for parse_descriptive_datestring_to_date @@ -399,47 +399,52 @@ sub parse_descriptive_datestring_to_date { return unless ($string && $string =~ qr/^$descriptive_datestring_matches$/); - my $now = DateTime->now(); - my $month = $+{'m'}; # Set in each alternative match. + my $now = DateTime->now(); + my $month = $+{'m'}; # Set in each alternative match. if (my $day = $+{'d'}) { - return parse_datestring_to_date("$day $month ".$now->year()); - } - elsif (my $relative_dir = $+{'q'}) { - my $tmp_date = parse_datestring_to_date("01 $month ".$now->year()); + return parse_datestring_to_date("$day $month " . $now->year()); + } elsif (my $relative_dir = $+{'q'}) { + my $tmp_date = parse_datestring_to_date("01 $month " . $now->year()); # next - $tmp_date->add( years => 1) if ($relative_dir eq "next" && DateTime->compare($tmp_date, $now) != 1); + $tmp_date->add(years => 1) if ($relative_dir eq "next" && DateTime->compare($tmp_date, $now) != 1); # last - $tmp_date->add( years => -1) if ($relative_dir eq "last" && DateTime->compare($tmp_date, $now) != -1); + $tmp_date->add(years => -1) if ($relative_dir eq "last" && DateTime->compare($tmp_date, $now) != -1); return $tmp_date; - } - elsif (my $year = $+{'y'}) { + } elsif (my $year = $+{'y'}) { # Month and year is the first of that month. return parse_datestring_to_date("01 $month $year"); - } - elsif (my $relative_date = $+{'r'}) { + } elsif (my $relative_date = $+{'r'}) { + return $now if ($relative_date =~ qr/now|today|(?:current day)/); + my $tmp_date = $now; - return $now if ($+{'r'} =~ qr/now|today|(current day)/); - - $tmp_date->add( days => 1) if ($+{'r'} =~ qr/tomorrow|(next day)/); - $tmp_date->add( days => -1) if ($+{'r'} =~ qr/yesterday|(previous day)/); - - if ($+{'r'} =~ qr/(next|last) (week|month)/) { - my $num = 1; - $num = -1 if ($1 eq "last"); - - $tmp_date->add( days => 7*$num ) if($2 eq "week"); - $tmp_date->add( months => $num ) if($2 eq "month"); + my @to_add; + + if ($relative_date =~ qr/tomorrow|(?:next day)/) { + @to_add = (days => 1); + } elsif ($relative_date =~ qr/yesterday|(?:previous day)/) { + @to_add = (days => -1); + } elsif ($relative_date =~ qr/(?next|last) (?week|month|year)/) { + my $unit = $+{'unit'}; + my $num = ($+{'dir'} eq 'next') ? 1 : -1; + + @to_add = + ($unit eq 'week') ? (days => 7 * $num) + : ($unit eq 'month') ? (months => $num) + : ($unit eq 'year') ? (years => $num) + : (); + } + + $tmp_date->add(@to_add) if (@to_add); return $tmp_date; - } - else { + } else { # single named months # "january" in january means the current month # otherwise it always means the coming month of that name, be it this year or next year - return parse_datestring_to_date("01 ".$now->month()." ".$now->year()) if lc($now->month_name()) eq lc($month); - my $this_years_month = parse_datestring_to_date("01 $month ".$now->year()); - $this_years_month->add( years => 1 ) if (DateTime->compare($this_years_month, $now) == -1); + return parse_datestring_to_date("01 " . $now->month() . " " . $now->year()) if lc($now->month_name()) eq lc($month); + my $this_years_month = parse_datestring_to_date("01 $month " . $now->year()); + $this_years_month->add(years => 1) if (DateTime->compare($this_years_month, $now) == -1); return $this_years_month; } } diff --git a/t/00-roles.t b/t/00-roles.t index 4d93a3fd0..ff3fa1bb7 100755 --- a/t/00-roles.t +++ b/t/00-roles.t @@ -268,6 +268,7 @@ subtest 'Dates' => sub { 'next jan' => '01 Jan 2001', 'last jan' => '01 Jan 2000', 'feb 2038' => '01 Feb 2038', + 'next day' => '02 Aug 2000', }, '2015-12-01T00:00:00Z' => { 'next december' => '01 Dec 2016', @@ -281,22 +282,30 @@ subtest 'Dates' => sub { 'feb 2038' => '01 Feb 2038', 'now' => '01 Dec 2015', 'today' => '01 Dec 2015', + 'current day' => '01 Dec 2015', + 'next month' => '01 Jan 2016', }, '2000-01-01T00:00:00Z' => { - 'feb 21st' => '21 Feb 2000', - '11th feb' => '11 Feb 2000', - 'march 13' => '13 Mar 2000', - '12 march' => '12 Mar 2000', - 'next week' => '08 Jan 2000', - 'last week' => '25 Dec 1999', - 'tomorrow' => '02 Jan 2000', - 'yesterday' => '31 Dec 1999', + 'feb 21st' => '21 Feb 2000', + '11th feb' => '11 Feb 2000', + 'march 13' => '13 Mar 2000', + '12 march' => '12 Mar 2000', + 'next week' => '08 Jan 2000', + 'last week' => '25 Dec 1999', + 'tomorrow' => '02 Jan 2000', + 'yesterday' => '31 Dec 1999', + 'last year' => '01 Jan 1999', + 'next year' => '01 Jan 2001', }, '2014-10-08T00:00:00Z' => { - 'next week' => '01 Oct 2014', - 'last week' => '15 oct 2014', - 'next month' => '08 Nov 2014', - 'last month' => '08 Sep 2014' + 'next week' => '15 Oct 2014', + 'last week' => '01 Oct 2014', + 'next month' => '08 Nov 2014', + 'last month' => '08 Sep 2014', + 'next year' => '08 Oct 2015', + 'last year' => '08 Oct 2013', + 'december 2015' => '01 Dec 2015', + 'march 13' => '13 Mar 2014', }, ); foreach my $query_time (sort keys %time_strings) {