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

First off, I'm sorry if there is some module that will do this for me. I'm not very good at searching CPAN. Secondly, my search here didn't help with my particular question. Not saying this hasn't been asked here, just saying I didn't have much luck (or patience) in finding it.

I want to know how many Mondays and Tuesdays and all are in a given date range. So far my mind has worked up the following code. I'm wondering if there is a quicker way or a module way of getting this.

my $days; # This is set to the number of days in the range. # However you get this, it is just end date - start # date in days. my $firstday; # This is a number (1-7) telling which day of the # week the range starts on. For example, if the first # day is today (Tuesday), then this is set to 2. my @daycounts; # This will be an array with seven elements, each one # holding the number of occurances of that day in the # range for(my $i=1; $i<8; $i++){ if($i<$firstday){ $daycounts[$i]=$days/7; } else { $daycounts[$i]=($days+$firstday-$i)/7; if (($days+$firstday-$i)%7>0) { $daycounts[$i]++; } } }

'Course, you can rearrange the ifs if you like. You could do all of that work with one if( || ). As far as I know that code should work. I just don't know if it is the best.

I was also thinking there was a division operator that would only return the interger part dropping any remainder. I thought it was \, but I'm not finding doco for that in Perl, so I must be thinking a different language. I would've used \ in place of all of the /s. I'll have to find some way to cast the result of the divisions to integers instead of floats, but that is easy.

Replies are listed 'Best First'.
Re: How Many Mondays in Date Range?
by Cubes (Pilgrim) on Aug 01, 2001 at 01:57 UTC
    Check out Date::Manip at CPAN. Something in there ought to simplify your life.
      I'm using DateManip for ParseDate and UnixDate. I'm not finding a routine to do this. Are you using ParseRecur? -Travis
Re: How Many Mondays in Date Range?
by tachyon (Chancellor) on Aug 01, 2001 at 21:24 UTC

    Once again we use day 0-6. I have added a hash to show the output in a nice format. Oh you can do it in a one liner as shown :-). The advantage of 0-6 for the days is that we can use modulus to good effect to generate our 7 indexes (0-6) for the days. You could always copy element 0 of the day count array to element 7 if you are really set on having Sunday as day 7 or add 1 to the mod and iterate from ($firstday-1)..($days+$firstday-2) but you do waste element 0 in your array.

    my $days = 71; my $firstday = 5; my @daycounts = (0) x 7; # initialise 7 array elements to 0 for my $day_num( $firstday .. ($days+$firstday-1) ) { $daycounts[$day_num%7]++; } # now print it out nice and pretty my %weekdays = ( 1 => Monday, 2 => Tuesday, 3 => Wednesday, 4 => Thursday, 5 => Friday, 6 => Saturday, 0 => Sunday); for my $day_num(0..6) { print "$daycounts[$day_num]\t$weekdays{$day_num}\n"; } # here it is as a one liner $daycounts[$_%7]++ for $firstday..($days+$firstday-1);
    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      In this, I am getting the answer I expect. As I said before, I'm using Date::Manip, which is why I'm using 1..7. But I can +/- 1 as necessary, though I do need to make Monday 0 and Sunday 6. I like the one liner, since this is going to be put into an ASP, that helps.

      Though you solution does require it step through the date range. That can be annoying for very large date ranges.

      -Travis

Re: How Many Mondays in Date Range?
by suaveant (Parson) on Aug 02, 2001 at 00:20 UTC
    Maybe I'm mistaken... but if I read your problem right this should do it...
    $daycounts[($firstday+$_-1)%7] = int(($days+6-$_)/7) for(0..6);
    BTW: this takes monday as 1, sunday as 7 but outputs a seven elemet array going from monday to sunday... I'm not sure if that is what you wanted, but it seemed like it was

                    - Ant

      This works very well. It doesn't require a lop larger than 7. But I take out the -1 to get

      $daycounts[($firstday+$_)%7] = int(($days+6-$_)/7) for(0..6);

      And get an array where Monday is 0 and Sunday is 6. This way I can use the UnixDate result -1 to match it to this result.

      -Travis

        I put the -1 in because you said $firstday went from 1-7, was I wrong?

                        - Ant

Re: How Many Mondays in Date Range?
by runrig (Abbot) on Aug 01, 2001 at 20:23 UTC
    This seems to be a little better, benchmarkwise (it assumes days are numbered 0-6):
    use strict; # Days start at 0, just like arrays my $start_day = 6; my $days = 7; my @cnts = daycounts($start_day, $days); for (0..6) { print "$_: $cnts[$_]\n"; } sub daycounts { use integer; my $start_day = shift; my $days = shift; my $end_day = ($start_day + $days) % 7; my $compare = ($start_day <= $end_day) ? sub { my $num = shift; $start_day <= $num and $num <= $end_day } : sub { my $num = shift; $num <= $end_day or $start_day <= $num }; my @adjust = map { $compare->($_)? 1 : 0 } 0..6; my $cnts = $days / 7; map { $cnts + $adjust[$_] } 0..6; }
    Update: There. fixed. Except now its slower :( though if you unroll that sub reference/dereference and map into two separate maps then its faster :)

    Another update: Re:ThuG's reply - I made the assumption that zero days means just the start day, and 1 day means the start day and the next day, so 17 days from Wednesday would take you to Saturday, not Friday. So just subtract one from $days coming in to get what you expect.

      I'm using 1..7 because Date::Manip returns 1 for the first day of the week, not 0. Yes, I loose the 0 index. That isn't a problem since I can very easily add or subtract 1 as needed to convert between Date::Manip and this code.

      When I step through your code, which by the way, took some work for me since you are using much Perl mojo that I'm just not used to using, I am not getting the answer I expect.

      If you start on Wednesday ($start_day = 2) and run to the third Friday ($days = 17), then you should have two Mondays, Tuesdays, Saturdays, and Sundays, and three Wednesdays, Thrusdays, and Fridays. I expect 2, 2, 3, 3, 3, 2, 2, but I get 2, 2, 3, 3, 3, 3, 2.

      -Travis

Re: How Many Mondays in Date Range?
by tachyon (Chancellor) on Aug 02, 2001 at 18:52 UTC

    Here is an update that does exactly as you ask (Monday = 1 and Sunday = 7) and iterates a maximum of 6 times - ie it adds initialises the array with int($days/7) and just adds the remaining 0-6 to the appropriate elements. This is as fast and efficient as it gets.

    my $days = 72; my $firstday = 5; # for each 7 days each element gets a value of +1 # initialise 8 array elements my @daycounts = (int($days/7)) x 8; $days = $days%7; for my $day_num( ($firstday-1) .. ($days+$firstday-2) ) { $daycounts[1+$day_num%7]++; } # now print it out nice and pretty my %weekdays = ( 1 => Monday, 2 => Tuesday, 3 => Wednesday, 4 => Thursday, 5 => Friday, 6 => Saturday, 7 => Sunday); for my $day_num(1..7) { print "$daycounts[$day_num]\t$weekdays{$day_num}\n"; } # here it is as a one liner $daycounts[1+$_%7]++ for ($firstday-1)..($days+$firstday-2);

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: How Many Mondays in Date Range?
by scain (Curate) on Aug 02, 2001 at 19:00 UTC
    I'm surprised no one mentioned Date::Calc yet. It is well geared towards this type of problem and there are even examples that a similar to this problem.

    Scott