暦相互変換プログラム when.exe は、開発してもう10年近くになるMS-DOS コマンドライン型ツールです。開発しながら暦の勉強をしてきたため、そ のアーキテクチャはつぎはぎだらけで、改造や強化には向きません。また、 内部構造が汚くなっているため、UNIX や MacOS など他のプラットホーム に移植したり、ソースリストを公開して、そのアルゴリズムを社会に還元 することもできません。これまではプログラム自体に手をつけることなく、 when.exe を検索ツールとして tcl/tk や Web から使用するという方策で、 しのいできました。 約7年前にプログラムを C++ で全面的に作り直そうと考え、着手しました。 C++ を選択したのは、暦は「互いに似ているがちょっと異なる」というもの が多く、オブジェクト指向でアーキテクチャを整理するのに向いているから です。ところがデータを含めて約1万行近く作成したところで、自分が作っ ているのが、暦相互変換プログラムそのものではないことに気づいたのです。 「互いに似ているがちょっと異なる、今知られているものだけではなく、 今後見つかるものも簡単に作れる」というアーキテクチャにするためには、 C++ は力不足でした。そのため前提となる必要機能を作り込む必要があっ たのです。今なら Ruby の用語で、必要とした機能を示すことができます。
などです。このことに気づいた時点で、時期尚早ということで C++ での 開発を中断しました。そして、これらのほとんどすべてをカバーしている Ruby に出会ったことで、開発を再会する気になったのです。現時点でも Ruby ほどこれらの機能を備えている言語は他にありません。約3年前に 一度手をつけたときはスキルが伴わず「オブジェクト指向のエッセンスは 継承関係 - is a - だ」という思いこみがあって失敗しました。今回、 包含関係 - have a - を積極的に使ってようやく作業が軌道にのりました。
現在when.exe のRuby 化を行っています。既存ライブラリである Date ,newdate を参考にしつつ試作品(v0.41) を作ってみました。 今回の変更はLocation クラスの追加と、それにともなうインド暦 の正規対応です。Surya-Siddhanta も計算できます。アドヴァイス いただければ幸いです(旧バージョンの仕様案はこちら )。 このクラスライブラリへのコメントはこちらへ。
継承木を下記に示します。それぞれをクリックすると詳細説明が出ます。 まず Date::Extended , 次に Calendar を読んでください。 Object --+-Date -Date::Extended +-Eras | | | | +-Range ----Period +-Civil | | | | +-Outfit -+-Calendar ---+-DMD +-ILSB | | | | | | | +-Tibet | | | | | +-Residue +-EMD -----+-ChinaB -+-ChinaJ --ChinaS | | | : | | | | +-FMD0 +-ChinaT | | | | | +-Ephemeris +-FMD -----+-FMD1 ---+-Java | | | | | | +-FMD2 +-Maya | +-Timezone -----DST | | | +-Week -----Easter | | | | +-Location +-ThaiC ----ThaiB | | +-Location::Coordinate +-Jewish | +-Location::Cycle
Date#rjd
ユリウス通日(-4712-01-01T12:00:00Zからの経過時間(単位 日))を返します。 下位互換性保持のため、jd を sdn の意味に使うので、date2.rb にあわせて rjd(Real/Rational Julian Day)と命名しています。詳しくはDate#sdn をご 覧ください。
Date#jdn
rjd の小数点以下を四捨五入した整数( Julian Day Number または Julian Day Noon のつもり)を返します。
Date#jdf
rjd - jdn を返します。正であるとは限りません。
Date#sd
地方時通日(地方時-4712-01-01T12:00:00からの経過時間(単位 日))を返します。
Date#sdn
Date#jd
地方時正午通日(*)を返します。 * sd の小数点以下を丸めた整数 1日の始まりの時刻が暦法によって異なるため、丸めは必ずしも四捨五入で はありません。本メソッドは必ず正午に対応する sdn (Serial Day Number または Serial Day Noon のつもり - 将来はユリウス通日との関係がなくな る可能性があるので「地方時の」という含みを持つ、Local Day Number と はしない)を返します。 最初のDateクラスは時刻概念を持たなかったので、日付に対応するユリウス 通日(整数)をメソッドjdで返しました。これは非常に妥当性がある振る舞い であり、当時それ以外の選択肢があるはずもなかったのです。今回下位互換 性の保持のため、「絶対」時系-原則として文字列 jd を含む-と地方」時系 -原則として文字列 sd を含む-という命名の例外とします。
Date#sdf
sd - sdn を返します。正であるとは限りません。
Date#day_fraction
sdf + 1/2 を返します。標準添付のDate に対する互換性のために存在しています。
CCYY-MM-DD[T[hh:mm:ss.s[Z|+ZZ[:]zz]]] a b c d e 【要請】 (1) 通常用いられている西暦の場合、ISO8601 の拡張表現と同一となる。 (2) 1年が1月1日に始まらない暦法(例:会計年度)を表現できるようにする。 (3) 1日が午前0時に始まらない暦法(例:イスラム暦)を表現できるようにする。 (4) 中国の太陰太陽暦の日付を表現できるようにする。 (5) インドの太陰太陽暦の日付を表現できるようにする。 (6) CCYY が ' 0' 〜 '9999' の範囲に入る場合、同一の暦法/時差に対して 文字列による昇順ソートで、日付時刻を昇順ソートできる。 【区切り文字】 下表の通りとする。ただし日の区切りが午前0時の場合'-'のかわりに'T'を用いる。 +----------+-------+-------------------------------------+---------+------+ |区切り文字| a | b | c | e | | +--+----+--+------+------+------+------+------+--+------+ | | (code)| Y| | M| S1 | S2 | S3 | S4 |その他| D| | | +----------+--+----+--+------+------+------+------+------+--+------+------+ | ! (0x21)| | |-5| | | 黒分 | | | | | | | % (0x25)| | |-4| |閏黒分| | |重複月| | | | | & (0x26)| | |-3|閏白分|閏白分|閏白分| | | | | | | * (0x2A)|-1|前年|-2|閏黒分| |閏黒分| | |-1| 前日 | | | + (0x2B)| | |-1| | 黒分 | | | | | |東経側| | - (0x2D)| 0|当年| 0| 白分 | 白分 | 白分 | 白分 |通常月| 0| 当日 |西経側| | < (0x3C)| | | 1| 黒分 | | | 黒分 | | | | | | = (0x3D)| 1|翌年| 2| | | |閏白分| 閏月 | 1| 翌日 | | | > (0x3E)| | | 3| | | |閏黒分| | 2|翌々日| | +---+------+--+----+--+------+------+------+------+------+--+------+------+
【年】 一般形 = [年号, 検索番号, [年の上位階層, .. , [年, オフセット]]] 年号 : Eras クラスの場合に年を特定するために年号がつきます。 国名や対応する人名が定義されているときは'.'で区切っ てそれらを結合した文字列になります。 検索番号 : 同一のキーに一致する年号が複数ある場合、検索番号で 年号を特定します(例えば天保という年号は、日本、後梁 北斉の3ヶ国にあります)。 年の上位階層 : 年の上位階層がある場合につきます。マヤ暦のカトゥン・ バクトゥン、あるいはギリシャのオリンピアード番号など。 年 : 常につきます。単なる整数であり、普通の暦では、これだ けで年を指定できます。 オフセット : 運用と暦法で年の区切りが異なる場合につきます。これが 現れるもっとも日常的な例は満年齢です。暦法はグレゴリ オ暦でも満年齢は誕生日が年の区切りです(オフセット値 は上表 Y 欄の通りです)。 ['A', [42, 1]] は 'A' さんが 正月を過ぎているが誕生日前で満42歳であることを示しま す。誕生日を過ぎると ['A', [43, 0]] になります。 【月】 一般形 = [月番号, 詳細番号] ([]は配列, 詳細番号なしならただの整数) 月番号 : 閏と白分・黒を無視した月の通し番号です。 普通の暦では、これだけで月を指定できます。 詳細番号 : 対応する閏と白分・黒分情報を示します(値は上表 M 欄の 通りです。 【日】 一般形 = [日番号, オフセット] ([]は配列, オフセットなしならただの整数) 日番号 : 日の区切り、余日・欠日を無視した日の通し番号です。 普通の暦では、これだけで日を指定できます。 オフセット : 運用と暦法で日の区切りが異なる場合、および余日のある 暦法の場合につきます。イスラーム暦やインド暦が典型的 です(値は上表 D 欄の通りです)。 【時】【分】【秒】【秒の小数】 一般形 = 通し番号 通し番号 : 時分秒の通し番号を意味する整数(Integer )と秒の小数を 意味する分数(Rational )です。 夏時間から冬時間への変更時や閏秒の挿入時に、 [重複する時または秒, 1]という配列にするかもしれませ んが、今後の検討課題でまだ実装していません。
今後の作業
データの移行 暦法名を整理する(頭文字によるマッチング・ルール)。 事前実装予定の暦法を Region ディレクトリに定義する。 Japan, China, East_Asia, South_East_Asia, India, ..., Maya エラー処理を整理する。 年と日の指定に剰余類を許すようにする。 Marshal に対応する。 NIPE を拡張ライブラリ化する。
問題点
Eras 読み込み時に毎回、年号開始日の日付をユリウス通日化するのは冗長。 Marshal などを使って、効率化すべき。 Marshal Calendar が Proc オブジェクトを保持している(_m_in_y, d_in_mの計算のため) ので、Marshal できない。見直す必要がある。 ThaiC 『中国天文学史文集(第3集)』のデータでうまく計算が整合しないので、確認 する必要がある。 Tibet 『蔵暦的原理与実践』のアルゴリズムが、まだ今回の date.rb のアーキテク チャに変換されていないため、余日の表記が他の暦と整合していない。要調整。 DST 夏時間から冬時間への切替時の、1時間の重複が処理されていない。閏時など も含めて、対策を考える必要がある。