2001/12/03

前回は次のような hcal.pl を作成しました。赤い部分を完成させましたか?

#!/usr/bin/perl

# コマンドライン引数をもらう
die "usage: cal month year\n" unless @ARGV == 2;
($mon, $year) = @ARGV;

# その月の末日を計算(2行目は閏年の計算)
$lastday = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$mon - 1]
         + ($mon == 2 && ($year % 4 == 0 && $year % 100 != 0 || $year % 400 == 0));

#print "      $year/$mon\n S  M Tu  W Th  F  S\n";

print <<"HERE";
<HTML>
<HEAD>
<TITLE>$year年$mon月の予定</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
[<A HREF="perl02.html">「出力の工夫」に戻る</A>]
<CENTER>
<H1>$year 年 $mon 月</H1>
<TABLE BORDER BGCOLOR="#C0C0FF">
<TR BGCOLOR="#80FF80">
<TH WIDTH="100">日</TH>
<TH WIDTH="100">月</TH>
<TH WIDTH="100">火</TH>
<TH WIDTH="100">水</TH>
<TH WIDTH="100">木</TH>
<TH WIDTH="100">金</TH>
<TH WIDTH="100">土</TH>
</TR>
HERE

#foreach(("  ") x &getweek($year, $mon), 1 .. $lastday){
#    printf ('%2.2s ' , $_);
#    print "\n" unless ++$days % 7;
#}

#print "\n"; exit;

print "</TABLE>
</CENTER>
</BODY>
</HTML>
";

# 曜日を得る関数
sub getweek{
    local($year, $month) = @_;
    if($month == 1 || $month == 2) {$year--;$month += 12;}
    int($year + int($year/4) - int($year/100) + int($year/400) + int((13*$month+8)/5) + 1) % 7;
}

実行結果:

2001 年 12 月

今回は本体部分を出力できるようにします。 現在コメントになっている foreach ループ部を復活させてください (他は省略してあります)。


foreach(("  ") x &getweek($year, $mon), 1 .. $lastday){
    printf ('%2.2s ' , $_);
    print "\n" unless ++$days % 7;
}

単に日付($_変数に格納されている)を出力するかわりに

        "<TD VALIGN=\"top\" WIDTH=\"100\" >$_ <BR></TD>\n"
という文字列を print すればよさそうです。


foreach(("  ") x &getweek($year, $mon), 1 .. $lastday){
    print "<TD VALIGN=\"top\" WIDTH=\"100\" >$_ <BR></TD>\n";
    print "\n" unless ++$days % 7;
}

しかしこれでは

....
<TH WIDTH="100">金</TH>
<TH WIDTH="100">土</TH>
</TR>
<TD VALIGN="top" WIDTH="100" >  <BR></TD>
<TD VALIGN="top" WIDTH="100" >  <BR></TD>
<TD VALIGN="top" WIDTH="100" >  <BR></TD>
<TD VALIGN="top" WIDTH="100" >1 <BR></TD>
<TD VALIGN="top" WIDTH="100" >2 <BR></TD>
<TD VALIGN="top" WIDTH="100" >3 <BR></TD>
<TD VALIGN="top" WIDTH="100" >4 <BR></TD>

<TD VALIGN="top" WIDTH="100" >5 <BR></TD>
<TD VALIGN="top" WIDTH="100" >6 <BR></TD>
<TD VALIGN="top" WIDTH="100" >7 <BR></TD>
<TD VALIGN="top" WIDTH="100" >8 <BR></TD>
<TD VALIGN="top" WIDTH="100" >9 <BR></TD>
<TD VALIGN="top" WIDTH="100" >10 <BR></TD>
<TD VALIGN="top" WIDTH="100" >11 <BR></TD>

<TD VALIGN="top" WIDTH="100" >12 <BR></TD>
<TD VALIGN="top" WIDTH="100" >13 <BR></TD>
....

のようになってしまいます。つまり各一週間ごとの行を指定する <TR></TR>が出力されません。 ですから、日曜日なら<TR> という1行を日付のセルの前に、 土曜日なら </TR> という1行を日付のセル後に print しなければ なりません。 上のスクリプトでは$daysがカレンダーの最初からかぞえた日数を 表していますから、$days % 7 が 0 ならば日曜日ですし、6 ならば 土曜日です(もしくは $days に 1 を加えたものが 7 で割り切れれば土曜日です)。 もとのスクリプトでは土曜日なら日付のセルのあとに余分の改行がひとつ 追加されていますのでそれをまねてやればよいでしょう。 ついでに unless の部分をもう少しわかりやすく書き換えましょう。


foreach(("  ") x &getweek($year, $mon), 1 .. $lastday){
    print "<TR>\n"  if ( $days % 7 == 0 );
    print "<TD VALIGN=\"top\" WIDTH=\"100\" >$_ <BR></TD>\n";
    ++$days;
    print "</TR>\n" if ( $days % 7 == 0 );
}

これで2001年12月のカレンダーを出力すると次のようになります。

2001 年 12 月


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

おおむね良いのですが最後の行に白いセルが並び、しかもソースをよく 見ると行を閉じる</TR>もないことがわかります。 それは最後の日(31日)が土曜日でないからです。 月の最後の日が土曜でない場合は空欄を余分に出力する必要があるのです。 対応には色々方法があるでしょう。

好きなやり方で完成させて下さい。 スクリプトは hcal2.pl という名前で保存し、実行してみましょう。

2001 年 12 月


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31





今回の課題

2002年2月と2003年2月のカレンダーを自分のホームページに掲載またはリンクして ください。 使用したスクリプトも読めるようにしておいて下さい。

もし可能なら、さらに日曜日ならセルの色を変えたり、祝日を自動判定したり、 さらには振替休日にも対応させてみましょう。