С Perl, используя DateTime
use warnings;
use strict;
use feature 'say';
use DateTime;
my $dt = DateTime->new(year => 2020, month => 1, day => 1);
my $first_sunday = 7 - $dt->day_of_week + 1; # day of month for first Sun
while (1) {
my $day = $dt->day;
if ($day >= $first_sunday and $day < $first_sunday + 7) {
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
continue {
$dt->add(days => 1);
if ($dt->day == 1) { # new month
last if $dt->month > 3;
$first_sunday = 7 - $dt->day_of_week + 1;
}
}
Это сохраняет состояние (первое за месяц в определяет, какой день первое воскресенье), что довольно подходит, если программа предназначена для генерации и go по всем датам из интересующего диапазона.
С другой стороны, программе может потребоваться проверка для данного дня; возможно, он работает ежедневно и на этот день нужно проверять. Тогда проще увидеть, находится ли день между первым и вторым воскресеньями месяца
my $dt = DateTime->today;
while ( $dt->add(days => 1)->month <= 3) {
if ($dt->day_of_week == 7) { # it's a Sunday
if ($dt->weekday_of_month == 1) { # first Sunday in the month
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
else {
my $sdt = $dt->clone; # preserve $dt
$sdt->subtract( $dt->day_of_week ); # drop to previous Sunday
if ($sdt->weekday_of_month == 1) { # was first Sunday in the month
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
}
while
l oop вокруг кода используется для облегчения проверки.
Для других дней, кроме воскресенья, мы переходим к прошлому воскресенью, чтобы проверить, было ли это первое воскресенье месяца. Если так, то наш день находится в нужном интервале. Если день равен воскресенью, нам нужно только проверить, является ли он первым в месяце.
Код можно сделать немного более эффективным и кратким, если это имеет значение
if ( (my $dow = $dt->day_of_week) == 7) {
if ($dt->weekday_of_month == 1) {
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
elsif ( $dt->clone->subtract(days => $dow)->weekday_of_month == 1 ) {
say $dt->ymd, " (", $dt->day_abbr, ")";
}
... за счет читаемости.