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

I'm not quite sure how to word this question, but I'm looking for the best way to get a date from the current date using how many days ago. For example, if the current date is 2005-03-29.

1 day ago: 2005-03-28

7 days ago: 2005-03-21

14 days ago: 2005-03-14

etc ...

Basically, I want to cover from 1 day ago to 30 days ago. I'm sure there are some Date modules, but I'm not sure the best one to use for this purpose.

Thanks a bunch all !!

~~~~~~~~~~~~~

Thanks all for your help. With a collection of all your feedback, I implemented the following code.

use Date::Manip my $format = "%Y-%m-%d"; my $time = "1 day ago"; my $date = &UnixDate(ParseDate($time), $format); print "$date\n";
__OUTPUT__

2005-03-29

Replies are listed 'Best First'.
Re: Getting previous dates from the current date
by cazz (Pilgrim) on Mar 29, 2005 at 16:09 UTC
      Actually, don't even bother with any of the modules. simple math is all you need.
      my $NUM_DAYS = 7; my $time = time(); print scalar localtime ($time - (60*60*24*$NUM_DAYS));
      Then just strftime to whatever format you need.
        simple math is all you need.

        Except that the mathematics of date-handling is rarely simple. In this case you've made the error of assuming there are 24 hours in the day.

        In the UK, for example, the clocks have just moved forwards by an hour for British Summer Time: Sunday only had 23 hours in it.

        Most of the time your code will work just fine. But if it happens to be run in the first hour of a day then it will yield the wrong output, skipping a day.

        For example, if I ran it at 00:17 early tomorrow (Wednesday) morning with $NUM_DAYS set to 2 then it would correctly output:

        Mon Mar 28 00:17:00 2005

        But setting $NUM_DAYS to 3 would yield:

        Sat Mar 26 23:17:00 2005

        The original poster just wanted dates; if you truncate the time portions off the above then you've ended up skipping a day, jumping straight from Monday to Saturday!

        Now it would be possible to fix this, but it isn't worth trying when so many hours of hard thinking have already been put into getting these sorts of things write by the creators of the DateTime module and friends.

        Do not try to do arithmetic with dates. You will get it wrong.

        Smylers

        Rather than time(), I'd suggest using...

        use Time::Local; my $time = timelocal(0, 0, 12, (localtime)[3, 4, 5]);

        This'll make midday your starting point for calculations, which helps sidestep the whole daylight savings issue Smylers pointed out.

        As an added bonus, Time::Local is also part of the core distribution, so you don't need to install anything via CPAN (which is good, as some of those other more fancy Date:: modules are mighty bloaty!).

            --k.


Re: Getting previous dates from the current date
by dragonchild (Archbishop) on Mar 29, 2005 at 16:11 UTC
    DateTime is also very good. That, combined with the two mentioned above, are the three "standard" datetime modules in Perl. Each has its proponents and many of us will use more than one, depending on the project.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      DateTime is also very good.

      I agree; the DateTime suite is excellent: it has a very well-thought-out interface, does pretty much anything that any of the other modules do, and correctly covers all the awkward corners of date- and time-handling that you don't want to think about.

      This thread has a bunch of different responses, but none of them look to me anywhere near as simple as:

      print DateTime->now->subtract(days => 7)->date

      Update: And, after actually bothering to read the other suggestions (rather than just noticing how complicated they look), the hand-rolled ones (avoiding modules for the date arithmetic) all seem to be wrong as well. So that's another advantage of DateTime: it quietly gets correct things that you haven't even bothered to think about.

      Smylers

Re: Getting previous dates from the current date
by tlm (Prior) on Mar 29, 2005 at 16:12 UTC

    Time::Piece has been bery bery good to me.

    the lowliest monk

