ナビゲーション リンクのスキップホーム > そのまま使えるシリーズ > そのまま使える暦(KOYOMI)

そのまま使える暦(KOYOMI)

このページは、2019年4月1日現在の情報に基づいて掲載しております。
情報の信頼性は、法令の改正や温暖化の進行具合、摂取したコラーゲンの量、ムカデがどの足から歩き出したか等の様々な事象の影響を受け、日々刻々と常に変化しております。

今日

西暦: 2024年4月19日(金)

和暦: 令和6年4月19日(金)(旧暦令和6年3月11日)先勝

干支: 甲辰己巳癸丑(旧暦甲辰戊辰癸丑)

指定した日

日付を指定すると、即時に旧暦と六曜を表示します。
このページでは、System.Globalization.JapaneseLunisolarCalendarクラスを使用して旧暦を算出し、算出した旧暦に基づいて六曜を特定しております。
指定可能範囲は、1960/1/28~2050/1/22です。

西暦: 2024年4月19日(金)

和暦: 令和6年4月19日(金)(旧暦令和6年3月11日)先勝

干支: 甲辰己巳癸丑(旧暦甲辰戊辰癸丑)

2033年問題

巷では、西暦2033年秋から2034年春にかけて、旧暦が決定できなくなる問題が起きているとのこと。
そもそも旧暦とは太陰太陽暦の一種で、日本が独自に制定した「天保暦」を指すようです。

太陰太陽暦とは、月の満ち欠けを1サイクルとした月に対して立春や夏至などの二十四節気を用いて何月にするかを決めるという暦法ですが、中国の太陰太陽暦と日本の天保暦とでは二十四節気の配置の仕方が異なり、この配置の仕方によって日本の天保暦だけが2033年問題に直面するということのようです。

この旧暦が決まらないと何が困るかといえば、「大安」や「仏滅」などの六曜が決まらないことになり、主に冠婚葬祭業界が大きな影響を受けることになります。

ちなみに六曜は、次の計算式によって決まります。
(閏月は、その前の月と同じです。)

(旧暦の月 + 旧暦の日) % 6
0 ... 大安
1 ... 赤口
2 ... 先勝
3 ... 友引
4 ... 先負
5 ... 仏滅
※"%"は剰余を求める演算子。
なかなか、どうして、プログラミング心がくすぐられるではありませんか!

アメリカ人は国際化対応に熱心

話は変わりますが、Windowsに限らず、Linux、MacOSなど、アメリカ発のOSは、必ず国際化対応がなされております。
たとえばWindowsであれば、コントロールパネルの「日付と時刻」からカレンダーの設定を変更することで、ファイルのタイムスタンプなどの日付を和暦で表示することが可能です。

.NETにも、西暦を簡単に和暦に変換できるSystem.Globalization.JapaneseCalendarクラスが用意されております。
もしかして旧暦もあるのではとおもむろにWeb検索をしてみると、これがあるんですね。
System.Globalization.JapaneseLunisolarCalendarクラスとして用意されております。

JapaneseLunisolarCalendarクラスがサポートしている範囲は、1960年1月28日(旧暦昭和35年1月1日)から2050年1月22日(旧暦令和31年12月29日)まで。
つまり、2033年の旧暦を算出できるんです。

実際に算出してみると

2033年問題は、2033年8月25日から2034年3月19日までの月名が決まらない問題ですが、早速算出してみることに。

  • 2033年8月25日~9月22日 → 8月

  • 2033年9月23日~10月22日 → 9月

  • 2033年10月23日~11月21日 → 10月

  • 2033年11月22日~12月21日 → 11月

  • 2033年12月22日~2034年1月19日 → 閏11月

  • 2034年1月20日~2月18日 → 12月

  • 2034年2月19日~3月19日 → 1月

といった具合に、いとも簡単に算出してくれました。
しかし、なぜMicrosoft®では解決できているのでしょうか。
この答えは、冒頭で触れた二十四節気の配置の仕方にありそうです。

JapaneseLunisolarCalendarクラスは、EastAsianLunisolarCalendarクラスから派生したクラスですが、同じように派生した兄弟クラスにChineseLunisolarCalendarクラスやKoreanLunisolarCalendarクラスなどがあります。
二十四節気の配置法として、天保暦の定気法に対し、中国や韓国では平気法を採っており、派生元のEastAsianLunisolarCalendarクラスが平気法であるから、JapaneseLunisolarCalendarクラスも平気法になっているということなのではないかと仮説を立ててみました。
定気法では2033年問題が起こりますが、平気法では起こらないから、いとも簡単に算出できたということなんじゃないかと。

この仮説に基づいて、定気法と平気法とで異なる月名のついた直近の年で比べてみたのですが、JapaneseLunisolarCalendarクラスとChineseLunisolarCalendarクラスとで本来違うはず(?)の月名が、同じ月名になってしまいました。
2004年と2006年の閏月が、定気法と平気法で異なるはずなのですが、いずれのクラスも定気法による月名が出てきてしまいました。
むむむ・・・これもMicrosoft®マジックなのか。。
以上の結果から、JapaneseLunisolarCalendarクラスは、ちゃんと定気法に則っている一方で、2033年だけMicrosoft®が何らかの方法によって算出できるようにした、そしてその方法は平気法と同じ算出結果になる、というところまでわかりました。

ところで、もしここで、日本の有識者がJapaneseLunisolarCalendarクラスとは異なった月名を当てはめたとしたら・・・。
もしかしたらそのときが、本当の2033年問題の到来なのかも知れませんね。

(ちなみに、平気法によると2004年1月22日は前年の閏12月1日になるらしいのですが、この年の春節は1月22日から始まっているんですよね。ちゃんとした文献を漁ってみたくなってきました。。)

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更新版のリリースを待たねばなりませんが。

本サイトは、下記のサイトポリシーに同意いただいたうえでご利用ください。

サイトポリシー | お問い合わせ | 運営会社(株式会社GAIDE)

Copyright © 2016 GAIDE CO., LTD. All rights reserved.