From 3656531632353d5dec7deff18413873aadbdbaa2 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 8 May 2012 19:01:16 -0700 Subject: [PATCH] add/subtract days/weeks/months/years to/from a date --- lib/DDG/Goodie/DateMath.pm | 84 ++++++++++++++++++++++++++++++++++++++ t/DateMath.t | 27 ++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 lib/DDG/Goodie/DateMath.pm create mode 100644 t/DateMath.t diff --git a/lib/DDG/Goodie/DateMath.pm b/lib/DDG/Goodie/DateMath.pm new file mode 100644 index 00000000000..e7887bf59e5 --- /dev/null +++ b/lib/DDG/Goodie/DateMath.pm @@ -0,0 +1,84 @@ +package DDG::Goodie::DateMath; +# ABSTRACT add/subtract days/weeks/months/years to/from a date + +use DDG::Goodie; +use Date::Calc qw( Add_Delta_Days Add_Delta_YM This_Year ); + +triggers any => qw( plus minus + - ); +zci is_cached => 1; +zci answer_type => 'date_math'; + +handle query_lc => sub { + my @param = split /\s+/; + return unless scalar @param == 4; + + my ( $date, $action, $number, $unit ) = @param; + + # check/tweak input + + # default to this year: + my $md_re = '[01]?[0-9]/[0-3]?[0-9]'; + $date .= '/' . This_Year() if $date =~ /^$md_re$/; + return unless $date =~ /^$md_re\/[0-9]{4}$/; + + my %action_map = ( + plus => '+', + '+' => '+', + minus => '-', + '-' => '-', + ); + $action = $action_map{$action} || return; + + return unless $number =~ /^\d+$/; + $number = 0 - $number if $action eq '-'; + + $unit =~ s/s$//g; + + # calculate answer + my $sub_map = { + # sub parameters: year, month, day, number + day => sub { + return Add_Delta_Days( @_ ); + }, + week => sub { + $_[3] *= 7; # weeks to days + return Add_Delta_Days( @_ ); + }, + month => sub { + $_[4] = $_[3]; # months + $_[3] = 0; # 0 years + return Add_Delta_YM( @_ ); + }, + year => sub { + push @_, 0; # 0 months + return Add_Delta_YM( @_ ); + }, + }; + + my $sub = $sub_map->{$unit} || return; + my $answer; + eval { + $answer = format_ymd( $sub->( mdy_to_ymd_array( $date ), $number ) ); + }; + return if $@; + + # output, using original @param + $param[0] = $date; # include the optional year + $param[3] = $unit; + $param[3] .= 's' if $param[2] > 1; # plural? + return "@param is $answer"; +}; + +sub mdy_to_ymd_array +{ + my @date = split '/', shift; + return ( $date[2], $date[0], $date[1] ); +} + +sub format_ymd +{ + # ymd array to m/d/y + return join '/', $_[1], $_[2], $_[0]; +} + +1; diff --git a/t/DateMath.t b/t/DateMath.t new file mode 100644 index 00000000000..43371037da2 --- /dev/null +++ b/t/DateMath.t @@ -0,0 +1,27 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use Test::More; +use DDG::Test::Goodie; + +zci answer_type => 'date_math'; +zci is_cached => 1; + +ddg_goodie_test( + [qw( + DDG::Goodie::DateMath + )], + '1/1/2012 plus 32 days' => test_zci( '1/1/2012 plus 32 days is 2/2/2012' ), + '1/1 plus 32 days' => test_zci( '1/1/2012 plus 32 days is 2/2/2012' ), + '1/1/2012 plus 5 weeks' => test_zci( '1/1/2012 plus 5 weeks is 2/5/2012' ), + '1/1/2012 plus 5 months' => test_zci( '1/1/2012 plus 5 months is 6/1/2012' ), + '1/1/2012 PLUS 5 years' => test_zci( '1/1/2012 plus 5 years is 1/1/2017' ), + '1/1/2012 plus 1 day' => test_zci( '1/1/2012 plus 1 day is 1/2/2012' ), + '1/1/2012 plus 1 days' => test_zci( '1/1/2012 plus 1 day is 1/2/2012' ), + '1/1/2012 + 1 day' => test_zci( '1/1/2012 + 1 day is 1/2/2012' ), + '1/1/2012 minus 10 days' => test_zci( '1/1/2012 minus 10 days is 12/22/2011' ), +); + +done_testing; +