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

I'm trying to generate a dynamic report showing default allocations for resources (in this case, Justices). The user has the option of choosing a report period of:

In any scenario, for example, "July - August", I need to start reporting from the first Monday's date in July, to the last Friday's date in August, with each cell in the report being one Mon - Fri week.

I have the stored procedure to fetch the data I need, I can populate the list of justices I need, but I CAN'T figure out an algorithm for getting the set of dates such that if the first day in July was a Thursday the breakdown would be:

     Thu 1 - Fri 2, Mon 5 - Fri 9, Mon 12 - Fri 16, etc.

Or if the first day was a Tuesday:

     Tue 1 - Fri 4, Mon 7 - Fri 11, Mon 14 - Fri 18, etc.

These dates are the headings above each cell of the report.

Any ideas?

Replies are listed 'Best First'.
Re: Getting Weekly Dates
by oakley (Scribe) on Mar 09, 2001 at 01:01 UTC
    Like Coyote said, use Date::Manip - it'a a darn good module and it will do alot more than what you would probably expect.

    Now though - to get a list of the first Monday in every month starting Jan 2001 - Dec 2001, I used the following snippet of code and got every single one:
    #!/usr/bin/perl -w use strict; use Date::Manip; my $base = "2000123123:59:59"; my $start = "2001010100:00:00"; my $stop = "2001123123:59:59"; my $syntax = "0:1*1:1:0:0:0"; my @dates = &ParseRecur($syntax,$base,$start,$stop); foreach my $day (@dates) { print "\$day is [$day]\n"; }
    This prints out:
    $day is [2001010100:00:00] $day is [2001020500:00:00] $day is [2001030500:00:00] $day is [2001040200:00:00] $day is [2001050700:00:00] $day is [2001060400:00:00] $day is [2001070200:00:00] $day is [2001080600:00:00] $day is [2001090300:00:00] $day is [2001100100:00:00] $day is [2001110500:00:00] $day is [2001120300:00:00]
    And yes, I did verify the dates :)

    Update:You may want to use substr($day,0,8); to get the first 8 characters which is the date - anything after that is the time
    - oakley
    Embracing insanity - one twitch at a time >:)
      I think you're working at that too hard:
      use Date::Manip; print map UnixDate($_, "%g\n"), ParseRecur("first monday of every mont +h in 2001");
      which yields
      Mon, 01 Jan 2001 00:00:00 -0800 Mon, 05 Feb 2001 00:00:00 -0800 Mon, 05 Mar 2001 00:00:00 -0800 Mon, 02 Apr 2001 00:00:00 -0800 Mon, 07 May 2001 00:00:00 -0800 Mon, 04 Jun 2001 00:00:00 -0800 Mon, 02 Jul 2001 00:00:00 -0800 Mon, 06 Aug 2001 00:00:00 -0800 Mon, 03 Sep 2001 00:00:00 -0800 Mon, 01 Oct 2001 00:00:00 -0800 Mon, 05 Nov 2001 00:00:00 -0800 Mon, 03 Dec 2001 00:00:00 -0800
      Date::Manip does everything. From the docs:
      I'm trying to build a library which can do _EVERY_ con- ceivable date/time manipulation that you'll run into in everyday life.

      -- Randal L. Schwartz, Perl hacker

        ...If only Date::Manip wouldn't whine about not being able to figure out my timezone. :( (on NT4WS)
