Hatena::ブログ(Diary)

風柳メモ このページをアンテナに追加 RSSフィード Twitter

2015-04-10

さくらのレンタルサーバ・共有SSLで .htaccess によりSSLのみのアクセス許可を設定する方法

さくらのレンタルサーバで共有SSLを使用する際に、SSLのみのアクセス許可を設定(SSLアクセス強制のため、http://〜 は https://〜 へリダイレクト)するための .htaccess の記述方法を調べてみた。


設定する .htaccess の内容

<IfModule mod_rewrite.c>
RewriteEngine On
# --- SSLアクセスでない場合(${ENV:HTTPS} が 'on' でなく、且つ、%{HTTP:X-Sakura-Forwarded-For} が未設定の場合)には
#     https://%{SERVER_NAME}%{REQUEST_URI} へリダイレクト
RewriteCond %{ENV:HTTPS} !^on$
RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$
RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</IfModule>


なお、WordPress を設置している場合(例として /wordpress/ 下)

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /wordpress/

# --- SSLアクセスでない場合(%{ENV:HTTPS} が 'on' でなく、且つ、%{HTTP:X-Sakura-Forwarded-For} が未設定の場合)には
#     https://%{SERVER_NAME}%{REQUEST_URI} へリダイレクト
RewriteCond %{ENV:HTTPS} !^on$
RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$
RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wordpress/index.php [L]

</IfModule>

のようにすればよいと思われる。

WordPress の元の .htaccess の RewriteBase 行と最初のRewriteCond 行の間に、SSLアクセス強制用の RewriteCond・RewriteRule を挟み込む。 /wordpress/(二か所)は環境にあわせて書き換えること。


また、wp-config.php の「/* That's all, stop editing! Happy blogging. */(/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */)」直前に、

if ( isset($_SERVER['HTTP_X_SAKURA_FORWARDED_FOR']) ) {
    $_SERVER['HTTPS'] = 'on';
    $_ENV['HTTPS'] = 'on';
}

/* That's all, stop editing! Happy blogging. */

という記述を追加しておく。


解説(覚え書き)

さくらのレンタルサーバで共有SSLを使用する場合、以下の点に留意する必要がある。


.htaccess からの参照時
  • %{SERVER_PORT} には、SSLかそうでないかによらず '80' が設定される。
    このため、%{SERVER_PORT}ではSSL接続かどうかの判別はできない。
    「RewriteCond %{SERVER_PORT} ^80$」や「RewriteCond %{SERVER_PORT} !^443$」は常に真となるために、リダイレクトループが発生してしまう。
  • SSLアクセス時には通常、 %{ENV:HTTPS} には 'on' が、%{HTTP:X-Sakura-Forwarded-For} にはクライアント(リクエスト元)のIPアドレスが設定される。*1
    ただし、SSLアクセスした場合であっても、mod_rewrite.c の RewriteRule によりリライトされるケースでは、リライト後には %{ENV:HTTPS} が未設定となってしまう。
    RewriteRuleの[R]フラグによりhttps://〜にリダイレクトされた場合には 'on' が設定される。

上記の .htaccess では、リダイレクトするかどうかの判別に、

RewriteCond %{ENV:HTTPS} !^on$

だけでなく、

RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$

のような条件(%{HTTP:X-Sakura-Forwarded-For}が未設定)を設定している。


これをせずに %{ENV:HTTPS} のみで判定してしまうと、例えば WordPress を設置したサイト上の http://〜/wordpress/year/month/day/ というページ(URL)にアクセスした場合、

URL%{REQUEST_URI}%{ENV:HTTPS}%{HTTP:X-Sakura-Forwarded-For}mod_rewrite.cで実行されるルール結果
1http://〜/wordpress/year/month/day//wordpress/year/month/day/(未設定)(未設定)RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L][A] リダイレクト
2https://〜/wordpress/year/month/day//wordpress/year/month/day/'on'<IPアドレス>RewriteRule . /wordpress/index.php [L][B] リライト
3https://〜/wordpress/year/month/day//wordpress/index.php(未設定)<IPアドレス>RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L][C] リダイレクト
4https://〜/wordpress/index.php/wordpress/index.php'on'<IPアドレス>RewriteRule ^index\.php$ - [L][D](このまま)

のように、意図しないURLの PATH の書き換えが発生してしまう。

このため、[C] (3→4)のリダイレクトを発生させないように、条件を追加している。

むしろ「RewriteCond %{ENV:HTTPS} !^on$」の方は無くても現状では動作する。


PHP からの参照時
  • SSLアクセス時には、$_SERVER['HTTP_X_SAKURA_FORWARDED_FOR'] および $_ENV['HTTP_X_SAKURA_FORWARDED_FOR'] にはクライアント(リクエスト元)のIPアドレスが設定される。
  • SSLアクセスした場合であっても、mod_rewrite.c の RewriteRule によりリライトされるケースでは、$_SERVER['HTTPS'] と $_ENV['HTTPS'] が設定されない。
    RewriteRuleの[R]フラグによりhttps://〜にリダイレクトされた場合には 'on' が設定される。

WordPress 等では、SSL 接続かどうかを $_SERVER['HTTPS'] もしくは $_ENV['HTTPS'] の状態で判別している。

このため、上記のように $_SERVER['HTTP_X_SAKURA_FORWARDED_FOR'] が設定されているかどうかで SSL かどうかを判別し、SSL の場合には $_SERVER['HTTPS'] および $_ENV['HTTPS'] に 'on' を設定するような処理を追加してやる必要がある。


参考

*1:将来的にさくら側で仕様変更が行われると、%{HTTP:X-Sakura-Forwarded-For}による判別が出来なくなる可能性はある。

2015-03-14

Google App Engineのデプロイ時に認証エラーがでる場合の対策

久しぶりに GAE のアプリケーションをいじった後で、デプロイしようとすると、

Password for username: Use an application-specific password instead of your regular account password.
See http://www.google.com/support/accounts/bin/answer.py?answer=185833
However, now the recommended way to log in is using OAuth2. See
https://developers.google.com/appengine/docs/python/tools/uploadinganapp#Python_Password-less_login_with_OAuth2
2015-03-14 09:00:00,000 ERROR appcfg.py:2448 An error occurred processing file '': HTTP Error 401: Unauthorized. Aborting. 
Error 401: --- begin server output ---
Must authenticate first.
--- end server output ---

のようなエラーが出てしまった。


どうやら、アプリケーションパスワードを取得するか、OAuth2 認証を使用しろ、ということらしい(推奨はOAuth2)。


アプリケーションパスワードを取得して使用する場合には Google App Engine Launcher でもそのまま使えるが、OAuth2 認証の方だと Launcher で使用する方法が見つからなかった。コマンド プロンプトから appcfg.py を使用するしかないのかも。


アプリケーションパスワードを取得して使用する場合

エラーメッセージ内で指示されているSign in using App Passwords - Accounts Helpに書かれている通り、アプリ パスワードのページに行き、必要に応じてアプリケーションで使用しているGoogleアカウントでログイン後、アプリ パスワード(16文字)を生成する。

『端末を選択』の項目は使用している端末を選択または入力(『Windows パソコン』等)、『アプリを選択』はその他『(名前を入力)』を選んで適当な名前(『Google App Engine』など)を付ければよい。

f:id:furyu-tei:20150314114346j:image

表示されたアプリ パスワード(スペースは含まない16文字)は、他人に知られないように覚えるかメモしておく。

「このパスワードを覚えておく必要はないので……」とか書かれているが、覚えておく必要、あるよ……。


取得したアプリ パスワードは、これまで使っていたユーザーパスワードの代わりに、デプロイ時に指定する(emailはそのまま使用する)。


OAuth2 認証を使用する場合

エラーメッセージ内で指示されているPassword-less Login with OAuth2に従って、コマンド プロンプトより、

appcfg.py --oauth2 update myapp/

とする。

myapp/ は、自分のアプリケーションのフォルダを指定。

設定によっては、appcfg.py だけだと実行できないかもしれない。その場合は「python "C:\Program Files (x86)\Google\google_appengine\appcfg.py"」のようにフルパス指定して実行する。

すると、ブラウザで認証画面が開くので、画面の指示に従って認証すれば、デプロイすることが出来る。


いちいち認証手順を行いたくない場合、2回目以降は、

appcfg.py --oauth2_refresh_token=<token> update myapp/

のように token を指定して実行すればよい。


この token は、ユーザーフォルダ(%UserProfile%)にある .appcfg_oauth2_tokens に記述されているので、これをコピーして使う。

type %UserProfile%\.appcfg_oauth2_tokens

として、表示されたデータ(JSON形式)のうち、

...  "refresh_token": "<token>", ... 

のようになっている refresh_token を使用する。

2015-02-16

正常系・異常系共通の後処理を行うための手法検討と、「途中return禁止」「goto禁止」等の“一律禁止”問題

たいたい竹流さん @ガード節に関するツイートについて、

のようにリプライしたところ、

のようにコメント頂いた。


これに端を発した議論が有意義に思えたので、自分用の覚書として要点をまとめておく。


■ ガード節について

ガード節そのものについては、自分は最初から積極的に賛同している立場であるため、この記事では特記しない。


ガード節の有効性については、下記記事の引用部分に集約されているように思う。

これは、「ガード節による入れ子条件記述の置き換え」と呼ばれるもので、リファクタリング本では次のように説明されている。

「ガード節による入れ子条件記述の置き換え」のキーポイントを強調しておきましょう。if-then-else 構造が使われる時は、if 部にも else 部にも同じウエイトが置かれています。これは、プログラムの読み手に対して両方共等しく起こり得ること、等しく重要であることを伝えます。逆に、ガード節は『めったに起きないが、起きた時には、何もしないで出ていく』ことを伝えます。


リファクタリング - 第九章 条件記述の単純化 p.251

小野和俊のブログ:ガード節を用いた if-then-else 文の置き換え

■ 自分の発言の趣旨

上記であげた自分のリプライについてだが、もともと、複数のreturnを設けたり、Wrapper関数化するのに消極的だったのは、主にデバッグ上の要求から。


  • 複数 return があると、そのすべてにブレイクポイントやログ出力処理などを設ける必要が出てくるので、出口は一つにしたい
  • Wrapper化により異常系/正常系処理と共通の後処理とを切り離してしまうと、後処理内で異常系/正常系のローカル変数を参照できない
  • 後処理を共通化するためだけの単純なWrapperを設けるのは、余分にコールスタックが増えるということであり、デバッグ時にコールスタックを表示させた際、ソースコードにおいてネストが深くなるのと同等の見通しの悪さが生じる
  • Wrapperのために関数名が増えるのは面倒(苦笑)(言うまでもなく、これは副次的な理由)

なお、「ルールだから」という理由で途中 return を設けないわけではなく、デバッグ上の理由で消極的なだけなので、「必ず共通の後処理が実行される」保証があれば、途中で return することについては特に忌避感はない。


■ try-catch-finally で万全

実は、Java 等の try-catch-finally 相当の構文が使用できる言語であれば、次のように記述することで満足できる。

try {
    result = ABNORMAL;
    // [I] 共通の前処理(スコープ内で使用するローカル変数設定等)
    
    if ( /* 異常系判定条件 */ ) { // ガード節
        // [A] 異常系処理
        return result;
    }
    // ※状況に応じて複数のガード節を記述
    
    // [N] 正常系処理
    result = NORMAL;
    return result;
}
catch (Throwable errorObject) {
    // [E] 想定外の異常系(例外)処理
    throw errorObject;
}
finally {
    // [F] 共通の後処理
    // ※デバッグ用に、この部分にローカル変数を参照するログ出力処理等を入れたり
    //   条件付きブレイクポイントを設定したりできる
}


try-catch-finallyは、try節中等に return があっても必ず finally 節が実行されるため、ブレイクポイントやログ出力処理などもここにまとめることができ、Wrapperを用意する必要もなくなり、上記の要求は全て満たされる。



■ try-catch-finally の類が使えない場合の代替手段

問題は、try-catch-finally のような「途中の経過に関わりなく後処理が実行される」という機能を持たない言語の場合。

たいたい竹流さんが募集して下さった意見などを元に、いくつかの代替手段とその問題点等をあげる。


◇ 一度のみ実行するループを用いた方法
result = ABNORMAL
// [I] 共通の前処理(スコープ内で使用するローカル変数設定等)

<ループ> {
    if ( /* 異常系判定条件 */ ) {
        // [A] 異常系処理
        break
    }
    
    // [N] 正常系処理
    result = NORMAL
    break
}
// [F] 共通の後処理
return result

for(;;){…} や while(true){…}、do{…}while(false)を使う手法。


問題点

  • 初見の人には意図が判り辛い(do{…}while(false)であれば多少まし、ただし、言語によっては定数falseを使うことによる警告も)*1
  • 一度しか実行されないループ→ループとして機能していない→構造化構文の誤用

◇ goto を用いた方法
result = ABNORMAL
// [I] 共通の前処理(スコープ内で使用するローカル変数設定等)

if ( /* 異常系判定条件 */ ) {
    // [A] 異常系処理
    goto finally
}
    
// [N] 正常系処理
result = NORMAL

finally:
// [F] 共通の後処理
return result 

問題点


◇ 関数内関数を用いた方法
result = ABNORMAL
// [I] 共通の前処理(スコープ内で使用するローカル変数設定等)

(<関数>(){
    if ( /* 異常系判定条件 */ ) {
        // [A] 異常系処理
        return;
    }
    
    // [N] 正常系処理
    result = NORMAL
    return;
})()

// [F] 共通の後処理
return result

問題点

  • 一見してあえて関数内関数を使う意図が判り辛い
  • 本来スコープ内に閉じ込めるべきローカル変数を外出ししている(必要な変数をすべて引数として渡す、という方法もあるが、それならば最初から外部関数なりにするのと変わらず、内部関数にするメリットがない)

◇ classのデストラクタを利用した方法
<クラス定義> {
    result = ABNORMAL
    // [I] 共通の前処理(スコープ内で使用するローカル変数設定等)

    <メイン処理>(…) {
        if ( /* 異常系判定条件 */ ) {
            // [A] 異常系処理
            return result
        }
    
        // [N] 正常系処理
        result = NORMAL
        return result
    }

    <デストラクタ>(…) {
        // [F] 共通の後処理
    }
}

問題点


■ コーディングルールにおける「一律禁止」問題

上記の議論に付随して、「コーディングルールにおける、思考を停止した『一律禁止』は問題である」という話も出た。

知らず知らずのうちに思考停止に陥っていることが多いので、自戒を込めてまとめる。


ルールには、もちろんそれが制定された意味はある(場合がほとんどだ)けれども、盲目的に従うのではなく、歴史的な経緯を把握した上で、現状と照らし合わせてきちんと考えようということだと思う。

とはいえ、クライアントに要求されたルールに従わざるを得ない等、なかなか理想通りには行かないことも多いのだけれど……せめて吟味し批判する姿勢は保ちたいもの。



等々。


◇「途中return禁止」ルール

もともとのガード節に関するツイートに対し、「出口が複数あって却って見づらい」旨の否定的な意見受けて

ここで注意すべきなのは、構造化プログラミングの文脈では、「1つの入り口と1つの出口を持つ、順次・選択・反復の3つの論理構造の組み合わせ」というスタイルはプログラム実行時のフローをソースコードから追いやすくするために用いられている、という点だ。

MISRA-Cにおける「関数の末尾以外の return 禁止」の真意 - 新・日々録 by TRASH BOX@Eel

関数内での途中returnは、大抵がネストを浅くする――つまり静的構造自体が複雑化することを避ける目的で使用されることが多いはずだ。構造化プログラミングでは考慮されていない部分なのだ。

こうなってくると、基本的には「1つの入り口と1つの出口を持つ、順次・選択・反復の3つの論理構造の組み合わせ」というスタイルをとりつつも必要に応じてルールを破る、というスタイルに落ち着く。

MISRA-Cにおける「関数の末尾以外の return 禁止」の真意 - 新・日々録 by TRASH BOX@Eel

◇「goto禁止」ルール

上述した「一度のみ実行するループを用いた方法」に関して、そういった(自分でも自覚しているような)トリッキーな方法を使うのではなく、gotoを使うべき、という意見




タイムリーな記事もあった。

200万近いC言語のファイルと1万1千件を超えるプロジェクトからランダムに抽出した統計的に有効なサンプルを質的および量的に分析したところ、開発者はほとんどの場合gotoの使用を適切に制限しており、Dijkstra氏が懸念したような無制限な使用は行われていないことが判明した。これらのことから、実際にはgotoは有害でないものと考えられる。

C言語の開発者によるgoto文の使い方を対象とした実証研究の結果、「goto文は無害だと考えられる」 - エキサイトニュース

「goto一律禁止」のルールで作られているものはそもそもgoto文が現れないので抽出されないだろうし、そうでない場合は思考停止すること無くよく考えて使う開発者の割合が高くなるような気もするので、この結果を鵜呑みにして実際に解禁したら無制限な使用が増える、という可能性も否定できない気はするけれど。

*1:自分の場合は最初見た例がdo{…}while(false)だったために割とすんなり理解できたし、その土台があったため、警告対策でfor(;;){…}に切り替えた際も特に違和感を感じなかったという経緯がある

2015-02-13

究極超人あ〜るネタを調べていたら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 オプション相当の表示をしている模様。

◇ 愚痴

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

「デフォルトでグレゴリオ暦換算のカレンダーを表示、オプションでユリウス暦換算でも表示可能だよ」でええやん…。

2015-02-11

萌えるステータスコード 〜 生徒会長編 〜

無味乾燥なHTTPステータスコードに、潤いを。

麗しの生徒会長*1の台詞を通して、理解を深めよう!*2

何番煎じだ、とかいわない。

HTTPステータスコードの本来の意味に対して、台詞の内容に違和感がある(解釈がおかしい)等のコメントを緩募。


1xx Informational 情報

CodeReason Phrase台詞
100Continueええ、そのままで続けて。
101Switching Protocols解ったわ、言われた手順に変えてみるわね。
102Processing (WebDAV)その件なら処理中よ。

2xx Success 成功

CodeReason Phrase台詞
200OKいいわ。はい、これ。
201Createdうん、できたわ。これね。
202Acceptedひとまず、受け取っておくわ。
203Non-Authoritative Information不確かな噂なのだけれど……。
204No Contentバインダーに何も挟まっていないわ。
205Reset Content解ったわ。あ、次の人のために、ホワイトボードは消しておいてね。
206Partial Contentその範囲だと……これだけかしら。
207Multi-Status (WebDAV)いくつか頂いていた案件について、まとめておいたわ。
208Already Reported (WebDAV)その件は、もう報告済みだけれど……ほら、ここに、ね?
226IM Used前回から加筆修正された箇所? そうね、この辺りかしら。

3xx Redirection リダイレクション

CodeReason Phrase台詞
300Multiple Choicesどれかを選んでちょうだい。
301Moved Permanentlyそれなら、あちらの部屋に移したわ。
302Foundあ、あったわ。あちらを見てもらえるかしら。
303See Otherそれはここじゃなくて、他をあたって。紹介状は書かせてもらうわ。
304Not Modified前に伝えた通りよ。
305Use Proxyその件は委員会を通してちょうだい。
306(Unused)(今はまだ、なにも言えない。)
307Temporary Redirectあ、それは今だけ、そちらの棚に移してあるの。
308Permanent Redirectこの書類のままでいいから、あちらに提出しなおしてもらえる?

4xx Client Error クライアントエラー

CodeReason Phrase台詞
400Bad Request不正は許されないわ。
401Unauthorizedあなたに、どんな権限があるというの?
402Payment Requiredそれには、予算の計上が必要なの。
403Forbiddenそれは禁止されているでしょう?
404Not Found変ね、見つからないけれど?
405Method Not Allowedそんな方法は認められません。
406Not Acceptable書類に不備があるから、受領はできないわ。
407Proxy Authentication Requiredその件については、委員会の承認が必要よ。
408Request Timeout時間切れ、ね……。
409Conflictそうじゃないでしょう、矛盾しているわよ。
410Gone困ったわ、どうやら紛失してしまったみたい……。
411Length Requiredその書類、最初に何ページあるかを書いておいてね。
412Precondition Failedそもそもの前提が間違っているわ。
413Request Entity Too Largeごめんなさい、時間もないので、要点だけにしてもらえる?
414Request-URI Too Long枠からはみ出してしまっていて、よくわからないわ。
415Unsupported Media Typeあ……もうそのメディアでは受け付けられないのよ。
416Requested Range Not Satisfiableその期間分のデータは無いのよね……。
417Expectation Failedそ、それは生徒会の管轄外よ。
418I'm a teapot (RFC 2324)私、コーヒーはだめなのよ。紅茶にしてもらえるかしら。
422Unprocessable Entity (WebDAV)確かに書式はあっているのだけれど……この項目、おかしくないかしら?
423Locked (RFC 2324)そこの鍵は預かっていないのよ。
424Failed Dependency (RFC 2324)この前の議題が否決されてしまっているから……ごめんなさい、これも駄目ね。
426Upgrade Required申し訳ないのだけれど、新しい書式に則ってもらえるかしら。
428Precondition Required少し事情が変わってしまったの。悪いのだけれど、こんな感じに調整して再提出お願いできるかしら。
429Too Many Requests同じ人からの相談を続けて受けるというのは問題になってしまうの。日を改めてもらえるかしら。
431Request Header Fields Too Large序章の注釈が多すぎて読みづらくなってしまっているの。もう少し整理してもらえないかしら。

5xx Server Error サーバエラー

CodeReason Phrase台詞
500Internal Server Errorごめんなさい、こちらに落ち度があったようね……。
501Not Implementedその件は、まだ体制が整っていないのよ。
502Bad Gatewayその件だけれど、先方に断られてしまったわ。
503Service Unavailable今は少し手を離せないの、後でまたお願いできるかしら。
504Gateway Timeout出ないわね……ごめんなさい、担当の方がご不在みたい。
505HTTP Version Not Supportedその校則は草案はあるのだけれど、まだ適用されてはいないのよ。
506Variant Also Negotiatesえ、ええ……こちらのミスで、交渉先が判らなくなってしまっているようなの……。
507Insufficient Storage (WebDAV)ごめんなさい、どうやら保管場所が無くなってしまったようなの……。
508Loop Detected (WebDAV)堂々巡りで、結局何も決まらなかったわ……。
509Bandwidth Limit Exceededこの量は、一度に運びきれないわ。
510Not Extended今のままだと、そこまでは認められないわ。こんな感じでやり直してもらえるかしら。
511Network Authentication Required先に、こちらで委員会の登録手続きをすませてもらえる?

参考

HTTPステータスコード - Wikipedia

[Studying HTTP] HTTP Status Code

HTTP Status Codes

HTTPステータスコード | アリラボ(arison.jp)

先輩と覚える HTTP ステータスコード

*1:イメージは七草真由美@魔法科高校の劣等生(CV:花澤香菜さん)とか、望月真帆@大図書館の羊飼い(CV:中島沙樹さん)とか

*2:いやほんとに、いけないと思いながらも、ついつい手を抜いた実装にしてしまうんですよね……(サーバー・クライアント共に)