I was working with Perl DateTime Module, I was wondering how will I calculate First, Second, Third, Fourth and Last Sunday's, Monday's,... Saturday's of specified month.
My Approach:
Run a Loop from i= 1...DateTime->last_day_of_month( ... ) .
Assign date i to date($dt) and fetch the day of week using $dt->day_of_week().
Use counter to keep track of first, second, third, fourth, last.
Break the loop if day of week matches the desired day and counter matches the desired interval.
Can you suggest a better(or short) approach than above? Any help is appreciated.
This is a straightforward modification of my answer for finding the previous Monday (or any specified day of the week). The only difficulty is figuring out what date you start with.
use DateTime;
# Here $nth is 1, 2, 3... for first, second, third, etc.
# Or -1, -2, -3... for last, next-to-last, etc.
# $dow is 1-7 for Monday-Sunday
# $month is 1-12
sub nth_day_of_month {
my ($nth, $dow, $year, $month) = @_;
my $date = ($nth > 0
# For 1st etc. we want the last day of that week
# (i.e. 7, 14, 21, 28 ...). We have to use add because
# the last week may extend into next month.
? DateTime->new(year => $year, month => $month, day => 1)
->add( days => $nth * 7 - 1)
# For last etc. we want the last day of the month
# (minus a week if next-to-last, etc)
: DateTime->last_day_of_month(year => $year, month => $month)
->add( weeks => $nth + 1)); # $nth is negative
# Back up to the first $dow on or before $date
$date->subtract(days => ($date->day_of_week - $dow) % 7);
# If we're not in the right month, then that month doesn't have the
# specified date (e.g. there's no 5th Tuesday in Sept. 2013).
return (($date->month == $month) ? $date : undef);
}
Update: Here's a slightly more efficient version. It's using the same algorithm, but it's combining the calls to add and subtract, so it only has to do date math once.
sub nth_day_of_month {
my ($nth, $dow, $year, $month) = @_;
my ($date, $delta);
if ($nth > 0) {
# For 1st etc. we want the last day of that week (i.e. 7, 14, 21, 28, "35")
$date = DateTime->new(year => $year, month => $month, day => 1);
$delta = $nth * 7 - 1;
} else {
# For last etc. we want the last day of the month
# (minus a week if next-to-last, etc)
$date = DateTime->last_day_of_month(year => $year, month => $month);
$delta = 7 * ($nth + 1); # $nth is negative
}
# Back up to the first $dow on or before $date + $delta
$date->add(days => $delta - ($date->day_of_week + $delta - $dow) % 7);
# If we're not in the right month, then that month doesn't have the
# specified date (e.g. there's no 5th Tuesday in Sept. 2013).
return (($date->month == $month) ? $date : undef);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With