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

Or, What a fool am I.

I am writing a little script to pull some stuff out of my company's weblog files. They're named in the form companyname.com-%Y%m%d_log, e.g., companyname.com-20020423_log.

I want to let the user of the script tell it whether to work on this month's logs, or last month's.

Here is how I am setting the date string (to grep the right files) right now, depending on whether the user asked for this month or last:

$this_month = strftime ($format, localtime()); $last_month = strftime ($format, localtime (time - 2678400));

It seems like there should be a better way, but I (as a Perl non-genius) haven't been able to find it.

Of course, I could just cleanup the logfile directory, but what fun would that be?

Replies are listed 'Best First'.
Re: where does the time go?
by ferrency (Deacon) on Apr 24, 2002 at 15:33 UTC
    As you probably know, using epoch timestamps and adding/subtracting constant periods of time isn't accurate on the scale of months: February is a lot shorter than December. And then there are leap years and so on...

    What you really want is something which works on the granularity of days and months, not seconds. You could write your own, but before you do that you'll probably want to check out the modules Date::Format, Date::Calc, Date::Parse, and maybe even Date::Tie. Date::Calc is good at doing date/time math that's actually correct, but it depends on receiving dates pre-parsed as YYYY, MM, DD, etc. Some of the other modules can get you the results Date::Calc is looking for.

    Alan

Re: where does the time go?
by johannz (Hermit) on Apr 24, 2002 at 18:43 UTC

    No one really seriously talked about Date::Manip, one of my favorite modules. It sometimes gets a bad rap since it isn't the fastest module, but speed won't be critical for what you are doing.

    The following should be adaptable to what you need.

    #!perl use strict; use warnings; use Date::Manip; local $\ = "\n"; Date_Init('TZ=US/Mountain'); my $month = UnixDate('last month', '%B'); my @dates = ParseRecur("every day in $month"); print UnixDate($_, '%D') for @dates;
Re: where does the time go?
by DaWolf (Curate) on Apr 24, 2002 at 15:37 UTC
    Do it like this:
    @t = (localtime)[0..5]; $this_month = $t[4]+1; $previous_month = $t[4];
    Note that the fourth position of the array contains the month number minus one. This happens because everything starts from 0 here. So the months are 0 to 11.

    Since it always return the actual date based on the computer is running, you can play with the other elements of @t to find out what they are ;)

    Best regards,

    Er Galvão Abbott
    a.k.a. Lobo, DaWolf
    Webdeveloper
      Sorry, DaWolf, that won't work.

      If you're in March, on the 29th, 30th, or 31st, then the previous date you'd get your way won't work for Feb, 11 out of 12 times. (The 29th will work every 4 years, at least from now until 2100 :))

      Obviously, you'll have the same problem every time a month with 31 days follows a month with 30, as well...

      Date::Calc is clearly the way to go for this problem.
      --
      Mike

        Ahh, while the deeper parts of this thread explain why that's a nasty thing to do for specific dates, in this particular application you should be fine. Our noble friend Anonymous Monk is just looking to roll the month back, so he can take those month values and work with them. Of course, he'll want to check to see if he's working with December and January, but otherwise, grabbing the month (and throwing a zero onto the front of a short one) should suffice. Working with the previous code (hacking stupidly, rather than grabbing a pretty module here), and handling both this and last month...
        ($month, $year) = (localtime)[4..5]; # convert from index to 'normal' $this_months_year = $year + 1900; $last_months_year = $this_months_year; $this_month = $month + 1; # fix month if = 0 $last_month = $month; $last_month or $last_month = 12; # roll back year if this month is january ($this_month == 1) and $last_months_year--; $this = sprintf("%04d%02d", $this_months_year, $this_month); $last = sprintf("%04d%02d", $last_months_year, $last_month); @files_this = <companyname.com-$this??_log>; @files_last = <companyname.com-$last??_log>;

        I think that looks a bit ugly, but that should at least illustrate why it's a good idea to peek into those date related modules. As a sidenote.. did I miss anything else in there, or should that ugly hack do the trick?

        -=rev=-
        Let me understand this.

        Shouldn't localtime return a value based on the OS date? So, how come it won't work?

        I need to clear this out.

        Er Galvão Abbott
        a.k.a. Lobo, DaWolf
        Webdeveloper
      The problem with this is, you get 0 for "December of last year", which probably won't do what you want.

      Yes you can do it yourself fairly easily, but there are already modules which do this correctly, so you might as well not reinvent the wheel unless yours is a different shape.

      Alan

        ...okay then, how about this?

        You didn't like this:

        @t = (localtime)[0..5]; $this_month = $t[4]+1; $previous_month = $t[4];

        But if it is changed slightly,

        $this_month = (localtime)[4]; $previous_month = $this_month++ ? $this_month-1 : 12;

        This idea raises hackles for me because it depends upon order of evaluation, and it "cleverly" hides the logic of its decision mechanism (as the conditional operator often does), but it does account for the border case.

        ---v