風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

究極超人あ〜るネタを調べていたらFreeBSDのカレンダーコマンド(ncal)の不具合に行き当った件

ちょっと、2月のカレンダーと「14日の土曜日」ネタがらみで、

のようなことをつぶやきたいな、と思って、念のため調べていたら、いつの間にかFreeBSDのncalコマンドバグなのかも?と思われるものを見つけてしまった、という話。
いや、我ながら何を言っているのかわかりませんが…。

■ 経緯

◇ 13日の金曜日よりも14日の土曜日が重要なクラスタより
  • タイムライン上に「そういえば13日の金曜日」のようなツイートがちらちら
  • すると「14日は土曜日ではないか!」と反応しはじめる、偏った人々(ゆうきまさみフォロワーの方ね)
◇ 2月で「14日の土曜日」だった年を調べたところ…

『あ〜るのバレンタイン話は、確か1987年だったよなぁ』

$ cal 2 1987
      21987
日 月 火 水 木 金 土
 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

『うんうん。えーと、今年はそれから何回目?ついでに西暦1年から調べてみるか』

$ python
>>> import calendar
>>> all, counter = 0, 0
>>> for year in range(1, 1+2015):
...   if calendar.weekday(year, 2, 14) == 5:
...     all += 1
...     if 1987 < year: counter += 1
...     print year
...
4
9
15
:(中略)
1750
1756
:(中略)
1981
1987
1998
2004
2009
2015
>>> print all, counter
288 4
>>>

『ほぉ、西暦4年もそうだったのか』

$ cal 2 4
        24
日 月 火 水 木 金 土
                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

『あ、あれれ……?』

(中略)

$ cal 2 1750
      21750
日 月 火 水 木 金 土
             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

『んん…?』


― 間 ―

$ cal 2 1756
      21756
日 月 火 水 木 金 土
 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

『あ、これはちゃんと14日の土曜日になってるな。このあたりが境目、か……?』

◇ 原因は?
$ man cal
CAL(1)                  FreeBSD General Commands Manual                 CAL(1)

NAME
     cal, ncal ― displays a calendar and the date of Easter

(中略)

     -s country_code
             Assume the switch from Julian to Gregorian Calendar at the date
             associated with the country_code.  If not specified, ncal tries
             to guess the switch date from the local environment or falls back
             to September 2, 1752.  This was when Great Britain and her
             colonies switched to the Gregorian Calendar.

なる程、デフォルトだと、イギリスがグレゴリオ暦を採用した1752年9月14日以降がグレゴリオ暦表示で、それ以前はユリウス暦表示なのね。

$ cal 9 1752
      91752
日 月 火 水 木 金 土
       1  2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

ほうほう。グレゴリオ暦での1752年9月13日はユリウス暦1752年9月2日なので、こうなるのね。

◇ 国名コードを指定するとどうなる?

これ、他の国だとどうなるんだろう?

略号一覧

ISO 3166-1 - Wikipedia

コマンドの -s オプションで指定できる国名コード(country_code)は二字のもの、ただし、網羅されているわけではない。


そもそも、イタリア(1582年10月15日グレゴリオ暦制定)だと?

《参考資料》ユリウス暦からグレゴリオ暦への切り替え
・1582年にローマ法王グレゴリオ13世がユリウス暦からグレゴリオ暦への転換を宣言したことにより、この時期にユリウス暦からグレゴリオ暦への切り替えが行われた国があります。
・カトリック国のイタリア、スペインなどでは1582年10月に切り替えが行われ、1582年10月4日の翌日が10月15日になり、1582年10月5日から10月14日までは存在しません。

みんなの知識【ちょっと便利帳】 - その曜日は何日? 各年・各月の曜日を調べる - 西暦と月を設定し、各曜日の日付を一覧表示
$ cal -s IT 10 1582
Usage: cal [general options] [-hjy] [[month] year]
       cal [general options] [-hj] [-m month] [year]
       ncal [general options] [-hJjpwy] [-s country_code] [[month] year]
       ncal [general options] [-hJeo] [year]
General options: [-NC3] [-A months] [-B months]
For debug the highlighting: [-H yyyy-mm-dd] [-d yyyy-mm]

あ、-s オプションは ncal だけか。

