suggus has asked for the wisdom of the Perl Monks concerning the following question:

Ok Gurus,

I have an existing flat file database that contains a date in three different fields; $temp1[28] is the year (2001), $temp1[29] is the date (17), and $temp1[30] is the month (5). What I would like to do is take in those values and compare it to today's date. If that date is within 90 days, execute something. If not, then ignore it. Ultimately the snippet of code functions as a sliding window to check for items within a quarter.

Any suggestions? I don't know where to start when it comes to date manipulation.

Your monk-in-training,

Gus

Replies are listed 'Best First'.
Re: Comparing Dates With a Twist
by blakem (Monsignor) on Nov 01, 2001 at 04:11 UTC
    Here is something it get you started:
    #!/usr/bin/perl -wT use strict; use POSIX qw(mktime); my $oct7 = daysago(7,10,2001); print "October 7th was $oct7 days ago\n"; sub daysago { my $day = shift; my $month = shift; my $year = shift; my $then = mktime((localtime)[0..2], $day, $month-1, $year-1900); my $delta = time - $then; return int($delta/60/60/24); } =OUTPUT October 7th was 24 days ago

    -Blake

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Comparing Dates With a Twist (boo)
by boo_radley (Parson) on Nov 01, 2001 at 04:29 UTC
    Date::Manip has what you need : DateCalc to perform the comparison and ParseDate to merge the fields into one coherent date.
Re: Comparing Dates With a Twist
by growlf (Pilgrim) on Nov 01, 2001 at 07:05 UTC
    Hi Gus.

    Maybe I am way out in left field here, but if you are talking database, and you are probably using DBI to access it.. (such as DBD::CSV http://www.perldoc.com/perl5.6.1/lib/DBD/CSV.html) why not just use the SQL functions to query for exactly what you want? Perhaps even make the DB query a sub that takes a date-delta as an argument that it will embed in the prepared query.

    An extremely quick (and overly simplistic) example SQL statement that might do the trick for you is:
    select mydatefield, myvaluestuff from mytable where mydatefield between (CURRENT_DATE)-90 and CURRENT_DATE
    But, please be aware that I did not protect the date type in that query. You will need to determine more what you want to do in your query, what DBD to use, and use the apropriate functions in SQL.

    Optionaly, you could also use the other SQL functions to parse a partial date field if the data is not actually stored as a date in the file (i.e. your date is in it's own field) such as to_date and date_part.

    You might also gain some additional advantages by using DBI if you are not already doing so on your flatfile db.
Re: Comparing Dates With a Twist
by davorg (Chancellor) on Nov 01, 2001 at 14:03 UTC

    Let's attack this slightly differently:

    # get date 90 days ago my ($start_d, $start_m, $start_y) = (localtime(time - 90 * 86_400))[3,4,5]; my $start = sprintf('%4d-%02d-%02d', $start_y+1900, $start_m+1, $start_d); foreach (<FILE>) { my ($y, $m, $d) = (split /,/)[28,29,30]; my $date = sprintf('%4d-%02d-%02d', $y+1900, $m+1, $d); if ($start gt $date) { # record is more than 90 days old } else { # record is within 90 days } }

    Update: buckaduck has caught me writing code before drinking coffee. And didn't even point out the worst problems with the code. The new version is (hopefully) better.

    Update2: ergh! I give up. This time I was using numeric comparisons instead of string comparisons.

    --
    <http://www.dave.org.uk>

    "The first rule of Perl club is you don't talk about Perl club."

      His code has changed, and so has the error. Here was my original comment:

      What? Am I missing something here? It seems that there are two major flaws in this:

      1. I think that the comparison is backwards. If $y >= $start_y then the record is newer (not older) than the date 90 days ago.

      2. The date comparison strategy is all wrong. Would Nov. 3 come before Oct. 6, just because 3 <= 6?

      Update: And all of this assumes that there are no problems using localtime's month (zero-based) and year (minus 1900) to compare to the records...

      About the next version of the code:

      Is this method of comparing dates supposed to work in Perl? Not in my version, it doesn't. The dates '2001-10-06' and '2001-11-03' will be EQUAL in a numeric comparison. Numerically, they're both equal to 2001.

      And finally...

      Yes. This version will do nicely. I made davorg work pretty hard for my ++ this time...

      buckaduck

Re: Comparing Dates With a Twist
by {NULE} (Hermit) on Nov 01, 2001 at 04:21 UTC
    Hi,

    This is along the same lines as another post that has a lot of good discussion on the subject. There are many ways of doing this kind of thing, but I must admit to being a little biased towards my way. :) And if you read through some of the comments you'll see why.

    To give a very brief overview, many of the methods deal with converting the number into epoch seconds using timelocal and comparing the dates in epoch seconds using something like $ninetyDaysAgo = time - ($numberOfDays * 86400) and compare $ninetyDaysAgo with the value returned from timelocal.

    At any rate, check out that series of posts and you might find some good ideas!

    Good luck,
    {NULE} --
    http://www.nule.org

Re (tilly) 1: Comparing Dates With a Twist
by tilly (Archbishop) on Nov 01, 2001 at 18:23 UTC
    In my usual spirit of trying to notice something useful, I note that you are referring to locations of things in your database by position.

    Positional logic like that is notoriously hard to maintain and debug. (Quick! What is field 14? Which program were you looking at again? What if someone's output is off by one column? What if the spec changes?) If you can, you will save yourself a lot of pain by switching to name based logic, eg by using hashes instead of arrays. True, there is some overhead to doing that, but my experience is that the overhead nothing compared to the savings in development effort.

      Hi Tilly,

      I agree with your comment, but it's not my database, and that's the only logic that I've come up to parse the information. The database will remain the same though...it's just located in India instead of the US. :-) For the time being, my script is a small part of what the others (that own the DB) will be using it for.

      Thanks for all of your suggestions! I really appreciate the quick responses.

      -Gus
Re: Comparing Dates With a Twist
by suggus (Sexton) on Nov 07, 2001 at 05:19 UTC
    Ok Monks, I think I'm almost there, but I'm still having problems with the comparing dates...I've decided to go with the Date::Calc module specifically the Delta_Days function. I think I have all the code down but it's not comparing the Delta_Days is not working with my if statement. Can anyone tell me what I'm doing wrong? My guess is my loops are all *@!$ed up but I don't know where to start.

    Here's my code:
    #!/usr/local/bin/perl5 -wT print "Content-type: text/html\n\n"; use Date::Calc qw(Delta_Days); @Months = ('1','2','3','4','5','6','7','8','9','10','11','12'); ($Month_Day,$Month,$Year) = (localtime)[3,4,5]; $Year += 1900; open(HANDLE,"../script_integration_data/script_integration_records"); while (<HANDLE>) { @temp1 = split(/:/); $domain = unpack ("A4", $temp1[40]); $day_diff = Delta_Days($temp1[28], $temp1[30], $temp1[29], $Year, $ +Month, $Month_Day); if (($domain eq "BBA") && ($temp1[36] eq "In Production") && ($day_ +diff <= 90)) { $sum += $temp1[38]; $count++; } } close(HANDLE); $average = $sum / $count; if ($average >= 44) { print sprintf "<blink><font color=\"#FF0000\">%.f days</font></blin +k>", $average; } elsif (($average >= 11) && ($average <= 44)) { print sprintf "<font color=\"#FFFF00\">%.f days</font>", $averag +e; } elsif ($average <= 10) { print sprintf "<font color=\"#669900\">%.f days</font>", $average; } # EOF
    Much Thanks!

    -Gus