読者です 読者をやめる 読者になる 読者になる

酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

C#を使って現在日時から田村ゆかりさんが生まれて何日経ったかをユリウス通日(ユリウス日)を計算して調べる

今日は田村ゆかりさんのお誕生日らしいです。お誕生日おめでとうございます。

TLを見る限り今年で17歳の誕生日ということなのですが、「日本国における年齢に関する法律」は僕には解釈が難しくここでの記載は避けますので、(周囲の目など含めて)正しく計算できる方が田村ゆかりさんの年齢を計算しましょう。

さて、本記事では特定の日付(例えば誕生日など)から現在に至るまで何日経過したかを計算する方法を書きたいと思います。

DateTimeクラスを使って2つの日付から経過日数を調べる

DateTimeクラスを使えば2つの特定日の差を簡単に求めることができます。

// 田村ゆかりさんの誕生日
var yukariBirthday = new DateTime(1976, 2, 27);

// 現在の年月日
var nowDate = new DateTime(2015, 2, 27);

// 経過日数を調べる
var totalDays = nowDate.Subtract(yukariBirthday).TotalDays;

C#で経過日数を調べる際には、大抵の場合は上記のコードで事足りると思います。

1582年10月15日以前から現在までの経過日を計算すると誤差が発生する

ただ前述のコードを使って、田村ゆかりさんのお誕生日ではなくて、織田信長が本能寺の変で横死してから今日まで何日経過したのかを調べるには誤りを含むこととなります。

本能寺の変が発生したのは、天正10年6月2日(1582年6月21日)です。

本能寺の変から今日(2015年2月27日)までの経過日を調べると158,036日経っていることになりますが、実際には158,026日となります。これはDateTimeクラスがデフォルトでグレグリオ暦を使用しているために発生する誤りとなります。

1582年10月15日以前の日付からの経過日数を正しく計算する場合には、年月日を正しく解釈するためのCalendarに適切なものを指定する必要があります。

// 本能寺の変が発生した日(ユリウス暦として解釈する)
var calendar1 = new System.Globalization.JulianCalendar();
var honnoujiDate = new DateTime(1582, 6, 21, calendar1);

// 現在の年月日(グレグリオ暦として解釈する)
var calendar2 = new System.Globalization.GregorianCalendar();
var nowDate = new DateTime(2015, 2, 27, calendar2);

// 経過日数を調べる
var totalDays = nowDate.Subtract(honnoujiDate).TotalDays;

このCalendarを指定した上記のコードですと、本能寺の変から今日までの正しい経過日数が得られると思います。

DateTimeクラスでは紀元前以前の日付を扱えない

さらにDateTimeクラスでは、紀元元年(1年)の前年の紀元前1年(-1年)を表現することができません。下記のコードを実行すると例外が発生します。

var kigenzenDate = new DateTime(-1, 1, 1);

紀元前からの経過日数を求めたい場合には、ユリウス通日(ユリウス日とも呼ばれる)を利用します。ユリウス通日は、ユリウス暦を-4712年 (紀元前4713年) 1月1日まで遡って適用して、そこから数えた経過日数です。

グレグリオ暦とユリウス暦については以前「日本の元号(Japanese era name)の一覧 - 酢ろぐ!」で触れたことがありますのでご覧ください。

グレグリオ暦の年月日からユリウス通日を計算する

グレグリオ暦に改暦された1582年10月15日正子(0時)以降の日付からユリウス通日を計算する方法です。

public double ToJulianDayFromGregorianCalendar(
    int year, int month, int day, int hour, int minute)
{
    // 1月、2月は前年の13月、14月として計算する。
    if (month < 3)
    {
        year -= 1;
        month += 12;
    }

    var h = hour / 24d;
    var min = minute / 1440d;
    
    return Math.Floor(365.25d * year)
          + Math.Floor((double)year / 400)
          - Math.Floor((double)year / 100)
          + DifferenceValues[month]
          + day
          + 1721088.5d // (678912 + 2400000.5)
          + Math.Round(h + min, 5);
}

ユリウス暦の年月日からユリウス通日を計算する

グレグリオ暦に改暦される1582年10月15日正子(0時)以前の日付からユリウス通日を計算する方法です。

// 月数からの計算テーブル
// 0月~14月までの数値(0~2は使用しない)
private int[] DifferenceValues = { 0, 0, 0, 30, 61, 91, 122, 152, 183, 214, 244, 275, 305, 336, 367 };

public double ToJulianDayFromGregorianCalendar(
    int year, int month, int day, int hour, int minute)
{
    // 1月、2月は前年の13月、14月として計算する。
    if (month < 3)
    {
        year -= 1;
        month += 12;
    }

    var h = hour / 24d;
    var min = minute / 1440d;
    
    return Math.Floor(365.25d * year)
          + Math.Floor((double)year / 400)
          - Math.Floor((double)year / 100)
          + DifferenceValues[month]
          + day
          + 1721088.5d // (678912 + 2400000.5)
          + Math.Round(h + min, 5);
}

田村ゆかりさんが生まれて今日までで何日経つのか

さて、表題の件に戻りましょう。田村ゆかりさん(17歳)が生まれてから今日何日経ったのか計算してみましょう。先ほど紹介したグレゴリオ暦の日付からユリウス通日を求めるToJulianDayFromGregorianCalendarメソッドを使います。

// 田村ゆかりさんの誕生日のユリウス通日を求める
var jdYukariBirthday = ToJulianDayFromGregorianCalendar(1976, 2, 27, 0, 0);
// 2442835.5

// 今日のユリウス通日を求める
var jdToday = ToJulianDayFromGregorianCalendar(2015, 2, 27, 0, 0);
// 2457080.5

var interval = jdToday - jdYukariBirthday;
// 14245.0

上記のコードを実行してみましょう。実行できましたか?そうです。田村ゆかりさんが生まれてから今日までで14,245日が経過していることが分かりました。

さて、本能寺の変から田村ゆかりさんの誕生日までの経過日数を計算します。ユリウス暦の日付からユリウス通日を求めるためToJulianDayFromJulianCalendarメソッドを使います。

// 田村ゆかりさんの誕生日のユリウス通日を求める
var jdYukariBirthday = ToJulianDayFromGregorianCalendar(1976, 2, 27, 0, 0);
// 2442835.5

// 本能寺のユリウス通日を求める
var jdHen = ToJulianDayFromJulianCalendar(1582, 6, 21, 0, 0);
// 2299058.5

var interval =jdYukariBirthday - jdHen;
// 143777.0

本能寺の変の発生から田村ゆかりさんの誕生日までは143,777日経っていることがわかりました。今、気付いたけど下3桁がスリーセブンですね。

おわり。

追記

しません(