$ ncal -s IT 10 1582
    1015821 18 252 19 263 20 274 21 2815 22 2916 23 3017 24 31

うん、確かに10/15を境に切り替わっているな。
じゃあ、日本だとどうなの?

・1873年1月1日に当たる明治5年12月3日(旧暦)を明治6年1月1日(新暦)とする太陽暦への改暦(明治改暦)。

グレゴリオ暦 - Wikipedia
$ ncal -s JP -A 1 12 1872
    121872          118734 11 18 25     1  8 15 22 295 12 19 26     2  9 16 23 306 13 20 27     3 10 17 24 317 14 21 28     4 11 18 251  8 15 22 29     5 12 19 262  9 16 23 30     6 13 20 273 10 17 24 31     7 14 21 28

ん?日付は連続しているし……

$ ncal -s IT -A 1 12 1872
    121872          118732  9 16 23 30     6 13 20 273 10 17 24 31     7 14 21 284 11 18 25     1  8 15 22 295 12 19 26     2  9 16 23 306 13 20 27     3 10 17 24 317 14 21 28     4 11 18 251  8 15 22 29     5 12 19 26

イタリアの暦とも違っている、ということは、このあたりはまだユリウス暦で表示されている、ということ?

◇ ncalでは、日本はいつグレゴリオ暦に切り替えたことになっているのか?
$ ncal -s JP -A 1 12 1918
    121918          119193 10 17           6 13 20 274 11 18           7 14 21 285 12           1  8 15 22 296 13           2  9 16 23 307 14           3 10 17 24 311  8 15           4 11 18 252  9 16           5 12 19 26

は、はい……?
1919年1月1日から切り替えたことになっているのか……なぜ???

結論(暫定)

結局、自分が調べた範囲では理由がわからず、単純なプログラム上の登録ミスではないか? と考えている。
実は深い理由があるのかも……ご存知の方、教えてください。


FreeBSDのncalコマンドソースコード中で、

[base] Contents of /head/usr.bin/ncal/ncal.c

