JapaneseLunisolarCalendarクラスの問題
日本の元号は、年の途中で変わりますが、JapaneseCalendarクラスはこの点をしっかりとクリアしているのに対し、JapaneseLunisolarCalendarクラスでは中途半端な実装になっております。
具体的には、1989年2月6日から1989年2月12日の間、「平成0年」を返すメソッドがあり、この戻り値を使用する別のメソッドで例外が発生します。
この問題を解決するために、次のような構造体を作ってみました。
さらに、1989年1月8日から1989年2月5日の旧暦に和暦の年を「平成零年」としなければ1989年2月6日が「平成元年」にならないことから、1989年1月8日から1989年2月5日の年を「0」とする異例の対応を実装しております。
struct JapaneseOld
{
public readonly int Era;
public readonly int Year;
public readonly int Month;
public readonly int Day;
public readonly bool LeapMonth;
public readonly int DaysInYear;
public JapaneseOld(DateTime _dateTime)
{
var __jpnOldCalendar = new System.Globalization.JapaneseLunisolarCalendar();
var __month = __jpnOldCalendar.GetMonth(_dateTime);
this.Day = __jpnOldCalendar.GetDayOfMonth(_dateTime);
var __era = __jpnOldCalendar.GetEra(_dateTime);
var __year = __jpnOldCalendar.GetYear(_dateTime);
if (__year <= 0)
{
__era = __jpnOldCalendar.GetEra(_dateTime.AddYears(-1));
__year = __jpnOldCalendar.GetYear(_dateTime.AddYears(-1)) + 1;
}
var __jpnOldLeapMonth = __jpnOldCalendar.GetLeapMonth(__year, __era);
if (__jpnOldLeapMonth > 0 && __month >= __jpnOldLeapMonth)
{
if (__month == __jpnOldLeapMonth)
this.LeapMonth = true;
else
this.LeapMonth = false;
__month--;
}
else
this.LeapMonth = false;
this.DaysInYear = __jpnOldCalendar.GetDaysInYear(__year, __era);
var __jpnCalender = new System.Globalization.JapaneseCalendar();
if (__era != __jpnCalender.GetEra(_dateTime))
{
__era = __jpnCalender.GetEra(_dateTime);
__year = 1;
if (__year == __jpnOldCalendar.GetYear(_dateTime.AddDays(this.DaysInYear)))
__year = 0;
}
this.Era = __era;
this.Year = __year;
this.Month = __month;
}
}
ついでに干支も出してみる
せっかくなので、干支の計算式も作ってみました。
ここでいう干支とは、十干十二支や六十干支、天干地支などとも呼ばれる「かんし」のことです。
漢字で書くと、「えと」と区別がつかないのが難点ですが、そもそも「えと」という読み方も十干十二支を表すものであったようであり、日本では単に十二支だけを指して「えと」と言い表す文化が根付いたということのようです。
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 |
壬子 |
癸丑 |
甲寅 |
乙卯 |
丙辰 |
丁巳 |
戊午 |
己未 |
庚申 |
辛酉 |
壬戌 |
癸亥 |
では早速、コードを書いてみましょう。
static string JpnJikkan = "甲乙丙丁戊己庚辛壬癸";
static string JpnJunisi = "子丑寅卯辰巳午未申酉戌亥";
const int KinoeneYear = 1924;
static DateTime KinoeneDay = new DateTime(1959, 12, 8);
var __now = DateTime.Now;
var KansiString = string.Format("{0}{1}{2}{3}{4}{5}",
JpnJikkan[(__now.Year - KinoeneYear) % 10],
JpnJunisi[(__now.Year - KinoeneYear) % 12],
JpnJikkan[(__now.Year * 12 + __now.Month + 3) % 10],
JpnJunisi[(__now.Month + 1) % 12],
JpnJikkan[(__now - KinoeneDay).Days % 10],
JpnJunisi[(__now - KinoeneDay).Days % 12]);
DateTime.Nowを用いて現在日時を取得するときは、1回だけ取得して変数の中に入れるようにしましょう。
今回は日付だけの処理ですが、コードの中で何度もDateTime.Nowを取得すると、日付を跨いで処理した場合に意図せぬ事態に直面することになります。
年の干支
年の干支とは、大昔のある年を「甲子」として、以後60年周期で繰り返すというものです。
このツールでは、1960年の直前に「甲子」であった年(1924年)からの経過年数を、十干は10で割った剰余によって、十二支は12で割った剰余によって、それぞれ割り当てています。
ちなみに、阪神甲子園球場は1924年に竣工しました。
月の干支
月の十二支は月名ごとに決まっていて、そこに十干を加えた月の干支は5年周期で繰り返すというものです。
(閏月は、その前の月と同じです。)
ここで注目すべきポイントは、「甲子」となる月が「1月」ではない点です。
|
1月 |
2月 |
3月 |
4月 |
5月 |
6月 |
7月 |
8月 |
9月 |
10月 |
11月 |
12月 |
2013年 |
甲寅 |
乙卯 |
丙辰 |
丁巳 |
戊午 |
己未 |
庚申 |
辛酉 |
壬戌 |
癸亥 |
甲子 |
乙丑 |
2014年 |
丙寅 |
丁卯 |
戊辰 |
己巳 |
庚午 |
辛未 |
壬申 |
癸酉 |
甲戌 |
乙亥 |
丙子 |
丁丑 |
2015年 |
戊寅 |
己卯 |
庚辰 |
辛巳 |
壬午 |
癸未 |
甲申 |
乙酉 |
丙戌 |
丁亥 |
戊子 |
己丑 |
2016年 |
庚寅 |
辛卯 |
壬辰 |
癸巳 |
甲午 |
乙未 |
丙申 |
丁酉 |
戊戌 |
己亥 |
庚子 |
辛丑 |
2017年 |
壬寅 |
癸卯 |
甲辰 |
乙巳 |
丙午 |
丁未 |
戊申 |
己酉 |
庚戌 |
辛亥 |
壬子 |
癸丑 |
このツールでは、この微妙なズレをシフトさせて算出しています。
十干は、西暦年に12をかけて月を足し、さらに3を足した数を10で割った剰余により割り当てています。
十二支は、月に1を足した数を12で割った剰余により割り当てています。
日の干支
日の干支とは、大昔のある日を「甲子」として、以後60日周期で繰り返すというものです。
つまり、暦法に関係なく、「今日この日」は同じ干支になります。
このツールでは、1960年1月28日の直前に「甲子」であった日(1959年12月8日)からの経過日数を、十干は10で割った剰余によって、十二支は12で割った剰余によって、それぞれ割り当てています。
元号「令和」への対応
上述の構造体JapaneseOldにて、readOnly変数Eraの値を下記の配列に当てはめることで、元号を文字で取得することができます。
static string[] JpnEra = new string[] { string.Empty, "明治", "大正", "昭和", "平成" };
平成の次の元号が「令和」に決まったことを受けて、次のように「令和」を追記することで、元号「令和」への対応が完了いたします。
static string[] JpnEra = new string[] { string.Empty, "明治", "大正", "昭和", "平成", "令和" };
ただし、実際に「令和」が表示されるようになるためには、Microsoft
®さんによる.NET Framework更新版のリリースを待たねばなりませんが。