Merge pull request #614 from duckduckgo/mintsoft/workdays_between
Extending Dates Role's Vague String handling & Fixing #346master
commit
859f7e776d
|
@ -28,38 +28,29 @@ my @weekDays = ("S", "M", "T", "W", "T", "F", "S");
|
|||
# read in css-file only once
|
||||
my $css = share("style.css")->slurp;
|
||||
|
||||
my $month_regex = month_regex();
|
||||
my $date_regex = date_regex();
|
||||
my $datestring_regex = datestring_regex();
|
||||
my $formatted_datestring_regex = formatted_datestring_regex();
|
||||
|
||||
handle remainder => sub {
|
||||
my $query = $_;
|
||||
my $date_object = DateTime->now;
|
||||
my ($currentDay, $currentMonth, $currentYear) = ($date_object->day(), $date_object->month(), $date_object->year());
|
||||
my $highlightDay = 0; # Initialized, but won't match, by default.
|
||||
my $highlightDay = 0; # Initialized, but won't match, by default.
|
||||
if ($query) {
|
||||
my ($date_string, $other_format) = $query =~ qr#($date_regex)|((?:(?:next|last) )?$month_regex(?: [0-9]{4})?)#i;
|
||||
if ($date_string) {
|
||||
$date_object = parse_string_to_date($date_string);
|
||||
my ($date_string) = $query =~ qr#($datestring_regex)#i; # Extract any datestring from the query.
|
||||
|
||||
return unless $date_object;
|
||||
$highlightDay = $date_object->day();
|
||||
} elsif ($other_format) {
|
||||
$date_object = parse_vague_string_to_date($other_format);
|
||||
$date_object = parse_datestring_to_date($date_string);
|
||||
|
||||
return unless $date_object;
|
||||
# highlight today if current month is given
|
||||
if (($date_object->year() eq $currentYear) && ($date_object->month() eq $currentMonth)) {
|
||||
$highlightDay = $currentDay;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$highlightDay = $currentDay;
|
||||
return unless $date_object;
|
||||
$highlightDay = $date_object->day() if ($query =~ $formatted_datestring_regex); # They specified a date, so highlight.
|
||||
}
|
||||
# Highlight today if it's this month and no other day was chosen.
|
||||
$highlightDay ||= $currentDay if (($date_object->year() eq $currentYear) && ($date_object->month() eq $currentMonth));
|
||||
|
||||
my $the_year = $date_object->year();
|
||||
my $the_month = $date_object->month();
|
||||
# return calendar
|
||||
my $start = parse_string_to_date($the_year . "-" . $the_month . "-1");
|
||||
my $start = parse_datestring_to_date($the_year . "-" . $the_month . "-1");
|
||||
return format_result({
|
||||
first_day => $start,
|
||||
first_day_num => $start->day_of_week() % 7, # 0=Sunday
|
||||
|
|
|
@ -20,14 +20,14 @@ category 'time_sensitive';
|
|||
topics 'everyday';
|
||||
attribution github => ['http://github.com/cj01101', 'cj01101'];
|
||||
|
||||
my $date_regex = date_regex();
|
||||
my $datestring_regex = datestring_regex();
|
||||
|
||||
handle query_lc => sub {
|
||||
my $query = $_;
|
||||
return unless $query =~ qr!^($date_regex)\s+(plus|\+|\-|minus)\s+(\d+|[a-z\s-]+)\s+((?:day|week|month|year)s?)$!;
|
||||
return unless $query =~ qr!^($datestring_regex)\s+(plus|\+|\-|minus)\s+(\d+|[a-z\s-]+)\s+((?:day|week|month|year)s?)$!;
|
||||
my ($input_date, $input_action, $input_number, $unit) = ($1, $2, $3, $4);
|
||||
|
||||
$input_date = parse_string_to_date($input_date);
|
||||
$input_date = parse_datestring_to_date($input_date);
|
||||
$input_number = str2nbr($input_number);
|
||||
|
||||
# check/tweak other (non-date) input
|
||||
|
|
|
@ -18,12 +18,12 @@ category 'calculations';
|
|||
topics 'everyday';
|
||||
attribution github => ['http://github.com/JetFault', 'JetFault'];
|
||||
|
||||
my $date_regex = date_regex();
|
||||
my $datestring_regex = datestring_regex();
|
||||
|
||||
handle remainder => sub {
|
||||
return unless $_ =~ qr/^($date_regex) (?:(?:and|to) )?($date_regex)(?:[,]? inclusive)?$/i;
|
||||
return unless $_ =~ qr/^($datestring_regex) (?:(?:and|to) )?($datestring_regex)(?:[,]? inclusive)?$/i;
|
||||
|
||||
my ($date1, $date2) = parse_all_strings_to_date($1, $2);
|
||||
my ($date1, $date2) = parse_all_datestrings_to_date($1, $2);
|
||||
return unless ($date1 && $date2);
|
||||
|
||||
($date1, $date2) = ($date2, $date1) if ( DateTime->compare($date1, $date2) == 1 );
|
||||
|
|
|
@ -31,11 +31,11 @@ sub html_output {
|
|||
}
|
||||
|
||||
|
||||
my $date_regex = date_regex();
|
||||
my $datestring_regex = datestring_regex();
|
||||
|
||||
handle remainder => sub {
|
||||
return unless $_ =~ qr/^($date_regex) (?:(?:and|to) )?($date_regex)/i;
|
||||
my ($start, $end) = (parse_string_to_date($1), parse_string_to_date($2));
|
||||
return unless $_ =~ qr/^($datestring_regex) (?:(?:and|to) )?($datestring_regex)/i;
|
||||
my ($start, $end) = (parse_datestring_to_date($1), parse_datestring_to_date($2));
|
||||
return unless ($start && $end);
|
||||
|
||||
# Flip if the dates are the wrong way around
|
||||
|
|
|
@ -22,16 +22,17 @@ category 'calculations';
|
|||
topics 'everyday';
|
||||
attribution github => ['http://github.com/mgarriott', 'mgarriott'];
|
||||
|
||||
my $date_regex = date_regex();
|
||||
my $datestring_regex = datestring_regex();
|
||||
|
||||
handle remainder => sub {
|
||||
my $query = $_;
|
||||
return unless $query =~ qr/($date_regex) (?:(?:and|to) )?($date_regex)/i;
|
||||
|
||||
my ($start_date, $end_date) = parse_all_strings_to_date($1, $2);
|
||||
return unless ($start_date && $end_date);
|
||||
|
||||
($start_date, $end_date) = ($end_date, $start_date) if ( DateTime->compare($start_date, $end_date) == 1 );
|
||||
return unless ($query =~ qr/($datestring_regex) (?:(?:and|to) )?($datestring_regex)/i);
|
||||
my ($start_date, $end_date) = parse_all_datestrings_to_date($1, $2);
|
||||
|
||||
return unless ($start_date && $end_date);
|
||||
|
||||
($start_date, $end_date) = ($end_date, $start_date) if (DateTime->compare($start_date, $end_date) == 1);
|
||||
|
||||
my $calendar = Date::Calendar->new($Profiles->{US});
|
||||
my $workdays = $calendar->delta_workdays($start_date->year(), $start_date->month(), $start_date->day(), $end_date->year(), $end_date->month(), $end_date->day(), 1, 1);
|
||||
|
|
|
@ -3,7 +3,6 @@ package DDG::GoodieRole::Dates;
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use feature 'state';
|
||||
|
||||
use Moo::Role;
|
||||
|
||||
|
@ -21,6 +20,7 @@ my %full_month_to_short = map { lc $_ => substr($_, 0, 3) } qw(January February
|
|||
my %short_month_fix = map { lc $_ => $_ } (values %full_month_to_short);
|
||||
my $short_month = qr#Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec#i;
|
||||
my $full_month = qr#January|February|March|April|May|June|July|August|September|October|November|December#i;
|
||||
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]#;
|
||||
|
@ -36,6 +36,28 @@ my $number_suffixes = qr#(?:st|nd|rd|th)#i;
|
|||
|
||||
# Timezones: https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
|
||||
my $tz_suffixes = qr#(?:[+-][0-9]{4})|ACDT|ACST|ACT|ADT|AEDT|AEST|AFT|AKDT|AKST|AMST|AMT|ART|AST|AWDT|AWST|AZOST|AZT|BDT|BIOT|BIT|BOT|BRT|BST|BTT|CAT|CCT|CDT|CEDT|CEST|CET|CHADT|CHAST|CHOT|CHUT|CIST|CIT|CKT|CLST|CLT|COST|COT|CST|CT|CVT|CWST|CXT|ChST|DAVT|DDUT|DFT|EASST|EAST|EAT|ECT|EDT|EEDT|EEST|EET|EGST|EGT|EIT|EST|FET|FJT|FKST|FKT|FNT|GALT|GAMT|GET|GFT|GILT|GIT|GMT|GST|GYT|HADT|HAEC|HAST|HKT|HMT|HOVT|HST|ICT|IDT|IOT|IRDT|IRKT|IRST|IST|JST|KGT|KOST|KRAT|KST|LHST|LINT|MAGT|MART|MAWT|MDT|MEST|MET|MHT|MIST|MIT|MMT|MSK|MST|MUT|MVT|MYT|NCT|NDT|NFT|NPT|NST|NT|NUT|NZDT|NZST|OMST|ORAT|PDT|PET|PETT|PGT|PHOT|PHT|PKT|PMDT|PMST|PONT|PST|PYST|PYT|RET|ROTT|SAKT|SAMT|SAST|SBT|SCT|SGT|SLST|SRT|SST|SYOT|TAHT|TFT|THA|TJT|TKT|TLT|TMT|TOT|TVT|UCT|ULAT|UTC|UYST|UYT|UZT|VET|VLAT|VOLT|VOST|VUT|WAKT|WAST|WAT|WEDT|WEST|WET|WIT|WST|YAKT|YEKT|Z#i;
|
||||
|
||||
# formats parsed by vague datestring, without colouring
|
||||
# the context of the code using it
|
||||
my $descriptive_datestring = qr{
|
||||
(?:(?:next|last)\s(?:$month_regex)) | # next June, last jan
|
||||
(?:(?:$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
|
||||
}ix;
|
||||
|
||||
# Used for parse_descriptive_datestring_to_date
|
||||
my $descriptive_datestring_matches = qr#
|
||||
(?:(?<q>next|last)\s(?<m>$month_regex)) |
|
||||
(?:(?<m>$month_regex)\s(?<y>[0-9]{4})) |
|
||||
(?:(?<d>$date_number)\s?$number_suffixes?\s(?<m>$month_regex)) |
|
||||
(?:(?<m>$month_regex)\s(?<d>$date_number)\s?$number_suffixes?) |
|
||||
(?<m>$month_regex)
|
||||
#ix;
|
||||
|
||||
my $formatted_datestring = build_datestring_regex();
|
||||
|
||||
# Accessors for useful regexes
|
||||
sub full_month_regex {
|
||||
return $full_month;
|
||||
|
@ -44,7 +66,7 @@ sub short_month_regex {
|
|||
return $short_month;
|
||||
}
|
||||
sub month_regex {
|
||||
return qr/$full_month|$short_month/;
|
||||
return $month_regex;
|
||||
}
|
||||
sub full_day_of_week_regex {
|
||||
return $full_day_of_week;
|
||||
|
@ -53,9 +75,23 @@ sub short_day_of_week_regex {
|
|||
return $short_day_of_week;
|
||||
}
|
||||
|
||||
# Accessors for matching regexes
|
||||
# These matches are for "in the right format"/"looks about right"
|
||||
# not "are valid dates"; expects normalised whitespace
|
||||
sub date_regex {
|
||||
sub datestring_regex {
|
||||
return qr#$formatted_datestring|$descriptive_datestring#i;
|
||||
}
|
||||
|
||||
sub descriptive_datestring_regex {
|
||||
return $descriptive_datestring;
|
||||
}
|
||||
|
||||
sub formatted_datestring_regex {
|
||||
return $formatted_datestring;
|
||||
}
|
||||
|
||||
# Called once to build $formatted_datestring
|
||||
sub build_datestring_regex {
|
||||
my @regexes = ();
|
||||
|
||||
## unambigous and awesome date formats:
|
||||
|
@ -81,16 +117,23 @@ sub date_regex {
|
|||
## Ambiguous, but potentially valid date formats
|
||||
push @regexes, $ambiguous_dates;
|
||||
|
||||
state $returned_regex = join '|', @regexes;
|
||||
my $returned_regex = join '|', @regexes;
|
||||
return qr/$returned_regex/i;
|
||||
}
|
||||
|
||||
# Accepts a string which looks like date per the supplied date_regex (e.g. '31/10/1980')
|
||||
# Returns a DateTime object representing that date or `undef` if the string cannot be parsed.
|
||||
sub parse_string_to_date {
|
||||
# Parses any string that *can* be parsed to a date object
|
||||
sub parse_datestring_to_date {
|
||||
my ($d) = @_;
|
||||
|
||||
return unless ($d =~ date_regex()); # Only handle white-listed strings, even if they might otherwise work.
|
||||
return parse_formatted_datestring_to_date($d) || parse_descriptive_datestring_to_date($d);
|
||||
}
|
||||
|
||||
# Accepts a string which looks like date per the supplied datestring_regex (e.g. '31/10/1980')
|
||||
# Returns a DateTime object representing that date or `undef` if the string cannot be parsed.
|
||||
sub parse_formatted_datestring_to_date {
|
||||
my ($d) = @_;
|
||||
|
||||
return unless ($d =~ qr/^$formatted_datestring$/); # Only handle white-listed strings, even if they might otherwise work.
|
||||
if ($d =~ $ambiguous_dates_matches) {
|
||||
# guesswork for ambigous DMY/MDY and switch to ISO
|
||||
my ($month, $day, $year) = ($+{'m'}, $+{'d'}, $+{'y'}); # Assume MDY, even though it's crazy, for backward compatibility
|
||||
|
@ -117,7 +160,7 @@ sub parse_string_to_date {
|
|||
# parses multiple dates and guesses the consistent format over the set;
|
||||
# i.e. defaults to m/d/y unless one of them is obviously d/m/y then it'll
|
||||
# treat them all as d/m/y
|
||||
sub parse_all_strings_to_date {
|
||||
sub parse_all_datestrings_to_date {
|
||||
my @dates = @_;
|
||||
|
||||
# If there is an ambiguous date with a "month" over 12 in the set, we need to flip.
|
||||
|
@ -131,7 +174,7 @@ sub parse_all_strings_to_date {
|
|||
return if $month > 12; #there's a mish-mash of formats; give up
|
||||
$date = "$year-$month-$day";
|
||||
}
|
||||
my $date_object = parse_string_to_date($date);
|
||||
my $date_object = parse_datestring_to_date($date);
|
||||
return unless $date_object;
|
||||
push @dates_to_return, $date_object;
|
||||
}
|
||||
|
@ -139,6 +182,40 @@ sub parse_all_strings_to_date {
|
|||
return @dates_to_return;
|
||||
}
|
||||
|
||||
# Parses a really vague description and basically guesses
|
||||
sub parse_descriptive_datestring_to_date {
|
||||
my ($string) = @_;
|
||||
|
||||
return unless ($string =~ qr/^$descriptive_datestring_matches$/);
|
||||
|
||||
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());
|
||||
# next <month>
|
||||
$tmp_date->add( years => 1) if ($relative_dir eq "next" && DateTime->compare($tmp_date, $now) != 1);
|
||||
# last <month>
|
||||
$tmp_date->add( years => -1) if ($relative_dir eq "last" && DateTime->compare($tmp_date, $now) != -1);
|
||||
return $tmp_date;
|
||||
}
|
||||
elsif (my $year = $+{'y'}) {
|
||||
# Month and year is the first of that month.
|
||||
return parse_datestring_to_date("01 $month $year");
|
||||
}
|
||||
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 $this_years_month;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes a DateTime object (or a string which can be parsed into one)
|
||||
# and returns a standard formatted output string or an empty string if it cannot be parsed.
|
||||
|
@ -149,42 +226,11 @@ sub date_output_string {
|
|||
my $string = ''; # By default we've got nothing.
|
||||
|
||||
# They didn't give us a DateTime object, let's try to make one from whatever we got.
|
||||
$dt = parse_string_to_date($dt) if (ref($dt) !~ /DateTime/);
|
||||
$dt = parse_datestring_to_date($dt) if (ref($dt) !~ /DateTime/);
|
||||
|
||||
$string = $dt->strftime($ddg_format) if ($dt);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
# Parses a really vague description and basically guesses
|
||||
sub parse_vague_string_to_date {
|
||||
my ($string) = @_;
|
||||
if($string =~ qr#(?:(?<q>next|last)\s(?<m>$full_month|$short_month))|(?:(?<m>$full_month|$short_month)\s(?<y>[0-9]{4}))|(?<m>$full_month|$short_month)#i) {
|
||||
my $now = DateTime->now();
|
||||
my $month = $+{'m'}; # Set in each alternative match.
|
||||
if (my $relative_dir = $+{'q'}) {
|
||||
my $tmp_date = parse_string_to_date("01 $month ".$now->year());
|
||||
# next <month>
|
||||
$tmp_date->add( years => 1) if ($relative_dir eq "next" && DateTime->compare($tmp_date, $now) != 1);
|
||||
# last <month>
|
||||
$tmp_date->add( years => -1) if ($relative_dir eq "last" && DateTime->compare($tmp_date, $now) != -1);
|
||||
return $tmp_date;
|
||||
}
|
||||
elsif (my $year = $+{'y'}) {
|
||||
# Month and year is the first of that month.
|
||||
return parse_string_to_date("01 $month $year");
|
||||
}
|
||||
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_string_to_date("01 ".$now->month()." ".$now->year()) if lc($now->month_name()) eq lc($month);
|
||||
my $this_years_month = parse_string_to_date("01 $month ".$now->year());
|
||||
$this_years_month->add( years => 1 ) if (DateTime->compare($this_years_month, $now) == -1);
|
||||
return $this_years_month;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
71
t/00-roles.t
71
t/00-roles.t
|
@ -58,12 +58,18 @@ subtest 'Dates' => sub {
|
|||
|
||||
{ package RoleTester; use Moo; with 'DDG::GoodieRole::Dates'; 1; }
|
||||
|
||||
my $test_regex;
|
||||
my $test_datestring_regex;
|
||||
my $test_formatted_datestring_regex;
|
||||
my $test_descriptive_datestring_regex;
|
||||
|
||||
subtest 'Initialization' => sub {
|
||||
new_ok('RoleTester', [], 'Applied to a class');
|
||||
$test_regex = RoleTester::date_regex();
|
||||
isa_ok($test_regex, 'Regexp', 'date_regex()');
|
||||
$test_datestring_regex = RoleTester::datestring_regex();
|
||||
isa_ok($test_datestring_regex, 'Regexp', 'datestring_regex()');
|
||||
$test_formatted_datestring_regex = RoleTester::formatted_datestring_regex();
|
||||
isa_ok($test_formatted_datestring_regex, 'Regexp', 'formatted_datestring_regex()');
|
||||
$test_descriptive_datestring_regex = RoleTester::descriptive_datestring_regex();
|
||||
isa_ok($test_descriptive_datestring_regex, 'Regexp', 'descriptive_datestring_regex()');
|
||||
};
|
||||
|
||||
subtest 'Working single dates' => sub {
|
||||
|
@ -112,13 +118,17 @@ subtest 'Dates' => sub {
|
|||
);
|
||||
|
||||
foreach my $test_date (sort keys %dates_to_match) {
|
||||
like($test_date, qr/^$test_regex$/, "$test_date matches the date_regex");
|
||||
like($test_date, qr/^$test_datestring_regex$/, "$test_date matches the datestring_regex");
|
||||
like($test_date, qr/^$test_formatted_datestring_regex$/, "$test_date matches the formatted_datestring_regex");
|
||||
|
||||
# test_regex should not contain any submatches
|
||||
$test_date =~ qr/^$test_regex$/;
|
||||
$test_date =~ qr/^$test_datestring_regex$/;
|
||||
ok(scalar @- == 1 && scalar @+ == 1, ' with no sub-captures.');
|
||||
|
||||
my $date_object = RoleTester::parse_string_to_date($test_date);
|
||||
$test_formatted_datestring_regex =~ qr/^$test_datestring_regex$/;
|
||||
ok(scalar @- == 1 && scalar @+ == 1, ' with no sub-captures.');
|
||||
|
||||
my $date_object = RoleTester::parse_formatted_datestring_to_date($test_date);
|
||||
isa_ok($date_object, 'DateTime', $test_date);
|
||||
is($date_object->epoch, $dates_to_match{$test_date}, '... which represents the correct time.');
|
||||
}
|
||||
|
@ -169,7 +179,7 @@ subtest 'Dates' => sub {
|
|||
|
||||
foreach my $set (@date_sets) {
|
||||
my @source = @{$set->{src}};
|
||||
eq_or_diff([map { $_->epoch } (RoleTester::parse_all_strings_to_date(@source))],
|
||||
eq_or_diff([map { $_->epoch } (RoleTester::parse_all_datestrings_to_date(@source))],
|
||||
$set->{output}, '"' . join(', ', @source) . '": dates parsed correctly');
|
||||
}
|
||||
};
|
||||
|
@ -188,13 +198,13 @@ subtest 'Dates' => sub {
|
|||
|
||||
foreach my $test_string (sort keys %bad_strings_match) {
|
||||
if ($bad_strings_match{$test_string}) {
|
||||
like($test_string, qr/^$test_regex$/, "$test_string matches date_regex");
|
||||
like($test_string, qr/^$test_formatted_datestring_regex$/, "$test_string matches formatted_datestring_regex");
|
||||
} else {
|
||||
unlike($test_string, qr/^$test_regex$/, "$test_string does not match date_regex");
|
||||
unlike($test_string, qr/^$test_formatted_datestring_regex$/, "$test_string does not match formatted_datestring_regex");
|
||||
}
|
||||
|
||||
my $result;
|
||||
lives_ok { $result = RoleTester::parse_string_to_date($test_string) } '... and does not kill the parser.';
|
||||
lives_ok { $result = RoleTester::parse_formatted_datestring_to_date($test_string) } '... and does not kill the parser.';
|
||||
is($result, undef, '... and returns undef to signal failure.');
|
||||
}
|
||||
};
|
||||
|
@ -211,7 +221,7 @@ subtest 'Dates' => sub {
|
|||
|
||||
foreach my $set (@invalid_date_sets) {
|
||||
my @source = @$set;
|
||||
my @date_results = RoleTester::parse_all_strings_to_date(@source);
|
||||
my @date_results = RoleTester::parse_all_datestrings_to_date(@source);
|
||||
is(@date_results, 0, '"' . join(', ', @source) . '": cannot be parsed in combination.');
|
||||
}
|
||||
};
|
||||
|
@ -264,18 +274,55 @@ subtest 'Dates' => sub {
|
|||
'last jan' => '01 Jan 2015',
|
||||
'feb 2038' => '01 Feb 2038',
|
||||
},
|
||||
'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',
|
||||
}
|
||||
);
|
||||
foreach my $query_time (sort keys %time_strings) {
|
||||
set_fixed_time($query_time);
|
||||
my %strings = %{$time_strings{$query_time}};
|
||||
foreach my $test_date (sort keys %strings) {
|
||||
my $result = RoleTester::parse_vague_string_to_date($test_date);
|
||||
my $result = RoleTester::parse_descriptive_datestring_to_date($test_date);
|
||||
isa_ok($result, 'DateTime', $test_date);
|
||||
is(RoleTester::date_output_string($result), $strings{$test_date}, $test_date . ' relative to ' . $query_time);
|
||||
}
|
||||
}
|
||||
restore_time();
|
||||
};
|
||||
|
||||
subtest 'Valid mixture of formatted and descriptive dates' => sub {
|
||||
set_fixed_time('2000-01-01T00:00:00Z');
|
||||
my %mixed_dates_to_test = (
|
||||
'2014-11-27' => 1417046400,
|
||||
'1994-02-03T14:15:29' => 760284929,
|
||||
'Sat, 09 Aug 2014 18:20:00' => 1407608400,
|
||||
'08-Feb-94 14:15:29 GMT' => 760716929,
|
||||
'13/12/2011' => 1323734400,
|
||||
'01/01/2001' => 978307200,
|
||||
'29 June 2014' => 1404000000,
|
||||
'05 Mar 1990' => 636595200,
|
||||
'June 01 2012' => 1338508800,
|
||||
'May 05 2011' => 1304553600,
|
||||
'February 21st' => 951091200,
|
||||
'11th feb' => 950227200,
|
||||
'11 march' => 952732800,
|
||||
'11 mar' => 952732800,
|
||||
'jun 21' => 961545600,
|
||||
'next january' => 978307200,
|
||||
'december' => 975628800,
|
||||
);
|
||||
|
||||
foreach my $test_mixed_date (sort keys %mixed_dates_to_test) {
|
||||
my $parsed_date_object = RoleTester::parse_datestring_to_date($test_mixed_date);
|
||||
isa_ok($parsed_date_object, 'DateTime', $test_mixed_date);
|
||||
is($parsed_date_object->epoch, $mixed_dates_to_test{$test_mixed_date}, ' ... represents the correct time.');
|
||||
}
|
||||
|
||||
restore_time();
|
||||
}
|
||||
};
|
||||
|
||||
done_testing;
|
||||
|
|
|
@ -14,6 +14,7 @@ ddg_goodie_test(
|
|||
)],
|
||||
'calendar' => test_zci(qr/\nS M T W T F S[ ]+[A-Za-z]+ [0-9]{4}\n.+/, html => qr#<table class="calendar".+calendar__today.+</table>#),
|
||||
'calendar november' => test_zci(qr/\nS M T W T F S November [0-9]{4}\n.+/, html => qr#<table class="calendar".+</table>#),
|
||||
'calendar november 12th' => test_zci(qr/\nS M T W T F S November [0-9]{4}\n.+/, html => qr#<table class="calendar".+</table>#),
|
||||
'calendar last november' => test_zci(qr/\nS M T W T F S November [0-9]{4}\n.+/, html => qr#<table class="calendar".+</table>#),
|
||||
'calendar next november' => test_zci(qr/\nS M T W T F S November [0-9]{4}\n.+/, html => qr#<table class="calendar".+</table>#),
|
||||
'calendar november 2009' => test_zci("
|
||||
|
|
|
@ -8,7 +8,6 @@ use DDG::Test::Goodie;
|
|||
zci answer_type => 'date_math';
|
||||
zci is_cached => 1;
|
||||
|
||||
my $year = 1900 + ( localtime() )[5];
|
||||
ddg_goodie_test(
|
||||
[qw(
|
||||
DDG::Goodie::DateMath
|
||||
|
@ -17,9 +16,8 @@ ddg_goodie_test(
|
|||
'January 1 2012 plus 32 days' => test_zci( '01 Jan 2012 plus 32 days is 02 Feb 2012' ),
|
||||
'January 1, 2012 plus 32 days' => test_zci( '01 Jan 2012 plus 32 days is 02 Feb 2012' ),
|
||||
'January 1st 2012 plus 32 days' => test_zci( '01 Jan 2012 plus 32 days is 02 Feb 2012' ),
|
||||
# 'January 1st plus 32 days' => test_zci( "January 1st $year plus 32 days is 2/2/$year" ),
|
||||
'January 1st plus 32 days' => test_zci( qr/01 Jan [0-9]{4} plus 32 days is 02 Feb [0-9]{4}/ ),
|
||||
'1/1/2012 plus 32 days' => test_zci( '01 Jan 2012 plus 32 days is 02 Feb 2012' ),
|
||||
# '1/1 plus 32 days' => test_zci( "1/1/$year plus 32 days is 2/2/$year" ),
|
||||
'1/1/2012 plus 5 weeks' => test_zci( '01 Jan 2012 plus 5 weeks is 01 Jan 2012' ),
|
||||
'1/1/2012 plus 5 months' => test_zci( '01 Jan 2012 plus 5 months is 01 Jun 2012' ),
|
||||
'1/1/2012 PLUS 5 years' => test_zci( '01 Jan 2012 plus 5 years is 01 Jan 2017' ),
|
||||
|
@ -27,8 +25,6 @@ ddg_goodie_test(
|
|||
'1/1/2012 plus 1 days' => test_zci( '01 Jan 2012 plus 1 day is 02 Jan 2012' ),
|
||||
'01/01/2012 + 1 day' => test_zci( '01 Jan 2012 + 1 day is 02 Jan 2012' ),
|
||||
'1/1/2012 minus ten days' => test_zci( '01 Jan 2012 minus 10 days is 22 Dec 2011' ),
|
||||
# 'January First plus ten days' => test_zci( "January 1 $year plus 10 days is 1/11/$year" ),
|
||||
# 'January first minus ten days' => test_zci('January 1 2014 minus 10 days is 12/22/2013'),
|
||||
);
|
||||
|
||||
done_testing;
|
||||
|
|
|
@ -21,6 +21,8 @@ ddg_goodie_test(
|
|||
'days between January 31st, 2000 and 31-Jan-2001 inclusive' => test_zci('There are 367 days between 31 Jan 2000 and 31 Jan 2001, inclusive.'),
|
||||
'days between jan 1 2012 and jan 1 123456' => undef,
|
||||
'days between jan 1 2012 and jan 1 1234' => test_zci("There are 284158 days between 01 Jan 1234 and 01 Jan 2012."),
|
||||
'days between jan 1 and jan 15 inclusive' => test_zci(qr/^There are 15 days between.+inclusive\.$/),
|
||||
'days between jan 1 and 15th feb' => test_zci(qr/^There are 45 days between.+and 15 Feb [0-9]{4}\.$/),
|
||||
);
|
||||
|
||||
done_testing;
|
||||
|
|
|
@ -160,6 +160,11 @@ ddg_goodie_test(
|
|||
# 'workdays between jan 3, 14 to jan 6, 2014 inclusive' =>
|
||||
# test_zci("There are 2 workdays between 03 Jan 2014 and 06 Jan 2014."),
|
||||
|
||||
'business days between jan 10 and jan 20' =>
|
||||
test_zci(qr"There are [1-9] workdays between 10 Jan [0-9]{4} and 20 Jan [0-9]{4}\."),
|
||||
|
||||
'business days between january and february' =>
|
||||
test_zci(qr"There are [1-9][0-9] workdays between 01 Jan [0-9]{4} and 01 Feb [0-9]{4}\."),
|
||||
|
||||
# Invalid input
|
||||
'workdays between 01/2014 01/2015' => undef,
|
||||
|
|
Loading…
Reference in New Issue