challenge 年間カレンダー

nを入力としてn年の年間カレンダーを返すプログラムを作ってください
少なくとも日曜日と土曜日が判別出来るようにしてください
出力は標準出力でもファイルでも構いません
デザインは各自のお好みで

出力例1:
(y-calendar 2008)=>
#=Saturday, @=Sunday
2008/1 1 2 3 4 #5 @6 7 ...
2008/2 1 #2 @3 4 5 6 7 ...
...
2008/12 1 2 3 4 5 #6 @7 ...

出力例2:
(y-calendar 2008)=>
        M T W T F S S M
2008/ 1   1 2 3 4 5 6 7 ...
2008/ 2         1 2 3 4 ...
...
2008/12 1 2 3 4 5 6 7 8 ...

出力例3:
(y-calendar 2008)は2008.htmlを出力する
2008.htmlの中身
----
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
       "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>2008 calendar</title>
<style type="text/css">
* {font-family: monospace;}
span {margin: 0px 3px;}
span.sunday {color:red;font-weight:bold;}
span.saturday {color:blue;font-weight:bold;}
dd ul li{display:inline;}
</style>
</head>
<body>
<h1>2008 calendar</h1>
<dl>
<dt>2008/1</dt>
<dd><ul>
<li><span class="weekday">1</span></li>
<li><span class="weekday">2</span></li>
<li><span class="weekday">3</span></li>
<li><span class="weekday">4</span></li>
<li><span class="saturday">5</span></li>
<li><span class="sunday">6</span></li>
...
</ul></dd>
...
</dl>
</body>
</html>
----

Posted feedbacks - Flatten

Nested Hidden

裏技?系

1
2
3
4
5
6
module Main (main) where
import System.Cmd
import System.Environment
import System.Exit
main :: IO ExitCode
main = system . ("cal "++) . head =<< getArgs

これが一番簡単だと思います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
//http://ja.doukaku.org/119/ 投稿用
using System;
using System.Windows.Forms;
namespace どう書く_org年間カレンダ {
    class Program {
        [STAThread]
        static void Main(string[] args) {
            Application.Run(new Form1());
        }

    }
    class Form1:Form {
        MonthCalendar calendar = new MonthCalendar();
        public Form1() {
            int year = int.Parse(Environment.GetCommandLineArgs()[1]);
            AutoSize = true;
            calendar.Parent = this;
            calendar.SetCalendarDimensions(4, 3);
            calendar.SelectionStart = new DateTime(year, 1, 1);
        }
    }
}

標準出力へ。

========= 2008/ 1 =========
SUN MON TUE WED THR FRI SAT
          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

========= 2008/ 2 =========
SUN MON TUE WED THR FRI SAT
                      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

......
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
static class Program {
    static void Main(string[] args) {
        int year = int.Parse(args[0]);
        for(int month = 1; month <= 12; month++) {
            int lastday = DateTime.DaysInMonth(year, month);
            int week = (int) DateTime.Parse(string.Format("{0}/{1}/1", args[0], month)).DayOfWeek;
            Console.WriteLine("========= {0,4}/{1,2} =========", year, month);
            Console.WriteLine("SUN MON TUE WED THR FRI SAT");
            for(int i = 0; i < week; i++) {
                Console.Write("    ");
            }
            for(int day = 1; day <= lastday; day++) {
                Console.Write("{0,3} ", day);
                week++;
                if (week == 7) {
                    Console.WriteLine(); week = 0;
                }
            }
            Console.WriteLine();
            Console.WriteLine();
        }
    }
}

標準出力へ。

1
2
3
4
import sys
import calendar

calendar.prcal(int(sys.argv[1]))

ちょっと長いか?

< January_2007 >
Sun Mon Tue Wed Thu Fri Sat 
      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 

・・・
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require 'date'
y = ARGV[0].to_i

for m in 1 .. 12
  date = Date.new(y, m, 1)
  puts "< #{ Date::MONTHNAMES[date.month]}_#{y} >"
  Date::ABBR_DAYNAMES.each { |name|
    print name + " "
  }
  puts

  date.wday.times {
    print "    "
  }
  date.upto((date >> 1) - 1) { |d|
    print " " if d.day < 10
    print " " + d.day.to_s + " "
    if d.wday == 6
      puts
    end      
  }
  puts
  puts
end

Python/Tkinterで組んでみました。組んだはいいが、結構重い・・・(レイアウトを工夫すればもっと速くできるかもしれませんが)

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# coding: shift_jis

import Tkinter as Tk
import calendar
import sys

def grid_configure(frame, col_count, row_count):
    for col in xrange(col_count):
        frame.grid_columnconfigure(col, weight=1)
    for row in xrange(row_count):
        frame.grid_rowconfigure(row, weight=1)

class MonthPanel(Tk.Frame):
    def __init__(self, master, year, month):
        Tk.Frame.__init__(self, master)

        label = Tk.Label(self, text=u"%d月" % month)
        label.pack(side=Tk.TOP, fill=Tk.X)

        frame = Tk.Frame(self)
        frame.pack(side=Tk.TOP, fill=Tk.BOTH, expand=True)

        grid_configure(frame, 7, 7)

        def create_label(row, col):
            label = Tk.Label(frame, bg="white")
            label.grid(row=row, column=col, sticky="news", padx=1, pady=1)
            return label

        labels = [[create_label(row, col) for col in xrange(7)] for row in xrange(7)]

        def text(col, row, s, color="white"):
            labels[row][col].configure(text=s, bg=color)

        for col, s in enumerate(u"日月火水木金土"):
            if col == 0:
                color = "#F7CACA"
            elif col == 6:
                color = "#CEFFFF"
            else:
                color = "#E7FFE7"
            text(col, 0, s, color)

        for row, a in enumerate(calendar.Calendar(6).monthdayscalendar(year, month)):
            for col, day in enumerate(a):
                if day:
                    text(col, row + 1, day)

def main():
    if len(sys.argv) != 2:
        sys.stderr.write("usage: year\n")
        return
    year = int(sys.argv[1])

    root = Tk.Tk()
    root.title(u"%d年 年間カレンダー" % year)

    grid_configure(root, 4, 3)

    for i in xrange(12):
        frame = MonthPanel(root, year, i + 1)
        frame.grid(row=i/4, column=i%4, sticky="news", padx=2, pady=2)

    root.mainloop()

if __name__ == '__main__':
    main()

とりあえず表示例1で書いてみました

 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
●yCalendar(year)
    monthとは整数
    dayとは整数
    "{year} calendar (#=Saturday, @=Sunday)"を表示
    12回
        month=回数
        "{year}/{month}"を継続表示
        (daysOfMonth(year,month))回
            day=回数
            もし("{year}/{month}/{day}"の曜日="土")ならば
                " #{day}"を継続表示
            違えば、もし("{year}/{month}/{day}"の曜日="日")ならば
                " @{day}"を継続表示
            違えば
                " {day}"を継続表示
        改行を継続表示
    
●daysOfMonth(year,month)
    nextYearとは整数
    nextMonthとは整数
    もし(month=12)ならば、nextMonth=1。nextYear=year+1
    違えば、nextMonth=month+1。nextYear=year
    "{year}/{month}/1"と"{nextYear}/{nextMonth}/1"の日数差

yCalendar(2008)

            2008 /  1               2008 /  2               2008 /  3
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
        1  2  3  4  5                    1  2                       1
  6  7  8  9 10 11 12     3  4  5  6  7  8  9     2  3  4  5  6  7  8
 13 14 15 16 17 18 19    10 11 12 13 14 15 16     9 10 11 12 13 14 15
 20 21 22 23 24 25 26    17 18 19 20 21 22 23    16 17 18 19 20 21 22
 27 28 29 30 31          24 25 26 27 28 29       23 24 25 26 27 28 29
                                                 30 31

(中略)

            2008 / 10               2008 / 11               2008 / 12
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
           1  2  3  4                       1        1  2  3  4  5  6
  5  6  7  8  9 10 11     2  3  4  5  6  7  8     7  8  9 10 11 12 13
 12 13 14 15 16 17 18     9 10 11 12 13 14 15    14 15 16 17 18 19 20
 19 20 21 22 23 24 25    16 17 18 19 20 21 22    21 22 23 24 25 26 27
 26 27 28 29 30 31       23 24 25 26 27 28 29    28 29 30 31
                         30

と、こんな感じで表示します☆
 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
#include<stdio.h>

int f(int y, int m, int d){
  if(m < 3)
    y--, m += 12;
  return y * 365 + y / 4 - y / 100 + y / 400 + (m + 1) * 306 / 10 + d - 428;
}

int main(void){
  int y , m , d , i , j;
  printf("n = ");
  scanf("%d", &y);
  for(i = 0; i < 12; i += 3){
    for(m = i + 1 ; m < i + 4; m++)
      printf("%16d /%3d   ", y, m);
    printf("\n");
    for(j = 0; j < 3; j++)
      printf(" Su Mo Tu We Th Fr Sa   ");
    printf("\n");
    for(j = 0; j < 42; j += 7, printf("\n"))
      for(m = i + 1; m < i + 4; m++, printf("   "))
        for(d = j - f(y, m, 1) % 7 + 1; d < j - f(y, m, 1) % 7 + 8; d++)
          if(d < 1 || d > f(y, m + 1, 1) - f(y, m, 1))
            printf("   ");
          else
            printf("%3d", d);
    printf("\n");
  }
  return 0;
}