69	/* The switches from Julian to Gregorian in some countries */
70	static struct djswitch {
71	        const char *cc; /* Country code according to ISO 3166 */
72	        const char *nm; /* Name of country */
73	        date dt;        /* Last day of Julian calendar */
74	} switches[] = {
75	        {"AL", "Albania",       {1912, 11, 30}},
76	        {"AT", "Austria",       {1583, 10,  5}},

のように、ISO 3166の国名コード(2文字)毎にユリウス暦→グレゴリオ暦に移り変わった日付の定義がなされているが、そのうち、日本(JP)用の定義が違っているのではないか、と。

$ diff -c ncal.c.r241737 ./ncal.c
*** ncal.c.r241737      2015-02-13 02:24:25.000000000 +0900
--- ./ncal.c    2015-02-13 02:26:03.000000000 +0900
***************
*** 91,97 ****
        {"HU", "Hungary",       {1587, 10, 21}},
        {"IS", "Iceland",       {1700, 11, 16}},
        {"IT", "Italy",         {1582, 10,  4}},
!       {"JP", "Japan",         {1918, 12, 18}},
        {"LI", "Lithuania",     {1918,  2,  1}},
        {"LN", "Latin",         {9999, 05, 31}},
        {"LU", "Luxembourg",    {1582, 12, 14}},
--- 91,97 ----
        {"HU", "Hungary",       {1587, 10, 21}},
        {"IS", "Iceland",       {1700, 11, 16}},
        {"IT", "Italy",         {1582, 10,  4}},
!       {"JP", "Japan",         {1872, 12, 19}},
        {"LI", "Lithuania",     {1918,  2,  1}},
        {"LN", "Latin",         {9999, 05, 31}},
        {"LU", "Luxembourg",    {1582, 12, 14}},
$ # 改修版の実行結果
$ ./ncal -s JP -A 1 12 1872
    121872          118734 11 18           6 13 20 275 12 19           7 14 21 286 13           1  8 15 22 297 14           2  9 16 23 301  8 15           3 10 17 24 312  9 16           4 11 18 253 10 17           5 12 19 26
◇ 注釈

そもそも日本の場合、べつにグレゴリオ暦の前にユリウス暦を使っていたわけではないので、定義するのはナンセンス、という話はある。
その上で、元のソースコード中で「グレゴリオ暦1919年1月1日」(厳密にはその前日に当たるユリウス暦1918年12月18日)が登録されている根拠もよくわからず、まだこれよりは、明治改暦が実施された「グレゴリオ暦1873年1月1日」(同1872年12月19日)を登録した方がもっともらしいのではないか、と考える次第。

■ 補足等

◇ ncal のバグについて

そもそもncal中の国別のユリウス暦→グレゴリオ暦切替日定義は、日本以外に関しても信頼できるかどうかは怪しい、のかも。
ncal(1)

BUGS
The assignment of Julian--Gregorian switching dates to country codes is
historically naive for many countries.

https://www.freebsd.org/cgi/man.cgi?query=ncal&sektion=1&manpath=FreeBSD+6.0-RELEASE
$ ncal -p
 AL Albania        1912-11-30      IT Italy          1582-10-04
 AT Austria        1583-10-05     *JP Japan          1918-12-18
 AU Australia      1752-09-02      LI Lithuania      1918-02-01
 BE Belgium        1582-12-14      LN Latin          9999-05-31
 BG Bulgaria       1916-03-18      LU Luxembourg     1582-12-14
 CA Canada         1752-09-02      LV Latvia         1918-02-01
 CH Switzerland    1655-02-28      NL Netherlands    1582-12-14
 CN China          1911-12-18      NO Norway         1700-02-18
 CZ Czech Republic 1584-01-06      PL Poland         1582-10-04
 DE Germany        1700-02-18      PT Portugal       1582-10-04
 DK Denmark        1700-02-18      RO Romania        1919-03-31
 ES Spain          1582-10-04      RU Russia         1918-01-31
 FI Finland        1753-02-17      SI Slovenia       1919-03-04
 FR France         1582-12-09      SW Sweden         1753-02-17
 GB United Kingdom 1752-09-02      TR Turkey         1926-12-18
 GR Greece         1924-03-09      US United States  1752-09-02
 HU Hungary        1587-10-21      YU Yugoslavia     1919-03-04
 IS Iceland        1700-11-16

※ラテン世界の9999年

$ ncal -s LN 9999
                                  9999
    1234月
月   1  8 15 22 29        5 12 19 26        5 12 19 26        2  9 16 23 302  9 16 23 30        6 13 20 27        6 13 20 27        3 10 17 243 10 17 24 31        7 14 21 28        7 14 21 28        4 11 18 254 11 18 25        1  8 15 22        1  8 15 22 29        5 12 19 265 12 19 26        2  9 16 23        2  9 16 23 30        6 13 20 276 13 20 27        3 10 17 24        3 10 17 24 31        7 14 21 287 14 21 28        4 11 18 25        4 11 18 25        1  8 15 22 29

    5678月
月      7 14 21 28                                           16 23 301  8 15 22 29                                           17 24 312  9 16 23 30                                           18 253 10 17 24 31                                           19 264 11 18 25                                           13 20 275 12 19 26                                           14 21 286 13 20 27                                           15 22 29

    9101112月
月      6 13 20 27        4 11 18 25     1  8 15 22 29        6 13 20 277 14 21 28        5 12 19 26     2  9 16 23 30        7 14 21 281  8 15 22 29        6 13 20 27     3 10 17 24        1  8 15 22 292  9 16 23 30        7 14 21 28     4 11 18 25        2  9 16 23 303 10 17 24        1  8 15 22 29     5 12 19 26        3 10 17 24 314 11 18 25        2  9 16 23 30     6 13 20 27        4 11 18 255 12 19 26        3 10 17 24 31     7 14 21 28        5 12 19 26
◇ 覚書
  • FreeBSD の cal コマンドは ncal コマンドのエイリアス
    バイナリレベルで同一。コマンド名で判別し、ncalの -C オプション相当の表示をしている模様。
◇ 愚痴

というかそもそも、「とある年月を境にしてユリウス暦とグレゴリオ暦が切り替えられ、かつ、一見して(表示結果では)それとわからない」なんて仕様のカレンダー自体、やめて欲しいと思うのは自分だけ?
「デフォルトでグレゴリオ暦換算のカレンダーを表示、オプションでユリウス暦換算でも表示可能だよ」でええやん…。