Re: Getting Weekly Dates
by japhy (Canon) on Mar 09, 2001 at 01:11 UTC
    Eww. I don't think you should use Date::Calc or Date::Manip for this. I think you should use Time::Local.
    use Time::Local; sub getFirstNonWeekend { my ($month, $year) = @_; # (7,2001) for July 2001 # noon on the 1st of the month my $first = timelocal(0,0,12, 1, $month-1, $year-1900); my $shift = ((localtime $first)[6] + 1) % 7; if ($shift < 2) { $first += 86400 * (2 - $shift) if $shift < 2; return ($first, 3 - $shift, 1); } return ($first, 1, $shift - 1); }
    That will give you the time for (around) noon on the first day of a given month that is NOT a weekend, the day of the month, and a number representing the day of the week (1 = Monday, 5 = Friday). That's probably the hardest part.

    What's left is the partitioning of this month and the next. Since you know the day of the month and the day of the week, this shouldn't be too hard. I wouldn't mind showing you the code for it though, if you ask.

    japhy -- Perl and Regex Hacker

      Or, as a 'one-liner':
      perl -MTime::Local -wle ' for ($x = timelocal(0,0,0,1,0,2001); $x < timelocal(0,0,0,1,0,2002) +; $x += 24*60*60) { (localtime($x))[6] % 6 and print substr(localtime($x),0,10) }'
      Very neat!

      p
        Wow. Succinct, beautiful, and far less involving. I'm impressed. But you might want to use (0,0,12) instead of (0,0,0) for the time -- just in case you run into the daylight savings time switch.

        japhy -- Perl and Regex Hacker
      Under request of mothra, I'm posting my code to show the partitioning of a month into weekday weeks. It is not the entire solution to the problem, since mothra wants two-month spans, but that can be done with little effort. It's in the Code Catacombs, Week Partitioner.

      japhy -- Perl and Regex Hacker
Re: Getting Weekly Dates
by Tuna (Friar) on Mar 09, 2001 at 00:31 UTC
    See Date::Calc, for starters.

    UPDATE:

    I cooked up a solution that would work for my personal file naming convention.

    Consider a directory full of files like: summary.interfaces.yyyymmdd
    I could run the following code, and simply grep this directory for all files ending in "$period".
    #!/usr/local/bin/perl -w use strict; #Usage: $0 <yyyy> <1 2 3> #period 1,2,3, that is die "Usage: $0 <yyyy> <1 2 3>, #period 1,2,3, that is\n" unless @ARGV +==2; my $prepend = $ARGV[0]; my $per = $ARGV[1]; my @period; my @files; if ($per eq 1) { @period = ("$prepend"."0101", "$prepend"."0630"); } elsif ($per eq 2) { @period = ("$prepend"."0701", "$prepend"."0831"); } elsif ($per eq 3) { @period = ("$prepend"."0901", "$prepend"."1231"); } else {die "Usage: $0 <yyyy> <1 2 3>, #period 1,2,3, that is\n";} foreach my $element(@period) { print "$element\n"; } my $from = $period[0]; my $to = $period[1]; while ( $from < $to ) { print "$from\n"; } continue { $from++; }
    Certainly not the prettiest, but it would work for me.

    HTH,

    Steve

    update again:corrected typo
      Good try, but the year is not constant. :)
(tye)Re: Getting Weekly Dates
by tye (Sage) on Mar 09, 2001 at 00:35 UTC

    for example, "July - August", I need to start reporting from the first Monday's date in July, to the last Friday's date in August

    Not Perl-related but, under that scheme you will never report for weeks that cross the first of September. You probably want from the first Monday in July through the Friday after the last Monday in August, for example.

            - tye (but my friends call me "Tye")
      Yes, sorry, that's what I meant. :) (Or, ideally, just up to the last weekday of the last month of the reporting period, but either case is ok)

      My mind is just drawing a serious blank on this task. But Date::Calc seems like an interesting place for me to start.

Re: Getting Weekly Dates
by Coyote (Deacon) on Mar 09, 2001 at 00:37 UTC
    Tuna alread mentioned Date::Calc. You also might want to check out Date::Manip. I've used it fairly extensively for problems like you mentioned above and its always done the job.

    ----
    Coyote

Re: Getting Weekly Dates
by merlyn (Sage) on Mar 09, 2001 at 06:13 UTC
    You know, I'll have to say I was initially disappointed in the content of this node having read such a promising subject line. I was thinking Perl could help me with my social life finally!

    {grin}

    -- Randal L. Schwartz, Perl hacker