Re: Getting previous dates from the current date
by dmorelli (Scribe) on Mar 29, 2005 at 16:30 UTC
    Try this:

    #! /usr/bin/perl -w use strict; use Date::Manip qw(ParseDate); print ParseDate("1 day ago") . $/; print ParseDate("7 days ago") . $/; print ParseDate("14 days ago") . $/;

    Date::Manip is fun to mess around with, I'm surprised at the kinds of "fuzzy" descriptions it can handle.

    update: I didn't search carefully enough to see that cazz had already mentioned this module.

    update: In the interest of good form, added the import list. Is it kinda uncool that ParseDate is exported by default?

Re: Getting previous dates from the current date
by cog (Parson) on Mar 29, 2005 at 16:13 UTC
    Yes, there are modules to do that directly and you should use them.

    However, a small "proof of concept" won't hurt anyone:

    my $date = '2005-03-29'; my $d = ...; # anyone knows of a direct way to do this? for (1, 7, 14) { my $d = $d - $_ * 60 * 60 * 24; printf ("$_ days ago: %04d-%02d-%02d",(localtime($d))[5,4,3]); }
    Warning: The code presented doesn't consider daylight saving times changes.
      However, a small "proof of concept" won't hurt anyone

      Yes it will — it will hurt people who see your code and think that it works! Didn't the clocks change in Portugal on Sunday as well? You can't assume that days will have exactly 24 hours in them.

      Smylers

        Well, I do say it's a proof of concept, and I even underlined it to note that...

        But you are, of course, correct. People look at code and copy-paste it without looking at nothing else, so... I hope they will at least notice the bold warning at the bottom, which was just placed there.

Re: Getting previous dates from the current date
by FitTrend (Pilgrim) on Mar 29, 2005 at 16:13 UTC

    Pretty easy. There are 86400 seconds in a day.

    $time = time() - (86400 * $numOfDays); ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($tim +e); $mon++; $year = $year + 1900;
    or
    $time = time() - (86400 * $numOfDays); $realTime = localtime($time); print scalar $realTime;
      Pretty easy.

      Not in the general case it isn't. Date and time calculations seem to be one of those things that sound misleadingly simple, but actually turn out not to be.

      There are 86400 seconds in a day.

      Not always. This code will not work correctly if it spans changing the clocks for daylight-saving (see a previous reply above for a detailed example).

      Smylers

      You are right. This code does not account for dst. Using the same code and adding 10 days (which we change the clocks next weekend), it will output 10 days and 1 hour.

      To counter act this, determining the first weekend and april and last weekend in october will allow a user to adjust for this. Or you can use a module.

        To counter act not accounting for DST, determining the first weekend and april and last weekend in october will allow a user to adjust for this.

        Well it would do, so long as you're in a place that uses those dates. But it would still be wrong in the EU, for example, where the clocks go back on the last weekend of March, not the first one in April.

        And presumably it'd be even more wrong in the southern hemisphere, where I'm guessing the clocks change in the opposite direction?

        Now you could do lots of research and then allow for all these things, or ...

        Or you can use a module.

        Yes. Use a module. Modules are good!

        Smylers

Re: Getting previous dates from the current date
by tcf03 (Deacon) on Mar 29, 2005 at 16:43 UTC
    This should work - I had a similar question here a while back and this is what I came up with - with the help of others...
    use POSIX qw(strftime); my $thisday = time; my $SixtyDaysPrevious = $thisday - 60 * 24 * 60 * 60; $SixtyDaysPrevious = strftime "%m/%d/%Y", ( localtime($SixtyDaysPre +vious) );
      This should work

      But it won't do all the time: you've fallen for the apparently common trap of assuming that days have 24 hours in them, then using localtime.

      More generally, you've fallen for the trap of trying to write this stuff yourself rather than trusting the DateTime geniouses to have got it right for you, so that you don't need to think about such awkwardnesses.

      Smylers

        should being the operative word. Its worked to my advantage that just enough was good enough.
Re: Getting previous dates from the current date
by VladSu (Acolyte) on Mar 31, 2005 at 12:04 UTC
    Hi.

    In another your thread <url> http://www.perlmonks.org/?node_id=443499 </url> I gave few function which you can use for this task also
    my $daysBack = 14; printf "%04d-%02d-%02d", timenow( time() - $daysBack * 86400 );
    Vlad