出力例3をテーブルに変えて。
テーブル用に空白を&nbsp;で埋めています。
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
$year = 2008;
$weekdays = array( "S", "M", "T", "W", "T", "F", "S" );

for ( $month = 1; $month <= 12; $month++ ) {
    $month_ts = mktime( 0, 0, 0, $month, 1, $year );
    $days = date( 't', $month_ts );
    $weekday = date( 'w', $month_ts );
    $week = 0;
    for ( $day = 1; $day <= $days; $day++ ) {
        if ( $day == 1 && $weekday > 0 ) {
            for ( $i = 0; $i < $weekday; $i++ ) {
                $calendar[$month][$week][$i] = "&nbsp;";
            }
        }
        $calendar[$month][$week][] = $day;
        $weekday++;
        if ( $day == $days && $weekday < 7 ) {
            for ( $j = $weekday; $j < 7; $j++ ) {
                $calendar[$month][$week][$j] = "&nbsp;";
            }
        }
        if ( $weekday == 7 ) {
            $week++;
            $weekday = 0;
        }
    }
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
       "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title><?php echo($year); ?> calendar</title>
<style type="text/css">
* {font-family: monospace;}
td {text-align: right;}
.sunday {color:red;font-weight:bold;}
.saturday {color:blue;font-weight:bold;}
</style>
</head>
<body>
<h1><?php echo($year); ?> calendar</h1>
<dl>
<?php foreach ( $calendar as $month => $weeks ) { ?>
<dt><?php echo($year); ?>/<?php echo($month); ?></dt>
<dd>
<table>
<tr>
<?php
  foreach ( $weekdays as $key => $val ) {
    $class = "weekday";
    $class = ( $key === 0 )? "sunday": $class;
    $class = ( $key == 6 )? "saturday": $class;
?>
<th class="<?php echo($class); ?>"><?php echo($val); ?></th>
<?php } ?>
</tr>
<?php foreach ( $weeks as $week ) { ?>
<tr>
<?php
  foreach ( $week as $weekday => $day ) {
    $class = "weekday";
    $class = ( $weekday === 0 )? "sunday": $class;
    $class = ( $weekday == 6 )? "saturday": $class;
?>
<td class="<?php echo($class); ?>"><?php echo($day); ?></td>
<?php } ?>
</tr>
<?php } ?>
</table>
</dd>
<?php } ?>
</dl>
</body>
</html>


Java/Swingで組んでみました。

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import java.awt.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

class MeApp {
    public static void main(String[] args) {
        final int year;
        if (args.length == 0) {
            year = Calendar.getInstance().get(Calendar.YEAR);
        } else {
            year = Integer.valueOf(args[0]);
        }
        SwingUtilities.invokeLater(new Runnable(){
            public void run() {
                final MeFrame frame = new MeFrame(year);
                frame.setDefaultCloseOperation(MeFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

class MeFrame extends JFrame {
    public MeFrame(int year) {
        super(String.format("%d年 年間カレンダー", year));
        getContentPane().setLayout(new GridLayout(3, 4, 5, 5));
        for (int i = 0; i < 12; ++i) {
            getContentPane().add(new MeMonthPanel(year, i + 1));
        }
    }
}

class MeMonthPanel extends JPanel {
    public MeMonthPanel(int year, int month) {
        setLayout(new BorderLayout());
        add(new JLabel(String.format("%d月", month), JLabel.CENTER), BorderLayout.NORTH);
        add(new MeMonthCanvas(year, month), BorderLayout.CENTER);
    }
}

class MeMonthCanvas extends JComponent {
    private final String[][] _cells = new String[7][7];
    private final Color[][] _colors = new Color[7][7];
    public MeMonthCanvas(int year, int month) {
        final String[] weekdays = new DateFormatSymbols().getShortWeekdays();
        for (int col = 0; col < 7; ++col) {
            _cells[0][col] = weekdays[col + 1];
            _colors[0][col] = new Color(231, 255, 231);
        }
        _colors[0][0] = new Color(247, 202, 202); // Sunday
        _colors[0][6] = new Color(206, 255, 255); // Saturday

        final Calendar cal = Calendar.getInstance();
        for (int row = 1; row < 7; ++row) {
            for (int col = 0; col < 7; ++col) {
                cal.set(Calendar.YEAR, year);
                cal.set(Calendar.MONTH, month - 1);     // starts from 0
                cal.set(Calendar.DAY_OF_WEEK, col + 1); // starts from 1
                cal.set(Calendar.WEEK_OF_MONTH, row);   // starts from 1
                if (cal.get(Calendar.DAY_OF_WEEK) == col + 1 && cal.get(Calendar.WEEK_OF_MONTH) == row) {
                    _cells[row][col] = String.valueOf(cal.get(Calendar.DATE));
                } else {
                    _cells[row][col] = "";
                }
                _colors[row][col] = Color.WHITE;
            }
        }
    }
    public Dimension getPreferredSize() {
        return new Dimension(25 * 7, 25 * 7);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        final FontMetrics fm = g.getFontMetrics();
        final int w = getWidth() / 7;
        final int h = getHeight() / 7;
        for (int row = 0; row < 7; ++row) {
            for (int col = 0; col < 7; ++col) {
                g.setColor(_colors[row][col]);
                g.fill3DRect(w * col, h * row, w, h, true);
                final String s = _cells[row][col];
                g.setColor(Color.BLACK);
                g.drawString(
                    s,
                    w * col + (w - fm.stringWidth(s)) / 2,
                    h * row + (h - fm.getHeight()) / 2 + fm.getAscent()
                );
            }
        }
    }
}

せっかくJAVAのクラスを使っているので多言語対応で。

JAPAN
=================================
2007/1
  日  月  火  水  木  金  土
       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

US
=================================
2007/1
  Sun  Mon  Tue  Wed  Thu  Fri  Sat
         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

CHINA
=================================
2007/1
  星期日  星期一  星期二  星期三  星期四  星期五  星期六
               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


 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
import java.text.{DateFormatSymbols, SimpleDateFormat}
import java.util.{Date, Locale}

object YearCalendar {
  import java.util.Calendar._
  def mkString(n:int, s:String) = List.make(n, s).mkString
  def print(year:int):unit = print(year, Locale.getDefault)
  def print(year:int, locale:Locale):unit = {
    val ws   = (new DateFormatSymbols(locale)).getShortWeekdays mkString "  "
    val format = (new SimpleDateFormat("dd", locale)).format _:Date => String
    val wwidth = ws.split("  ")(1).getBytes.size
    val cal = getInstance(locale)

    (0 to 11).foreach { month => 
      println(year+"/"+(month+1)+"\n"+ws)
      var n = {cal.set(year,month,1); cal}.get(DAY_OF_WEEK)
      Console.print(mkString((n-1)*(wwidth+2), " "))
      do {
        Console.print(mkString(wwidth, " ")+format(cal.getTime).replaceFirst("^0", " "))
        if(n == 7){ println(""); n = 0 }
      }while({n = n +1;cal.add(DATE, 1);cal.get(MONTH) == month})
      
      println("\n")
    }
  }
}

YearCalendar print 2007
YearCalendar.print(2007, Locale.US)
YearCalendar.print(2007, Locale.CHINA)

ついでにJTable版。

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import java.awt.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

class MeApp {
    public static void main(String[] args) {
        final int year;
        if (args.length == 0) {
            year = Calendar.getInstance().get(Calendar.YEAR);
        } else {
            year = Integer.valueOf(args[0]);
        }
        SwingUtilities.invokeLater(new Runnable(){
            public void run() {
                final MeFrame frame = new MeFrame(year);
                frame.setDefaultCloseOperation(MeFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

class MeFrame extends JFrame {
    public MeFrame(int year) {
        super(String.valueOf(year));
        getContentPane().setLayout(new GridLayout(3, 4, 5, 5));
        for (int i = 0; i < 12; ++i) {
            getContentPane().add(new MeMonthPanel(year, i + 1));
        }
    }
}

class MeMonthPanel extends JPanel {
    public MeMonthPanel(int year, int month) {
        setLayout(new BorderLayout());
        add(new JLabel(new DateFormatSymbols().getMonths()[month - 1], JLabel.CENTER), BorderLayout.NORTH);
        add(new JScrollPane(new JTable(new MeMonthTableModel(year, month))), BorderLayout.CENTER);
    }
    public Dimension getPreferredSize() {
        return new Dimension(220, 180); // ???
    }
}

class MeMonthTableModel extends AbstractTableModel {
    private final int _year, _month;
    private final String[] _weekdays = new DateFormatSymbols().getShortWeekdays();
    private final Calendar _cal = Calendar.getInstance();

    public MeMonthTableModel(int year, int month) {
        _year = year; _month = month;
    }
    public int getColumnCount() {
        return 7;
    }
    public String getColumnName(int col) {
        return _weekdays[col + 1];
    }
    public int getRowCount() {
        return 6;
    }
    public Object getValueAt(int row, int col) {
        _cal.set(Calendar.YEAR, _year);
        _cal.set(Calendar.MONTH, _month - 1);      // starts from 0
        _cal.set(Calendar.DAY_OF_WEEK, col + 1);   // starts from 1
        _cal.set(Calendar.WEEK_OF_MONTH, row + 1); // starts from 1
        if (_cal.get(Calendar.DAY_OF_WEEK) == col + 1 && _cal.get(Calendar.WEEK_OF_MONTH) == row + 1) {
            return String.valueOf(_cal.get(Calendar.DATE));
        } else {
            return "";
        }
    }
}

何となく修正
1
2
3
4
5
6
7
8
9
--- calendar.cs.old     Sun Dec 30 10:22:50 2007
+++ calendar.cs Sun Dec 30 18:23:04 2007
@@ -6,10 +6,8 @@
-            Console.WriteLine("SUN MON TUE WED THR FRI SAT");
-            for(int i = 0; i < week; i++) {
-                Console.Write("    ");
-            }
+            Console.WriteLine("SUN MON TUE WED THU FRI SAT");
+            Console.Write    ("                           ".Substring(0, week * 4));

#5080を参考に、Javaの標準出力版を書いてみました。

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Locale;

public class Sample119 {
    private final int year_;
    private final Locale locale_;
    private DateFormatSymbols formatSymbols_;

    public Sample119(int year, Locale locale) {
        year_ = year;
        locale_ = locale;
        formatSymbols_ = new DateFormatSymbols(locale_);
    }

    public void printCalendar() {
        System.out.println(locale_.getDisplayCountry());
        for (int month = 0; month < 12; month++) {
            printCalendar(month);
        }
        System.out.println();
    }
    private void printCalendar(int month) {
        System.out.format("< %s/%d >%n", formatSymbols_.getShortMonths()[month], year_);
        String[] weekdays = formatSymbols_.getShortWeekdays();
        for (int index = 1; index < weekdays.length; index++) {
            System.out.format("%s ", weekdays[index]);
        }
        System.out.println();

        Calendar cal = Calendar.getInstance(locale_);
        cal.set(year_, month, 1);
        int dayLen = weekdays[1].getBytes().length;
        for (int index = 0, length = (cal.get(Calendar.DAY_OF_WEEK) - 1) * (dayLen + 1); index < length; index++) {
            System.out.print(" ");
        }
        String format = String.format("%%%dd ", dayLen);
        for (; cal.get(Calendar.MONTH) == month; cal.add(Calendar.DATE, 1)) {
            System.out.format(format, cal.get(Calendar.DATE));
            if (Calendar.SATURDAY == cal.get(Calendar.DAY_OF_WEEK)) {
                System.out.println();
            }
        }
        System.out.println();
    }

    public static void main(String[] args) {
        new Sample119(2008, Locale.JAPAN).printCalendar();
        new Sample119(2008, Locale.US).printCalendar();
    }
}

#5080ではなくて、#5060でした。

Date-Calcという同名のPerlのライブラリをCommon Lispで再現したものを
使用してみました。
ライブラリ機能のおまけで英仏独等々に対応させてみましたが、
日本語は対応していないという…。
(y-calendar 2008 7)  ;7はイタリア(数字で指定だと色んな意味で紛らわしいかも)
;==>
;       Gennaio 2008        
;Lun Mar Mer Gio Ven Sab Dom
;      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 
; ~~~
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(defpackage :doukaku-119 (:use :cl :date-calc))
(in-package :doukaku-119)

(defun y-calendar (y &optional (lang 1))
  (do ((m 1 (1+ m)) (col 4))
      ((> m 12))
    (format t "~&~%~V:@<~A ~D~>~%~{~A~^ ~}~%"
            (* col 7) (get-month-name m lang) y
            (get-week-day-name-list lang (1- col)))
    (do ((d 1 (1+ d))) (nil)
      (let ((dow (day-of-week y m d))
            (day (day-of-year y m d)))
        (when (= 1 d)
          (format t "~V,0T" (* col (1- dow))))
        (if (zerop day)
            (return)
            (format t "~VD~:[ ~;~%~]" (1- col) d (= 7 dow)))))))

(defun get-month-name (m &optional (lang 1))
  (aref (gethash lang date-calc::month-to-text) m))

(defun get-week-day-name-list (&optional (lang 1) (shoten 3))
  (map 'list (if shoten (lambda (name) (subseq name 0 shoten)) #'identity)
       (subseq (gethash lang date-calc::day-of-week-to-text) 1)))

Squeak Smalltalk で。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
| nn |
nn := 2008.
World findATranscript: nil.
Transcript cr; show: nn.
nn asYear monthsDo: [:month |
    Transcript cr; show: (month name first: 3).
    month datesDo: [:date |
        | template |
        template := date weekday caseOf: {
            [#Saturday] -> ['({1})'].
            [#Sunday] -> ['[{1}]']} otherwise: ['{1}'].
        Transcript space; show: (template format: {date dayOfMonth})]]

"=> 
2008
Jan 1 2 3 4 (5) [6] 7 8 9 1 ... "

他言語化。(ウィンドウタイトルはそのままだけど、まあいいか)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
--- MeApp.java.old  Mon Dec 31 18:21:09 2007
+++ MeApp.java  Mon Dec 31 18:21:22 2007
@@ -35,7 +35,7 @@
 class MeMonthPanel extends JPanel {
     public MeMonthPanel(int year, int month) {
         setLayout(new BorderLayout());
-        add(new JLabel(String.format("%dŒŽ", month), JLabel.CENTER), BorderLayout.NORTH);
+        add(new JLabel(new DateFormatSymbols().getMonths()[month - 1], JLabel.CENTER), BorderLayout.NORTH);
         add(new MeMonthCanvas(year, month), BorderLayout.CENTER);
     }
 }

Tk.LabelFrameを使ってみた。

 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
--- 1.py    Mon Dec 31 18:42:39 2007
+++ 2.py    Mon Dec 31 18:44:14 2007
@@ -10,20 +10,14 @@
     for row in xrange(row_count):
         frame.grid_rowconfigure(row, weight=1)
 
-class MonthPanel(Tk.Frame):
+class MonthPanel(Tk.LabelFrame):
     def __init__(self, master, year, month):
-        Tk.Frame.__init__(self, master)
+        Tk.LabelFrame.__init__(self, master, text=u"%dŒŽ" % month)
 
-        label = Tk.Label(self, text=u"%dŒŽ" % month)
-        label.pack(side=Tk.TOP, fill=Tk.X)
-
-        frame = Tk.Frame(self)
-        frame.pack(side=Tk.TOP, fill=Tk.BOTH, expand=True)
-
-        grid_configure(frame, 7, 7)
+        grid_configure(self, 7, 7)
 
         def create_label(row, col):
-            label = Tk.Label(frame, bg="white")
+            label = Tk.Label(self, bg="white")
             label.grid(row=row, column=col, sticky="news", padx=1, pady=1)
             return label

見た目修正。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//http://ja.doukaku.org/119/ 投稿用
using System;
using System.Drawing;
using System.Windows.Forms;
namespace どう書く_org年間カレンダ {
    class Program {
        [STAThread]
        static void Main(string[] args) {
            Application.Run(new Form1());
        }
    }
    class Form1:Form {
        public Form1() {
            int year = int.Parse(Environment.GetCommandLineArgs()[1]);
            Text = year + " Calender";
            MonthCalendar calendar = new MonthCalendar();
            calendar.Parent = this;
            calendar.Dock = DockStyle.Fill;
            calendar.SelectionStart = new DateTime(year, 1, 1);
            calendar.SelectionEnd = new DateTime(year, 1, 1);
            Size = new Size(calendar.SingleMonthSize.Width * 4, calendar.SingleMonthSize.Height * 3);
        }
    }
}

404 Blog Not Found:javascript - カレンダーを作るに実際に動くデモがありますので併せてご覧下さい。なるべく再利用しやすいよう書きました。

Dan the JavaScripter

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
function leap(year){
  return year % 4 ? 0 : year % 100 ? 1 : year % 400 ? 0 : 1;
}

function make_cal_array(year){
  var months = [31, 28 + leap(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  var result = [];
  for (var m = 0; m < 12; m++){
    result[m] = [];
    var dofw1 = (new Date(year, m, 1, 0, 0, 0)).getDay();
    for (var d = 1; d <= months[m]; d++){
      result[m][d + dofw1 - 1] = d;
    }
  }
  return result;
}

var daynames = ['sun','mon','tue','wed','thr','fri','sat'];

function make_cal_yearly(year){
  var cal = make_cal_array(year);
  var tbody = document.createElement('tbody');
  for (var m = 0; m < 12; m++){
    var tr = document.createElement('tr');
    var th = document.createElement('th');
    th.innerHTML = m + 1;
    tr.appendChild(th);
    for (var d = 0, l = cal[m].length; d < l; d++){
      var td = document.createElement('td');
      if (cal[m][d]){
        td.innerHTML = cal[m][d];
        td.className = daynames[d % 7];
      }
      tr.appendChild(td);
    }
    tbody.appendChild(tr);
  }
  var table = document.createElement('table');
  table.className = 'ycal';
  var caption = document.createElement('caption');
  caption.innerHTML = year;
  table.appendChild(caption);
  table.appendChild(tbody);
  return table;
}

function make_cal_monthly(year, m){
  var cal = make_cal_array(year);
  var table = document.createElement('table');
  // header
  var tr = document.createElement('tr');
  for (var d = 0; d < 7; d++){
    var th = document.createElement('th');
    th.innerHTML = th.className = daynames[d];
    tr.appendChild(th);
  }
  var thead = document.createElement('thead');
  thead.appendChild(tr);
  table.appendChild(thead);
  // body;
  var tbody = document.createElement('tbody');
  for (var d = 0, l = cal[m].length; d < l; d++){
    if (d % 7 == 0) tr = document.createElement('tr');
    var td = document.createElement('td');
    if (cal[m][d]){
      td.innerHTML = cal[m][d];
      td.className = daynames[d % 7];
    }
    tr.appendChild(td);
    if (d % 7 == 6) tbody.appendChild(tr);
  }
  tbody.appendChild(tr);
  table.className = 'mcal';
  var caption = document.createElement('caption');
  caption.innerHTML = year + '.' + (m+1);
  table.appendChild(caption);
  table.appendChild(tbody);
  return table;
}

function make_calendars(year, p){
  p.innerHTML = '';
  p.appendChild(make_cal_yearly(year));
  for (var m = 0; m < 12; m++){
   var mcal = make_cal_monthly(year, m);
   p.appendChild(mcal);
   if (m % 3 == 2){
     var br = document.createElement('br');
     br.clear = 'all';
     p.appendChild(br);
   }
  };
}

ひたすらコンパクトさ目指して、32進表記とか。
   cal 2008
uv12345 6789abc defghij klmnopq 
rstuv12 3456789 abcdefg hijklmn 
opqrst1 2345678 9abcdef ghijklm 
nopqrst uv12345 6789abc defghij 
klmnopq rstu123 456789a bcdefgh 
ijklmno pqrstuv 1234567 89abcde 
fghijkl mnopqrs tu12345 6789abc 
defghij klmnopq rstuv12 3456789 
abcdefg hijklmn opqrstu v123456 
789abcd efghijk lmnopqr stu1234 
56789ab cdefghi jklmnop qrstuv1 
2345678 9abcdef ghijklm nopqrst 
u123456 789abcd efghijk lmnopqr 
stuv123 456789a bcdefgh ijklmno 
1
2
3
load 'dates'
n32=:{&' 123456789abcdefghijklmnopqrstuv'
cal=:3 :'14 32$,,.&'' ''_7[\n32"0[,2}."1 todate((todayno-weekday)y,1 1)+i.400'

全部で365日をこえてしまってまっているようですが…


calコマンドっぽく表示します。

> erlc year_cal.erl
> erl -noshell -s year_cal main -s init stop

1月_2008
 日 月 火 水 木 金 土
        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

(中略)

12月_2008
 日 月 火 水 木 金 土
     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
 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
-module(year_cal).
-export([main/0]).

main() -> year_cal(2008).

year_cal(Year) -> lists:foreach(fun manth/1, [{Year, X} || X <- lists:seq(1, 12)]).

manth({Year, Manth}) ->
    io:format("~B月_~B~n", [Manth, Year]),
    io:format("日 月 火 水 木 金 土~n", []),
    First = calendar:day_of_the_week(Year, Manth, 1),
    if
        7 == First -> true;
        true -> io:format(lists:duplicate(((First rem 7) * 3) - 1, " "), [])
    end,
    manth(Year, Manth, 1).

manth(Year, Manth, Day) ->
    case calendar:valid_date(Year, Manth, Day) of
        false -> io:nl();        % 一ヶ月の終わり
        true  ->
            Dotw = calendar:day_of_the_week(Year, Manth, Day),
            if
                7 == Dotw -> io:format("~2B", [Day]);        % 日曜日
                6 == Dotw -> io:format("~3B~n", [Day]);        % 土曜日
                true      -> io:format("~3B", [Day])        % その他平日
            end,
            manth(Year, Manth, Day + 1)
    end.

初めて投稿させていただきます。

出力形式はUnixの calコマンドを参考にしました。

バッチファイルですので、ファイル名を cal.batなどにして保存し、ダブルクリックして
ください。Windows 2000以降であれば動作すると思います。一瞬で終了しないようにする
には、69行目のendlocalの下に以下の行を追加してください。

  e.g. pause >NUL

引数を指定しない場合はシステムの日時をもとに算出します。ただし、日本以外の地域の
日時表記を使用している環境では正常に動作しません。

  e.g. C:\>cal 1 2008
  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
@echo off

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: グローバル変数

  setlocal

  set Y4=%2
  set MM=%1
  set DOW=
  set EOM=
  set COUNT=
  set ERROR=
  set IS_LEAP_YEAR=0
  set MARGIN=
  set MONTH=
  set SPACE=
  set ARRAY_MONTH=

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: メイン関数

  if "%3" NEQ "" echo usage: cal [month] [year] & goto :EOF
  if "%2" EQU "" (
    if "%1" NEQ "" echo usage: cal [month] [year] & goto :EOF
    call :GetDate Y4 MM
  )

  ::  8進数として解釈されないようにする。
  set /a Y4=%Y4%+0
  set /a MM=%MM%+0

  call :IsNumber %Y4% _ERROR
  if %_ERROR% NEQ 0 echo usage: cal [month] [year] & goto :EOF

  call :IsNumber %MM% _ERROR
  if %_ERROR% NEQ 0 echo usage: cal [month] [year] & goto :EOF

  if %Y4% LSS    1 echo cal: illegal year value: use 1-9999 & goto :EOF
  if %Y4% GTR 9999 echo cal: illegal year value: use 1-9999 & goto :EOF

  if %MM% LSS  1 echo cal: illegal month value: use 1-12 & goto :EOF
  if %MM% GTR 12 echo cal: illegal month value: use 1-12 & goto :EOF

  call :GetMonthName ARRAY_MONTH

  call :GetMargin %MM% MARGIN

  call :GetSpace %MARGIN% SPACE

  call :GetArrayValue ARRAY_MONTH_%MM% MONTH

  call :PrintHeader %Y4% %MONTH% "%SPACE%"

  call :GetDayOfWeek %Y4% %MM% DOW

  :: 2 : セルの幅, 1 : セルの余白
  set /a MARGIN=%DOW%*(2+1)

  call :GetSpace %MARGIN% SPACE

  call :GetEndOfMonth %MM% EOM

  call :IsLeapYear %Y4% %MM% IS_LEAP_YEAR
  if %IS_LEAP_YEAR% NEQ 0 set /a EOM+=1

  call :PrintCalendar %EOM% %DOW% "%SPACE%"

  endlocal
goto :EOF

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: ユーザー定義関数

:SetArrayValue
  set %1_%COUNT%=%2
goto :EOF

:GetArrayValue
  for /f "delims== tokens=1,2" %%a in ('set^|findstr /b "%1="') do set %2=%%b
goto :EOF

:Increment
  set /a COUNT+=1
goto :EOF

:GetDate
  set _DATE=

  for /f "tokens=1" %%d in ('date /t') do set _DATE=%%d

  set %1=%_DATE:~0,4%
  set %2=%_DATE:~5,2%
goto :EOF

:IsNumber
  set %2=0

  echo %1|findstr /r "[^0-9]" >NUL 2>NUL
  if %ERRORLEVEL% EQU 0 set %2=1
goto :EOF

:PrintHeader
  set _SPACE=%3
  set _SPACE=%_SPACE:"=%

  echo %_SPACE% %2 %1
  echo Su Mo Tu We Th Fr Sa
goto :EOF

:GetMonthName
  set _PATH=_.txt

  echo January  > %_PATH%
  echo February >>%_PATH%
  echo March    >>%_PATH%
  echo April    >>%_PATH%
  echo May      >>%_PATH%
  echo June     >>%_PATH%
  echo July     >>%_PATH%
  echo Augst    >>%_PATH%
  echo September>>%_PATH%
  echo October  >>%_PATH%
  echo November >>%_PATH%
  echo December >>%_PATH%

  set COUNT=1
  for /f "delims=" %%m in (%_PATH%) do (
    call :SetArrayValue %1 %%m
    call :Increment
  )
  del /q %_PATH% >NUL 2>NUL
goto :EOF

:GetMargin
  set _PATH=_.bat

  echo set _MARGIN= 334455542333>%_PATH%
  echo set %2=%%_MARGIN:~%1,1%%>>%_PATH%

  call %_PATH%

  del %_PATH% >NUL 2>NUL
goto :EOF

:GetDayOfWeek
  set _Y4_PREV=%1
  set _MM_PREV=%2
  set _DOW=0

  if %2 LEQ 2 (
    set /a _Y4_PREV=%1-1
    set /a _MM_PREV=%2+12
  )

  set /a _DOW=_Y4_PREV+_Y4_PREV/4-_Y4_PREV/100+_Y4_PREV/400+(13*_MM_PREV+8)/5+1
  set /a %3=_DOW%%7
goto :EOF

:GetSpace
  set _PATH=_.bat

  echo set _SPACE=                   >%_PATH%
  echo set %2=%%_SPACE:~0,%1%%>>%_PATH%

  call %_PATH%

  del %_PATH% >NUL 2>NUL
goto :EOF

:GetEndOfMonth
  set _PATH=_.bat

  echo set _DIFF=0030101001010>%_PATH%
  echo set /a %2=31-%%_DIFF:~%1,1%%>>%_PATH%

  call %_PATH%

  del %_PATH% >NUL 2>NUL
goto :EOF

:PrintCalendar
  set _DOW=0
  set _SPACE=%3
  set _LINE=%_SPACE:"=%
  set _DD=1

  :LOOP_BEGIN
    if %_DD% GTR %1 goto LOOP_END

    set /a _DOW=(%_DD%+%2)%%7

    if %_DD% LSS 10 set _DD= %_DD%
    set _LINE=%_LINE%%_DD% 

    if %_DOW% EQU 0 (
      echo %_LINE%
      set _LINE=
    )

    set /a _DD+=1
    goto LOOP_BEGIN
  :LOOP_END
  if "%_LINE%" NEQ "" echo %_LINE%
goto :EOF

:IsLeapYear
  set _VALUE=0

  if %2 NEQ 2 goto :EOF

  set /a _VALUE=%1%%4
  if %_VALUE% NEQ 0 goto :EOF

  set /a _VALUE=%1%%100
  if %_VALUE% NEQ 0 set %3=1 && goto :EOF

  set /a _VALUE=%1%%400
  if %_VALUE% EQU 0 set %3=1 && goto :EOF
goto :EOF

一点訂正です。

47, 48, 50, 51行目で_ERRORという名前の変数を使用していますが、正しくは ERRORでし
た。(先頭のアンダースコアが不要。)

該当部分の処理を関数(擬似関数)内からメイン関数に移した際、変数名を変更するのを忘
れていました。とはいえ、setlocalとendlocalでスコープを細かく分けることはしていな
いので、投稿したものでも動作上問題はありません。

カレンダーをどう見せるかで一番迷いましたが、こんな見せ方もあってもいいかなあ、と。

ANSIカラーに対応した端末(まあ今時はたいていのがそうだけど)で実行してご確認ください。

Dan the Perl Monger

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/local/bin/perl
use strict;
use warnings;
use POSIX qw/mktime/;

sub leap {
    $_[0] % 4 ? 0 : $_[0] % 100 ? 1 : $_[0] % 400 ? 0 : 1;
}

my @dayname = qw/sun mon tue wed thu fri sat/;

sub make_cal_array {
    my $year = shift;
    my @months =
      ( 31, 28 + leap($year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
    my $result;
    for my $m ( 0 .. 11 ) {
        my $dofw1 = ( localtime( mktime( 0, 0, 0, 1, $m, $year - 1900 ) ) )[6];
        for my $d ( 1 .. $months[$m] ) {
            $result->[$m][ $d + $dofw1 - 1 ] = $d;
        }
    }
    return $result;
}

sub color {
    [
        sub { "\e[7m\e[31m$_[0]\e[0m" },    # reverse red
        sub { "\e[30m$_[0]\e[0m" },         # black
        sub { "\e[35m$_[0]\e[0m" },         # purple
        sub { "\e[32m$_[0]\e[0m" },         # green
        sub { "\e[33m$_[0]\e[0m" },         # yellow
        sub { "\e[36m$_[0]\e[0m" },         # skyblue
        sub { "\e[7m\e[34m$_[0]\e[0m" },    # reverse blue
    ]->[ $_[1] % 7 ]->( $_[0] );
}

sub print_cal {
    my $year = shift;
    my $cal  = make_cal_array($year);
    print "$year\n";
    for my $m ( 0 .. 11 ) {
        printf "%2d: ", $m + 1;
        my $c = 0;
        for my $d ( @{ $cal->[$m] } ) {
            print color( $d ? $d < 10 ? ' ' . $d : $d : '  ', $c++ );
        }
        print "\n";
    }
}

print_cal(shift || (localtime)[5]+1900);

前にも後ろにもはみ出しています。「年間」カレンダーとはよべないかも
しれません。4クールカレンダーかな。
もうすこしカレンダーらしくなるように考えてみます。

現状を維持しながら cal -y のような表示形式にするのは難しかったので、以下のワンラ
イナーでもって「年間カレンダー」に対する回答とさせてください。

  e.g.
    C:\>echo off && (for /l %i in (1,1,12) do (echo. & cal %i 2008)) && echo on
    
        January 2008
    Su Mo Tu We Th Fr Sa
           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
    
    (中略)
    
        December 2008
    Su Mo Tu We Th Fr Sa
        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

InstallShield Scriptで書いてみました。

InstallShield Scriptは Windowsアプリケーション用のインストーラーを作成するための
言語です。

インストーラーを実行すると、年を入力するダイアログが表示されます。西暦を入力して
[次へ]ボタンを押すと、ダイアログに以下の形式でカレンダーが表示されます。

  e.g.
    2008/ 1
    日 月 火 水 木 金 土
           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
    
    (中略)
    
    2008/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

# InstallShield Profesional 7.01でビルドし、動作することを確認。
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include "ifx.h"

prototype CreateCalendar(BYREF LIST, NUMBER, NUMBER);
prototype GetDayOfWeek(NUMBER, NUMBER, NUMBER);
prototype GetEndOfMonth(NUMBER, NUMBER);
prototype IsLeapYear(NUMBER);

LIST   l;
NUMBER m, y;
STRING s;

program
    AskText("年を入力してください。(1-9999)", "", s);
    if (StrToNum(y, s) < ISERR_SUCCESS) then abort; endif;
    if (y < 1 || y > 9999) then abort; endif;

    l = ListCreate(STRINGLIST);
    if (l = LIST_NULL) then abort; endif;
    for m = 1 to 12
        CreateCalendar(l, y, m);
    endfor;
    SdShowInfoList("", "", l);
    ListDestroy(l);

    abort;
endprogram

function CreateCalendar(l, y, m)
    NUMBER d, i, w;
    STRING s, t;
begin
    w = GetDayOfWeek(y, m, 1);
    d = GetEndOfMonth(y, m);

    NumToStr(s, y); t = s;
    NumToStr(s, m); if (StrLength(s) = 1) then s = " " + s; endif;
    ListAddString(l, t + "/" + s, AFTER); t = "";
    ListAddString(l, "日 月 火 水 木 金 土", AFTER);
    for i = 1 to w
        t = t + "   ";
    endfor;
    for i = 1 to d
        NumToStr(s, i);
        if (StrLength(s) = 1) then t = t + " " + s; else t = t + s; endif;
        if (GetDayOfWeek(y, m, i) = 6 || i = d) then
            ListAddString(l, t, AFTER); t = "";
        else
            t = t + " ";
        endif;
    endfor;
    ListAddString(l, "", AFTER);
end;

function GetDayOfWeek(y, m, d)
begin
    if (m <= 2) then y = y - 1; m = m + 12; endif;
    return (5 * y / 4 - y / 100 + y / 400 + (26 * m + 16) / 10 + d) % 7;
end;

function GetEndOfMonth(y, m)
begin
    switch (m)
        case 1, 3, 5, 7, 8, 10, 12 : return 31;
        case 2                     : if (IsLeapYear(y)) then return 29; else return 28; endif;
        case 4, 6, 9, 11           : return 30;
    endswitch;
end;

function IsLeapYear(y)
begin
    if (y % 400 = 0) then return 1; endif;
    if (y % 100 = 0) then return 0; endif;
    if (y %   4 = 0) then return 1; endif;
    return 0;
end;

出力例 1 と同じ形式。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import std.stdio;
import std.date;

void main(string[] args){
    auto year = args[1];
    auto newYearsDay = parse("Jan 01 " ~ year ~ " 00:00:00 GMT+0000");
    if(newYearsDay == d_time_nan){
        return;
    }
    auto wdayOffset = WeekDay(newYearsDay);
    int total;
    writefln("#=Saturday, @=Sunday");
    foreach(i, n; [31, 28 + !!inLeapYear(newYearsDay), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]){
        writef(year, "/", i + 1);
        for(int j = 1; j <= n; j++, total++){
            writef(" ", ["@", "", "", "", "", "", "#"][(wdayOffset + total) % 7], j);
        }
        writefln();
    }
}

「車輪の再発明」の誹りは免れませんが、Shell Scriptで書いてみました。

Bourne Shellでも動作するように書いたので、Unix系OSなら大半で動作すると思います。
時間のある時に AIX, HP-UX, Linux, Solaris などで動作確認してみます。

# 手元のCygwinで動作することを確認しました。

以下のワンライナーを実行することで、「年間カレンダー」を表示することが可能です。

  e.g.
    $ for i in `seq 1 12`; do ./cal.sh $i 2008; done
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/bin/sh

case "`echo -e`" in
  -e) echo_() { echo    "$@"; };;
   *) echo_() { echo -e "$@"; };;
esac

case "`echo_ '\r'`" in
  '\r') case "`(print X) 2>/dev/null`" in
          X) echo_() { print "$@"; };;
          *) PATH=/usr/5bin:$PATH; export PATH;;
        esac
        ;;
esac

is_numeric() { return `echo_ $1 | grep -E '^[0-9]+$' >/dev/null 2>&1`; }

dow() {
  y=$1; m=$2
  [ $2 -le 2 ] && { y=`expr $y - 1`; m=`expr $m + 12`; }
  expr \( 5 \* $y / 4 - $y / 100 + $y / 400 + \( 26 \* $m + 16 \) / 10 + $3 \) % 7
}

eom() {
  case $2 in
    1|3|5|7|8|10|12) echo_ 31;;
           4|6|9|11) echo_ 30;;
                  2) is_leap_year $1 && echo_ 29 || echo_ 28;;
  esac
}

is_leap_year() {
  [ `expr $1 % 400` -eq 0 ] && return 0
  [ `expr $1 % 100` -eq 0 ] && return 1
  [ `expr $1 %   4` -eq 0 ] && return 0
  return 1
}

main() {
  [ $# -ne 2 ] && { echo_ "usage: $0 [month] [year]"; return 1; }

  is_numeric $1 || return 1; [ $1 -lt 1 -o $1 -gt   12 ] && return 1
  is_numeric $2 || return 1; [ $2 -lt 1 -o $2 -gt 9999 ] && return 1

  w=`dow $2 $1 1`
  d=`eom $2 $1`

  echo_ "$2/\c"; [ `echo_ "$1\c" | wc -m` -eq 1 ] && echo_ ' \c'; echo_ "$1"
  echo_ 'Su Mo Tu We Th Fr Sa'
  for i in `seq 1 $w`; do echo_ '   \c'; done
  for i in `seq 1 $d`; do
    [ `echo_ "$i\c" | wc -m` -eq 1 ] && echo_ ' \c'
    echo_ "$i\c"
    [ `dow $2 $1 $i` -eq 6 ] && echo_ '' || echo_ ' \c'
  done
  echo_ '\n'

  return 0
}

main $*

ANSIカラーに対応してみました。

# ダム端末ではエスケープシーケンスがそのまま表示されるため、レイアウトが崩れます。

  e.g.
    53,54c54,59
    <     echo_ "$i\c"
    <     [ `dow $2 $1 $i` -eq 6 ] && echo_ '' || echo_ ' \c'
    ---
    >     case "`dow $2 $1 $i`" in
    >       0) echo_ "\033[31m$i\033[m \c";;
    >       6) echo_ "\033[34m$i\033[m"   ;;
    >       *) echo_ "$i \c"              ;;
    >     esac

すでに JavaScript + HTMLによる解が提出されていますが、 SVGで出力してみたかったの
で、 ECMA Script + SVGで書いてみました。

calendar.svgなどの名前でファイルを保存し、ブラウザにドラッグアンドドロップすれば、
以下の形式でカレンダーが表示されます。

  e.g.
    2008/ 1
    日 月 火 水 木 金 土
           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

Firefox 2.0.0.8, Internet Explorer 6, Opera 9.23で動作を確認しました。まだ試して
いませんが、Safari 3でも動作すると思います。

[注意]
  * IEで表示する場合は、プラグインとしてAdobe SVG Viewerがインストールされている
    必要があります。
  * IEで SVGファイルを実行するとスクロールバーが表示されないため、カレンダーを全
    て見ることができませんでした。スクロールバーを表示するには、別途HTMLファイル
    を用意し、 embed要素の src属性に SVGファイルを指定してあげる必要があるのかも
    しれません。
  * Calendarオブジェクトに何もかも詰め込んでいるので、ソースが汚い点はお許しを。
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="new Calendar(2008).draw(10, 10)">
    <script type="text/ecmascript">
    <![CDATA[
        function $(i) { return document.getElementById(i); }

        function isNumber(v) { return typeof(v) == 'number' && isFinite(v); }

        var Calendar = function (y /* 年 */) {
            if (!isNumber(y)) return;

            this.y = y;
            this.f = 14; // フォントサイズ

            this.isLeapYear = function (y) { return y % 4 ? true : y % 100 ? false : y % 400 ? true : false; };
            this.draw = function (l /* 左部余白 */, t /* 上部余白 */) {
                var d = [ 31, (this.isLeapYear(this.y)) ? 28 : 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
                var w = [ '日', '月', '火', '水', '木', '金', '土' ];
                var i, m, o, s, x, y;

                s = this.f / 2 /* 余白 */; y = t;
                for (m = 0; m < 12; m++) {
                    // 年, 月
                    x = l; y += this.f + s;
                    o = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                    o.setAttribute('x', x);
                    o.setAttribute('y', y);
                    o.setAttribute('font-family', 'MS Gothic');
                    o.setAttribute('font-size', this.f + 'px');
                    o.setAttribute('text-rendering', 'optimizeLegibility');
                    o.appendChild(document.createTextNode(this.y + '/' + ((m + 1 < 10) ? ' ' : '') + (m + 1)));
                    document.rootElement.appendChild(o);
                    // 曜日
                    x = l; y += this.f + s;
                    for (i = 0; i < w.length; i++) {
                        o = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                        o.setAttribute('x', x);
                        o.setAttribute('y', y);
                        o.setAttribute('font-family', 'MS Gothic');
                        o.setAttribute('font-size', this.f + 'px');
                        o.setAttribute('text-rendering', 'optimizeLegibility');
                        o.appendChild(document.createTextNode(w[i]));
                        document.rootElement.appendChild(o);
                        x += this.f + s;
                    }
                    // 日
                    x = l + (this.f + s) * (new Date(this.y, m, 1).getDay()); y += this.f + s;
                    for (i = 0; i < d[m] ; i++) {
                        o = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                        o.setAttribute('x', (i + 1 < 10) ? x + s : x);
                        o.setAttribute('y', y);
                        o.setAttribute('font-family', 'MS Gothic');
                        o.setAttribute('font-size', this.f + 'px');
                        o.setAttribute('text-rendering', 'optimizeLegibility');
                        o.appendChild(document.createTextNode(i + 1));
                        document.rootElement.appendChild(o);
                        if (new Date(this.y, m, i + 1).getDay() == 6) {
                            x = l; y += this.f + s;
                        } else {
                            x += this.f + s;
                        }
                    }
                    y += this.f + s;
                }
                // ビューポート
                document.rootElement.setAttribute('width', l + (this.f + s) * w.length);
                document.rootElement.setAttribute('height', y);
                // ビューボックス
                document.rootElement.setAttribute('viewBox', '0 0 ' + (l + (this.f + s) * w.length) + ' ' + y);
                document.rootElement.setAttribute('preserveAspectRatio', 'xMinYMin slice');
            };
        };
    ]]>
    </script>
</svg>

いくつかのUnix系OSでは正常に動作しなかったので、若干修正しました。

以下のOSで動作を確認。

  AIX     (5.2, 5.3)
  HP-UX   (11.00, 11.11, 11.23)
  Linux   (2.2, 2.4, 2.6)
  Solaris (5.5, 5.6, 5.7, 5.9, 5.10)
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/sh

case "`echo -e`" in
  -e) echo_() { echo    "$@"; };;
   *) echo_() { echo -e "$@"; };;
esac

case "`echo_ '\r'`" in
  '\r') case "`(print X) 2>/dev/null`" in
          X) echo_() { print "$@"; };;
          *) PATH=/usr/5bin:$PATH; export PATH;;
        esac
        ;;
esac

(echo '' | grep -E '' >/dev/null 2>&1) && grep_() { grep "$@"; } || grep_() { /usr/xpg4/bin/grep "$@"; }

seq_() { i=$1; while [ $i -le $2 ]; do echo_ "$i \c"; i=`expr $i + 1`; done; }

is_numeric() { return `echo_ $1 | grep_ -E '^[0-9]+$' >/dev/null 2>&1`; }

dow() {
  y=$1; m=$2
  [ $2 -le 2 ] && { y=`expr $y - 1`; m=`expr $m + 12`; }
  expr \( 5 \* $y / 4 - $y / 100 + $y / 400 + \( 26 \* $m + 16 \) / 10 + $3 \) % 7
}

eom() {
  case $2 in
    1|3|5|7|8|10|12) echo_ 31;;
           4|6|9|11) echo_ 30;;
                  2) is_leap_year $1 && echo_ 29 || echo_ 28;;
  esac
}

is_leap_year() {
  [ `expr $1 % 400` -eq 0 ] && return 0
  [ `expr $1 % 100` -eq 0 ] && return 1
  [ `expr $1 %   4` -eq 0 ] && return 0
  return 1
}

main() {
  [ $# -ne 2 ] && { echo_ "usage: $0 [month] [year]"; return 1; }

  is_numeric $1 || return 1; [ $1 -lt 1 -o $1 -gt   12 ] && return 1
  is_numeric $2 || return 1; [ $2 -lt 1 -o $2 -gt 9999 ] && return 1

  w=`dow $2 $1 1`
  d=`eom $2 $1`

  echo_ "$2/\c"; [ `echo_ "$1\c" | wc -c` -eq 1 ] && echo_ ' \c'; echo_ "$1"
  echo_ 'Su Mo Tu We Th Fr Sa'
  for i in `seq_ 1 $w`; do echo_ '   \c'; done
  for i in `seq_ 1 $d`; do
    [ `echo_ "$i\c" | wc -c` -eq 1 ] && echo_ ' \c'
    case "`dow $2 $1 $i`" in
      0) echo_ "\033[31m$i\033[m \c";;
      6) echo_ "\033[34m$i\033[m"   ;;
      *) echo_ "$i \c"              ;;
    esac
  done
  echo_ '\n'

  return 0
}

main $*

Safari 3.0.4 (Windows版)で試したところ文字化けが発生したので、フォント指定を以下
のように変更して表示を確認。

  e.g.
    29c29, 40c40, 53c53
    < o.setAttribute('font-family', 'monospace');
    ---
    > o.setAttribute('font-family', 'MS Gothic');

PostScript 版です。A4一枚に出力します。 年号を変更するときには先頭部の書き換えが必要です。
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
%!PS

/Year 2008 def

% Design Parameters 
/Columns 3 def
/FontSize 10 def
20 600  /Y0 exch def    /X0 exch def    % Top Left Position
200 150 /VMY exch def   /VMX exch def   % Size of 1 Month
20 13   /VY exch def    /VX exch def    % Size of 1 Day

% Color / Font Settings , Tables
% [ Sunday Monday ....  Saturday ]
/WColorR { [1 0 0 0 0 0 0] } def
/WColorG { [0 0 0 0 0 0 0] } def
/WColorB { [0 0 0 0 0 0 1] } def
/WFont { [ /Times-Bold /Times-Roman /Times-Roman /Times-Roman /Times-Roman
     /Times-Roman  /Times-Bold ] } def
/MonthName {[(January) (February) (March) (April) (May) (June)
 (July) (Augusut) (September) (October) (November) (December)]} def
/WeekdayName {[(Sun) (Mon) (Tue) (Wed) (Thu) (Fri) (Sat)]} def
/MDays { [31 28 31 30 31 30 31 31 30 31 30 31] } def

% -------------------------------------------------------------------------
/MonthFont {/Times-Roman findfont FontSize 2 mul scalefont setfont 0 setgray} \
def
/SetFont { dup WFont exch get findfont FontSize scalefont setfont
    dup WColorR exch get exch dup WColorG exch get exch
    WColorB exch get setrgbcolor
} def

/POS { VY mul YM0 exch sub exch VX mul XM0 add exch moveto } def
/POS2 { WOffset add dup 7 mod exch 7 idiv 2 add POS } def


/LeapYear {dup 400 mod 0 eq exch dup 100 mod 0 eq not
    exch 4 mod 0 eq and or } def
/MDay { dup MDays exch get exch 1 eq Year LeapYear and { 1 add } if } def
/ZellerFomula {
    dup 2 le { 12 add exch 1 sub exch } if
    13 mul 8 add 5 idiv exch dup 5 mul 4 idiv exch dup 100 idiv exch
    400 idiv exch sub add add 1 add 7 mod
} def

/DrawTitle {
    X0 150 add Y0 50 add moveto
    /Times-Italic findfont 100 scalefont setfont
    Year 4 string cvs show
} def

% -------------- Main Program -----------------
DrawTitle
0 1 11 {
    /Month exch def
    MonthFont
    /XM0 X0 Month Columns mod VMX mul add def
    /YM0 Y0 Month Columns idiv VMY mul sub def
    XM0 YM0 moveto
    Month 1 add 4 string cvs show
    MonthName Month get 10 0 rmoveto show
    0 1 6 {
    dup SetFont
    dup 1 POS
    WeekdayName exch get show
    } for
    /WOffset Year Month 1 add ZellerFomula def
    0 1 Month MDay 1 sub {
    dup POS2
    dup WOffset add 7 mod SetFont
    1 add 3 string cvs show
    } for
    grestore

} for

showpage

またも失敗。
72行は不要です。

月曜始まりにするには
4行目に
/StartMonday true def
を追加
33行目変更
/POS2 { WOffset StartMonday { 6 add 7 mod } if
    add dup 7 mod exch 7 idiv 2 add POS } def
63行目変更
        dup StartMonday { 6 add 7 mod } if 1 POS 

C言語復習中です. 


% ./a.out 2008
Calendar 2008 : #=Sun @=Sat
Jan | 01 02 03 04 @05 #06 07 08 09 10 11 @12 #13 14 15 16 17 18 @19 #20 21 22 23 24 25 @26 #27 28 29 30 31 
Feb | 01 @02 #03 04 05 06 07 08 @09 #10 11 12 13 14 15 @16 #17 18 19 20 21 22 @23 #24 25 26 27 28 29 
Mar | @01 #02 03 04 05 06 07 @08 #09 10 11 12 13 14 @15 #16 17 18 19 20 21 @22 #23 24 25 26 27 28 @29 #30 31 
Apr | 01 02 03 04 @05 #06 07 08 09 10 11 @12 #13 14 15 16 17 18 @19 #20 21 22 23 24 25 @26 #27 28 29 30 
May | 01 02 @03 #04 05 06 07 08 09 @10 #11 12 13 14 15 16 @17 #18 19 20 21 22 23 @24 #25 26 27 28 29 30 @31 
Jun | #01 02 03 04 05 06 @07 #08 09 10 11 12 13 @14 #15 16 17 18 19 20 @21 #22 23 24 25 26 27 @28 #29 30 
Jul | 01 02 03 04 @05 #06 07 08 09 10 11 @12 #13 14 15 16 17 18 @19 #20 21 22 23 24 25 @26 #27 28 29 30 31 
Aug | 01 @02 #03 04 05 06 07 08 @09 #10 11 12 13 14 15 @16 #17 18 19 20 21 22 @23 #24 25 26 27 28 29 @30 #31 
Sep | 01 02 03 04 05 @06 #07 08 09 10 11 12 @13 #14 15 16 17 18 19 @20 #21 22 23 24 25 26 @27 #28 29 30 
Oct | 01 02 03 @04 #05 06 07 08 09 10 @11 #12 13 14 15 16 17 @18 #19 20 21 22 23 24 @25 #26 27 28 29 30 31 
Nov | @01 #02 03 04 05 06 07 @08 #09 10 11 12 13 14 @15 #16 17 18 19 20 21 @22 #23 24 25 26 27 28 @29 #30 
Dec | 01 02 03 04 05 @06 #07 08 09 10 11 12 @13 #14 15 16 17 18 19 @20 #21 22 23 24 25 26 @27 #28 29 30 31 
 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
32
33
34
35
36
37
38
39
40
41
#include<stdio.h>
#include<time.h>

int main(int argc, char *argv[])
{
    int d = -1;
    char date[6], date_m[6];
    time_t date_tmp;
    struct tm *date_t;
    int year = atoi(argv[1]);
    
    time(&date_tmp);
    date_t = localtime(&date_tmp);
    
    date_t->tm_year = year - 1900;
    date_t->tm_mon = 0;
    date_t->tm_mday = 1;

    printf("Calendar %d : #=Sun @=Sat", year);

    while (year == date_t->tm_year + 1900) {
        if (d < date_t->tm_mon) {
            printf("\n");
            d = date_t->tm_mon;
            strftime(date_m, 6, "%b", date_t);
            printf("%s | ",  date_m);
        }
        
        if (date_t->tm_wday == 0) {
            printf("#");
        } else if (date_t->tm_wday == 6) {
            printf("@");
        }
        strftime(date, 6, "%d", date_t);
        printf("%s ",  date);
        date_t->tm_mday += 1;
        date_tmp = mktime(date_t);
        date_t = localtime(&date_tmp);
    }
    putchar('\n');
}

365行(or 366行)をだらーっと出力しますが、年間のカレンダーには一応なっています。

+------------------------+
| date                   |
+------------------------+
| 2008/1/1 (Tuesday)     | 
| 2008/1/2 (Wednesday)   | 
| 2008/1/3 (Thursday)    | 
(略)
| 2008/12/29 (Monday)    | 
| 2008/12/30 (Tuesday)   | 
| 2008/12/31 (Wednesday) | 
+------------------------+
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- いつものように数字を用意しておく
DROP TABLE if exists num_chars;
CREATE TABLE num_chars(id int not null);
INSERT INTO num_chars VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
DROP TABLE if exists nums;
CREATE TABLE nums as (
  SELECT n1.id + (n2.id * 10) + (n3.id * 100) as id
    FROM num_chars n1, num_chars n2, num_chars n3);

-- 2008年を指定
SET @ycalendar = 2008;
SELECT date_format(makedate(@ycalendar, id), '%Y/%c/%e (%W)') as date
  FROM nums
 WHERE nums.id between 1 and (365 + day(last_day(concat(@ycalendar, '-2-1'))) - 28);

Calendar.~
の文字があまりに多すぎるため、
static importで回避しています。

これって正しい使いかたですか?
 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
32
33
import java.text.*
import static java.util.Calendar.*

n = 2008

def firstDate = Calendar.getInstance()
def lastDate = Calendar.getInstance()

(0..11).each{ m ->
    firstDate.set(n, m, 1)
    lastDate.set(n, m+1, 0)

    println """< ${new SimpleDateFormat("yyyy/MM/dd").format(firstDate.time)} >"""
    ["日", "月", "火", "水", "木", "金", "土"].each {
        print it.padLeft(3)
    }
    println()

    (firstDate.get(DAY_OF_WEEK)-SUNDAY).times {
        print "".padLeft(4)
    }
    
    (firstDate.get(DATE)..lastDate.get(DATE)).each { d ->
        def eachday = Calendar.getInstance()
        eachday.set(n, m, d)
        print d.toString().padLeft(4)
        if( eachday.get(DAY_OF_WEEK) == SATURDAY ){
            println()
        }
    }
    println()
    println()
}

出力はシンプルなHTMLのtableで。
しかし、曜日を取得する関数を自作しなきゃいかんとわ...
  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:my="uri:ja.doukaku.org:my-functions"
  exclude-result-prefixes="xs fn my"
  >

  <xsl:param name="n" as="xs:integer" >2008</xsl:param>

  <xsl:output method="html" />

  <xsl:template match="/" >
    <html>
      <head>
        <title><xsl:value-of select="$n" />年の年間カレンダー</title>
      </head>
      <body>
        <h1><xsl:value-of select="$n" />年の年間カレンダー</h1>
        <xsl:for-each select="1 to 12">
          <hr />
          <h2><xsl:value-of select="." />月</h2>
          <xsl:call-template name="mcalender">
            <xsl:with-param name="year" select="$n" />
            <xsl:with-param name="month" select="." />
          </xsl:call-template>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <xsl:template name="mcalender">
    <xsl:param name="year" as="xs:integer" />
    <xsl:param name="month" as="xs:integer" />

    <!-- 今月1日 -->
    <xsl:variable name="first-date" as="xs:date"
      select="xs:date(
        fn:string-join(
          (my:pad($year,4),my:pad($month,2),'01'),
          '-'))" />
    <!-- 次月1日 -->
    <xsl:variable name="next-first-date" as="xs:date">
      <xsl:choose>
        <xsl:when test="$month=12">
          <xsl:value-of
            select="xs:date(
              fn:string-join(
                (my:pad($year+1,4),'01','01'),
                '-'))" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of
            select="xs:date(
              fn:string-join(
                (my:pad($year,4),my:pad($month+1,2),'01'),
                '-'))" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <!-- 今月の日数を求める -->
    <xsl:variable name="days" as="xs:integer"
      select="fn:days-from-duration($next-first-date - $first-date)" />

    <!-- 今月1日 の曜日を求める -->
    <xsl:variable name="start-day" as="xs:integer"
      select="my:get-day($first-date)" />

    <!-- カレンダーの左上の開始日 -->
    <xsl:variable name="rstart" as="xs:integer"
      select="1 - $start-day" />
    <!-- カレンダーの右下の終了日 -->
    <xsl:variable name="rend" as="xs:integer">
      <xsl:choose>
        <xsl:when test="(($days - $rstart + 1) mod 7)=0">
          <xsl:value-of select="$days - $rstart + 1" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="((($days - $rstart+1) idiv 7)+1)*7" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <table border="1">
      <xsl:call-template name="calender-header" />
      <xsl:for-each select="1 to $rend idiv 7">
        <tr>
          <xsl:for-each select="$rstart+((.-1)*7) to $rstart+(.*7)-1">
            <td>
              <xsl:choose>
                <xsl:when test="1 &lt;= . and . &lt;= $days">
                  <xsl:value-of select="." />
                </xsl:when>
                <xsl:otherwise>
                  <xsl:text>&#160;</xsl:text>
                </xsl:otherwise>
              </xsl:choose>
            </td>
          </xsl:for-each>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>

  <xsl:template name="calender-header">
    <tr>
      <xsl:for-each select="('日','月','火','水','木','金','土')">
        <th><xsl:value-of select="." /></th>
      </xsl:for-each>
    </tr>
  </xsl:template>

  <!-- 曜日を取得する -->
  <xsl:function name="my:get-day" as="xs:integer">
    <xsl:param name="date" as="xs:date" />
    <xsl:variable name="epoch" as="xs:date"
      select="xs:date('1970-01-01')" />

    <xsl:variable name="day" as="xs:integer"
      select="(fn:days-from-duration($date - $epoch) + 4) mod 7" />
    <xsl:choose>
      <xsl:when test="$day &lt; 0">
        <xsl:value-of select="$day + 7" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$day" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:function>

  <xsl:function name="my:pad" as="xs:string">
    <xsl:param name="i" as="xs:integer" />
    <xsl:param name="digit" as="xs:integer" />

    <xsl:variable name="seq" as="xs:string*">
      <xsl:for-each
        select="1 to $digit - fn:string-length(xs:string($i))">
        <xsl:sequence select="'0'" />
      </xsl:for-each>
      <xsl:sequence select="xs:string($i)" />
    </xsl:variable>

    <xsl:value-of select="fn:string-join($seq, '')" />
  </xsl:function>
