Yet Another Date Class 仕様案
=== Calendar Converter when.exe の Ruby 化のための検討 ===
2002-01-31 Rev 0.2 須賀 隆
Calendar Converter when.exe の Ruby 化を行うに当たり、仕様の設計を
しています。既存のライブラリである date2, newdate を参照しつつ、以下
のようなたたき台を作ってみました。アドヴァイスいただければ幸いです。
日付クラス
(Yet Another) Date クラス
Date2 クラス
日付範囲クラス
Period クラス
暦法クラス
Calendar クラス - 暦法クラスの基底クラス
Civil クラス - 西暦
ISO8601Week クラス - 暦週
中国剰余クラス
Residueクラス
日付時刻の基本表現
ISO8601, X0301拡張
既存の Date との主な相違点
0. 主にアルゴリズムの記述を残すことを主眼とし、スピードにはこだわらない。
負のユリウス日に対しても正しい結果が返るようにする。
1. 暦法の拡張を容易にするため暦法クラスを新規に設けた。
1.0 暦法クラスを分離する理由
- 暦法を拡張すると、相互変換メソッドが状態を持つようになるため、
クラスメソッドでは対応できない。Date オブジェクトのインスタンス
メソッドにした場合、自分自身以外の日付に対応するデータを扱うのは
不自然である。
(実際、以前 Date オブジェクト自身で暦法を扱って破綻した経験あり)
1.1 Calendar クラスを基底クラスとする。
1.2 新規暦法を定義するときは、
- 日付とユリウス日の相互変換を行うメソッド
- 年、月の長さを返すメソッド
- 日付を文字列で返すメソッド
- その暦法固有の暦注を扱うメソッド
を定義すればよい。
1.3 ISO8601の暦週は暦法の一種として取扱い、グレゴリオ
暦のみに対応することとした。
2. 暦法クラスを Date クラスから分離したことによるインタフェースへの影響
は以下の通り。
2.1 exist?メソッド群が Date クラスのクラスメソッドから、暦法クラス
のインスタンスメソッドに変わった。
2.2 exist2? は置き場所がなくなった。
2.3 月名,曜日名も Date クラスの定数から暦法クラスの定数(将来はイン
スタンスメソッド?)に移動している。
2.4 インタフェースに互換性がなくなるので、メソッド名称を無理に一致
させることはしなかった。この方針にそうなら exist は dtoj, ord
または ordinal などの名称が考えられる?
3. ISO8601の「継続期間」をサポートしている。
Periodクラスを設けた。
このクラスは中国剰余やニュートン法を支援するものとして、日付に限定
しないでもう少し一般化できるかもしれない。
====================================================================
【(Yet Another) Date クラス】
スーパークラス
Object
クラスメソッド
year (year, cal)
month (year, mon, cal)
day (year, mon, day, cal)
hour (year, mon, day, hour, cal)
minute(year, mon, day, hour, min, cal)
second(year, mon, day, hour, min, sec, cal)
new (year, mon, day, hour, min, sec, secf, cal)
parsed(string, format, cal)
today ( cal)
now ( cal)
これらはすべて Date クラスのインスタンスを作るメソッドである。
引数:
year 年を指定する。年以上の階層を持つ暦法の場合、配列になる
ことがある(デフォルトは今年)。
mon 月を指定する(デフォルトは指定の年の開始日付の月 通常 1)。
day 日を指定する(デフォルトは指定の年の開始日付の日 通常 1)。
hour 時を指定する(デフォルトは指定の日の開始時刻の時 通常 0)。
min 分を指定する(デフォルトは指定の年の開始時刻の分 通常 0)。
sec 秒を指定する(デフォルトは指定の年の開始時刻の秒 通常 0)。
secf 秒の端数を指定する(デフォルトは指定の年の開始時刻の秒の小数部
通常 0)。
string format が nil の時は、ISO8601準拠の日付文字列。
format が nil 以外の時は、その format に従った日付文字列。
(デフォルトは '-4712-01-01T00:00:00Z')
format string で指定される日付文字列の書式(デフェルトは nil)。
cal 暦法を指定する(デフォルトは 2299161(1582-10-15のユリウス日))。
false の時は、ユリウス暦を意味する。
true の時は、グレゴリオ暦を意味する。
数値の時は、その整数を改暦日とみなし、前日までユリウス暦、
当日以降グレゴリオ暦が使われたものとする。
暦法オブジェクトの時は、その指定する暦法に従う。
文字列の時は、その文字列から対応する暦法を検索し、その指定
する暦法に従う。
インスタンス変数
@cal 暦法オブジェクト
@jd 範囲の始点の'-4712-01-01T00:00:00Z'からの経過日数/日
@strZone 時差文字列
@zone 時差/日
(必要になったときに計算される, once を使えば、原理
的にはいらないが、非常に基本的な情報なので独立した
変数とする。@zone から @strZone は復元できないので、
to_sで使うために @strZone も残しておく)
その他 once で作られるインスタンス変数が存在する。
インスタンスメソッド
cal @cal を返す。
jd @jd を返す。
strZone @strZone を返す。
zone @zone を返す。
newCal(cal) self を複製して、@cal を設定し直す。
(=newsg(cal)) (cal は new の引数と同様)
newZone(zone) self を複製して、@strZone を設定し直す。
(zone は数値を指定してもよい)
newPeriod(string)
self を複製して、self を端点とする Period オブジェ
クトを作って返す。
(string は継続期間を表すISO8601準拠の日付文字列)
self + x x が数値の場合、x 日後の日付オブジェクトを返す。
x が中国剰余オブジェクトの場合、当日または以降の日付で
最初に条件を満たす日付を返す。
x が文字列の時は、その文字列から対応する中国剰余オブ
ジェクトを検索し、その指定に従う。
self - x x が数値の場合、n 日前の日付オブジェクトを返す。
x が中国剰余オブジェクトの場合、以前の日付で最も
最後に条件を満たす日付を返す。
x が文字列の時は、その文字列から対応する中国剰余オブ
ジェクトを検索し、その指定に従う。
x が日付オブジェクトの場合、self と x の間の日数を返す。
self << n 日のひとつ上の単位を単位として、n 単位前の日付オブジェ
クトを返す。
self >> n 日のひとつ上の単位を単位として、n 単位後の日付オブジェ
クトを返す。
self <=> other 両者を比較して、-1, 0, 1を返す。
self === other 同じ日なら真を返す。
next または succ UTCで1日後の日付オブジェクトを返す。
jdn ユリウス日の整数部を返す。
jdf ユリウス日の小数部を返す。
year 年を返す。
month 月を返す。
mday または day 月内の日を返す。
hour 時を返す。
min 分を返す。
sec 秒の整数部を返す。
secf 秒の小数部を返す。
yday 年初からの通日を返す。
wday 曜日を返す(0-6, 日曜日が 0)。
to_s 日時に対応するISO8601形式文字列を返す。
《日付時刻の基本表現》を参照のこと。
is?(key, value) key で指定した暦注が value である時、真 を返す。
(value のデフォルトは true)
today(key) key で指定した暦注を返す。
key が nil(デフォルト)の時は、すべての暦注を保持する
連想配列を返す。
mjd, tjd, tjd2, ld, 祝際日, 復活祭, 第NK曜日,閏年などは
暦注と位置づける。
step(limit, s){} self から limit まで s 刻みでブロックの評価を繰り返す。
downto(limit){} (=step(limit, -1){})
upto(limit){} (=step(limit, 1){})
====================================================================
【Date2 クラス】
スーパークラス
Date
クラスメソッド
.....
詳細未定、1日の長さがUTCの1日と異なるような暦法を処理するとき、
@cal の暦法クラス指定だけでは、振る舞いを定義できない可能性大
====================================================================
【Period クラス】
スーパークラス
Range
クラスメソッド
new(range)
Period クラスのインスタンスを作るメソッドである。
引数:
range 日付オブジェクトを端点とする Range オブジェクト。
parsed(string, format, cal)
Period クラスのインスタンスを作るメソッドである。
引数:
string format が nil の時は、ISO8601準拠の日付文字列。
format が nil 以外の時は、その format に従った日付文字列。
(デフォルトは '-4712-01-01T00:00:00Z/P1D')
format string で指定される日付文字列の書式(デフェルトは nil)。
cal 暦法を指定する(デフォルトは 2299161(1582-10-15のユリウス日))。
false の時は、ユリウス暦を意味する。
true の時は、グレゴリオ暦を意味する。
数値の時は、その整数を改暦日とみなし、前日までユリウス暦、
当日以降グレゴリオ暦が使われたものとする。
暦法オブジェクトの時は、その指定する暦法に従う。
文字列の時は、その文字列から対応する暦法を検索し、その指定
する暦法に従う。
なお DateクラスのnewPeriodメソッドでも Period オブジェクトを生成できる。
インスタンス変数
@range 継続期間を指定する文字列
その他 once で作られるインスタンス変数が存在する。
インスタンスメソッド
find_all(c) c が中国剰余オブジェクトの時は、範囲内で条件を満たす
すべての日付の配列を返す。
c が文字列の時は、その文字列から対応する中国剰余オブ
ジェクトまたは暦注 key を検索し、その指定に従う。
(Date.year(2002).newRange('P1Y').find_all('土用&丑'))
c がブロックの時は、Range と同様の動作をする。
select(c) find_all(c)と同じ
find(c) c が中国剰余オブジェクトの時は、範囲内で条件を満たす
最初の日付を返す。
c が文字列の時は、その文字列から対応する中国剰余オブ
ジェクトまたは暦注 key を検索し、その指定に従う。
c がブロックの時は、Range と同様の動作をする。
これらのメソッドをオーバーライドしたのは毎日の評価
を省略できる高速検索が可能な場合があるためである。
そのアルゴリズムを記述しておく。
なお、暦注を扱うメソッドが Period クラスのオブジェクトを返すことがある。
====================================================================
【Calendar クラス】(暦法クラスの基底クラス)
スーパークラス
Object
クラス定数
DAYNAMES
ABBR_DAYNAMES
これらは、引数の渡し方の見直しにともないインスタンスメソッドに変更する
可能性大。
クラスメソッド
new
Calendar クラスのインスタンスを作るメソッドである。
インスタンス変数
func で作られるインスタンス変数が存在する。
インスタンスメソッド
exist?(jd,..) ユリウス日を返す。 # 別案 ord, dtoj
date(jd) ユリウス日,1,1を返す。# 別案 day, jtod
minY(y) 常に 1 を返す。
dinY(y) 常に 1 を返す。
dinM(y,m) 常に 1 を返す。
sprint(date) Dateオブジェクトdateの日時に対応する文字列を返す。
....
その他ユリウス日によって確定する暦注を扱うメソッドをここで定義する。
====================================================================
【Civil クラス】
スーパークラス
Calendar
クラス定数
MONTHNAMES
ABBR_MONTHNAMES
ITALY
ENGLAND
JULIAN
GREGORIAN
これらは、引数の渡し方の見直しにともないインスタンスメソッドに変更する
可能性大。
クラスメソッド
new(cal)
Civil クラスのインスタンスを作るメソッドである。
引数:
cal 暦法を指定する(デフォルトは 2299161(1582-10-15のユリウス日))。
false の時は、ユリウス暦を意味する。
true の時は、グレゴリオ暦を意味する。
数値の時は、その整数を改暦日とみなし、前日までユリウス暦、
当日以降グレゴリオ暦が使われたものとする。
一般の暦法クラスにおいては、cal は連想配列となり暦法の詳細を
定義する。
インスタンス変数
@sg 改暦日。グレゴリオ暦の使用を開始した日のユリウス日。
その他 func で作られるインスタンス変数が存在する。
インスタンスメソッド
sg @sg を返す。
newsg(cal) self を複製して、@sg を設定し直す。
exist?(y,m,mday) 正しい日付であれば、相当するユリウス日を返す。
そうでないなら、偽を返す。 # 別案 ord, dtoj
date(jd) ユリウス日に相当する日付 y, m, mday を返す。
# 別案 day, jtod
minY(y) 当年について 年の月数 を返す。
y の省略値は nil である。このとき、もし暦法が
閏月を持たず年が常に一定の月を含む場合、その
月数を返す。暦法が閏月を持てば 偽 を返す。
dinY(y) 当年について 年の日数 を返す。
y の省略値は nil である。このとき、もし暦法で
年の日数が常に一定の場合、その日数を返す。年の
日数が変化するならば 偽 を返す。
dinM(y,m) 当月について 月の日数 を返す。
y, m の省略値は nil である。このとき、もし暦法
で月の日数が常に一定の場合、その日数を返す。月
の日数が変化するならば 偽 を返す。
minLY(y) 前年について 年の月数 を返す。
dinLY(y) 前年について 年の日数 を返す。
dinLM(y,m) 前月について 月の日数 を返す。
sprint(date) Dateオブジェクトdateの日時に対応するISO8601形式
文字列を返す。
....
その他暦注の扱いなどについては、要検討。Date の動作のために、
下請けとなるメソッドが多数必要のはず。
====================================================================
【ISO8601Week クラス】
スーパークラス
未定
クラスメソッド
new
ISO8601Week クラスのインスタンスを作るメソッドである。
引数はなく常にグレゴリオ暦を扱う。
(週の最初の曜日と、年の第1週が必ず含む日の指定ができる
ようにして、デフォルトを ISO8601 とする?)
インスタンス変数
func で作られるインスタンス変数が存在する。
インスタンスメソッド
exist?(y,w,wday) 正しい日付であれば、相当するユリウス日を返す。
そうでないなら、偽を返す。 # 別案 ord, dtoj
date(jd) ユリウス日に相当する日付 y, w, wday を返す。
# 別案 day, jtod
minY(y) 当年について 年の週数 を返す。
y の省略値は nil である。このとき、偽を返す。
dinY(y) 当年について 年の日数 を返す。
y の省略値は nil である。このとき、偽を返す。
dinM(y,m) 常に 7 を返す。
sprint(date) Dateオブジェクトdateの日時に対応するISO8601形式
文字列を返す。
....
====================================================================
【Residue クラス】中国剰余クラス
クラスメソッド
new(mod, div)
中国剰余クラスのインスタンスを作るメソッドである。
引数:
mod 剰余の配列。数値の場合には1要素の配列に変換される。
div 除数。
インスタンス変数
@mod 剰余の配列
@div 除数
インスタンスメソッド
mod @mod を返す。
div @div を返す。
self + n @div で割った剰余が @mod の要素のいずれかに一致
する数の内、n 以上で最も小さい数を返す。
(Date クラスも参照のこと)
self & x 共通集合を表現する中国剰余オブジェクトを返す。
self | x 和集合を表現する中国剰余オブジェクトを返す。
====================================================================
《日付時刻の基本表現》
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' の範囲に入る場合、同一の暦法/時差に対して
文字列による昇順ソートで、日付時刻を昇順ソートできる。
【区切り文字案】
下表の通りとする。
+----------+--------+---------------------------+---------+--------+
|区切り文字| | b | | |
| | a +------+------+------+------+ c | e |
| (code)| | P1 | P2 | P3 |その他| (時刻付)| |
+----------+--------+------+------+------*------+---------+--------+
| ! (0x21)| | | 黒分 | | | | |
| % (0x25)| |閏黒分| | | | | |
| & (0x26)| 前々年 |閏白分|閏白分|閏白分| |前々日(R)| |
| * (0x2A)| 前年 | |閏黒分|閏黒分| | 前日 (S)| |
| + (0x2B)| | 黒分 | | | | | 東経側 |
| - (0x2D)| 当年 | 白分 | 白分 | 白分 |通常月| 当日 (T)| 西経側 |
| < (0x3C)| | | | 黒分 | | | |
| = (0x3D)| 翌年 | | | | 閏月 | 翌日 (U)| |
| > (0x3E)| 翌々年 | | | | |翌々日(V)| |
+---+------+--------+------+------+------+------+---------+--------+
【補足】
すべてに対して:
・'-'(0x2D) は ','(0x2C)または'.'(0x2E)で代用して良い
(JIS X 0301拡張を許容する)。
a:
・1年が1月1日より前に始まる場合、
'*' 年初から1月1日より前の日付につける。
'-' 1月1日以後の日付につける。
・1年が1月1日より後に始まる場合、
'-' 年初から1月1日より前の日付につける。
'=' 1月1日以後の日付につける。
・紀年が満年数の場合、区切り文字をずらせて用いる。
b:
・インドの太陰太陽暦の場合、
満月終わり(P1,P2)/晦終わり(P3)に応じて、上表の通りとする。
・中国の太陰太陽暦などその他の暦の場合、
上表 b の「その他」の通りとする。
c:
・1日が午前0時より前に始まる場合、
'*', 'S' 日初から午前0時より前の時刻につける。
'-', 'T' 午前0時以後の時刻につける。
・1日が午前0時より後に始まる場合、
'-', 'T' 日初から午前0時より前の時刻につける。
'=' ,'U' 午前0時以後の時刻につける。
・当該日が余日の場合、区切り文字をずらせて用いる。
d:
a,c の発想の延長として、夏時間の終わりに同じ時刻が2回発生することの
対策として、':'(0x3A) のかわりに '='(0x3D)を用いることが考えられる
が、保留する。