Календарь
Как построить календарь на месяц.

Программа, описанная в этой статье, формирует в виде таблицы календарь на любой месяц (почти) любого года. Числа расположены в порядке, принятом в России, то есть дни недели — сверху вниз, а первая строка таблицы — понедельник.

Использование

Всю работу выполняет функция calendar(), которой необходимо передать три аргумента — день, месяц и год (в «естественной» нумерации, то есть числа и дни отсчитываются с единицы; год — четырехзначное число):

my ($mday, $mon, $year) = (localtime (time)) [3..6];
$mon++;
$year += 1900;

&calendar ($mday, $mon, $year);

Числа месяца до указанного в первом параметре выводятся полужирным шрифтом.

Описание работы

В начале работы переданные аргументы копируются в переменные $mday, $mon и $year.

sub calendar{
   my ($mday, $mon, $year) = @_;

Затем определяется следующий месяц. Если запрошен календарь на декабрь, на единицу увеличивается и следующий год. О том, зачем нам потребуются переменные $next_mon и $next_year, будет сказано далее.

   my $next_mon = $mon;
   my $next_year = $year;
   if ($next_mon == 12){
      $next_mon = 0;
      $next_year++;
   }
   $mon--;

Поскольку встроенные функции Perl предполагают, что неделя начинается с воскресенья, подготовим таблицу перестановки дней недели.

   my @wd = (7, 1..6);

Определим день недели первого числа запрошенного и следующего за ним месяца.

   my $first = timelocal (0, 0, 0, 1, $mon, $year);
   my $wday = (localtime ($first)) [6];
   $wday = $wd[$wday];

   my $next_first = timelocal (0, 0, 0, 1, $next_mon, $next_year);
   my $next_wday = (localtime ($next_first)) [6];
   $next_wday = $wd[$next_wday];

Теперь начнем заполнять двумерный массив @days, элементы которого будут содержать числа. Первый индекс — номер недели в месяце (начиная с нуля), второй — день недели (от 1 до 7).

В таблице должны оказаться только числа текущего месяца. Чтобы остановить заполнение массива в последний день месяца, требуется знать число дней в нем. Определить это можно, составить таблицу продолжительности месяцев, но все равно останется необходимость отдельно рассчитывать дни в феврале.

Однако, можно поступить совершенно по-другому. Вот здесь и пригодится вычисленный ранее день недели первого числа следующего месяца. Будем заполнять массив @days, проверяя, не достигнуто ли 28 число. После него будем дополнительно проверять день недели. Как только наступит день, приходящийся на первое число следующего месяца, цикл следует остановить.

   my @days = ();
   my $w;
   for (my $d = 1, my $wd = $wday, $w = 0;; $d++){
      $days[$w][$wd] = $d;

      if ($wd == 7) {$wd = 1; $w++;}
      else {$wd++;}

      last if ($d > 27 && $wd == $next_wday);
   }

Массив заполнен и можно его распечатать. Делается это достаточно просто.

   my $weeks = $w;

   print "<table border=\"0\">\n";
   for (my $wd = 1; $wd <= 7; $wd++){
      print "<tr align=\"right\">";
      for (my $w = 0; $w <= $weeks; $w++){
         my $d = $days[$w][$wd];
         $d = "&nbsp;" unless $d;
         $d = "<b>$d</b>" if ($d <= $mday);
         print "<td>$d</td>";
      }
      print "</tr>\n";
   }
   print "</table>\n";
}

Весь вывод сосредоточен лишь в этом блоке кода, при необходимости можно произвольно откорректировать html-форматирование.

Статья взята с  © webcode.ru
Хостинг от uCoz