</xsl:stylesheet>

Gauche で書いてみました。
出力部が意外と長くなってしまった。

出力例:
    << 2007/1 >>
 Sun  Mon  Tue  Wed  Thu  Fri  Sat
        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  
 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
32
33
34
35
36
37
38
39
40
(use srfi-1)
(use srfi-19)
(use gauche.collection)
(use gauche.sequence)

(define (show-calendar n)
  (define (leap-year? n)
    (or (zero? (modulo n 400))
        (and (zero? (modulo n 4))
             (not (zero? (modulo n 100))))))
  (define week-days
    (circular-list 0 1 2 3 4 5 6))
  (define (make-calendar)
    (map (lambda (month days)
           (let ((m (date-week-day (make-date 0 0 0 0 1 month n 0))))
             (append (make-list m #f) (take (drop week-days m) days))))
         (iota 12 1)
         `(31 ,(if (leap-year? n) 29 28) 31 30 31 30 31 31 30 31 30 31)))
  (for-each-with-index
   (lambda (i ws)
     (format #t "   << ~A/~A >>~%" n (+ i 1))
     (format #t "Sun  Mon  Tue  Wed  Thu  Fri  Sat~%")
     (let loop ((ws ws) (d 1))
       (cond
        ((null? ws)
         (format #t "~%~%"))
        ((not (car ws))
         (format #t "     ")
         (loop (cdr ws) d))
        (else
         (format #t (case (car ws)
                      ((0) "<~2@A> ")
                      ((6) "[~2@A]~%")
                      (else " ~2@A  "))
                 d)
         (loop (cdr ws) (+ d 1))))))
   (make-calendar)))

(define (main args)
  (show-calendar (string->number (cadr args))))

 Lua始めました。
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
-- 閏年かどうか判定
L = 
function (y)
    if y % 400 == 0 then return 1 end
    if y % 100 == 0 then return 0 end
    if y %   4 == 0 then return 1 end
    return 0
end

-- 指定された日の曜日を取得
W = 
function (y, m, d)
    return tonumber(os.date('%w', os.time({ year = y, month = m, day = d })))
end

(function (v)
    local d = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    local w = { 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' }
    local m, t, y

    if #v ~= 2 then
        io.stderr:write('usage: ' .. v[0] .. ' [month] [year]\n')
        return 1
    end

    m, y = tonumber(v[1]), tonumber(v[2])

    if m == nil or y == nil then
        io.stderr:write('usage: ' .. v[0] .. ' [month] [year]\n')
        return 1
    end

    if m < 1 or m > 12 then
        io.stderr:write('cal: illegal month value: use 1-12\n')
        return 1
    end
    if y < 1 or y > 9999 then
        io.stderr:write('cal: illegal year value: use 1-9999\n')
        return 1
    end

    -- 閏日を補正
    d[2] = d[2] + L(y)

    t = string.format('%4d/%2d', y, m) .. '\n'
     .. table.concat(w, ' ') .. '\n'
     .. string.rep('   ', W(y, m, 1))
    for i = 1, d[m] do
        t = t .. string.format('%2d', i)
        t = W(y, m, i) < 6 and t .. ' ' or t .. '\n'
    end
    print(t)

    return 0
end)(arg)

Index

Feed

Other

Link

Pathtraq

loading...