link もっと前
   2009年 11月 18日 -
      2009年 11月 18日  
link もっと後

link 未来から過去へ表示(*)
link 過去から未来へ表示

日々

link permalink

経過日を年月日に変換 - その 2

前回(2009年11月16日の日記参照)は経過日から月日への計算を行いました。残るは年の計算です。

グレゴリオ歴の周期性

前回も紹介したとおり、グレゴリオ暦の平年、閏年には下記の法則があります。

  1. 平年は 365日、閏年は 366日
  2. 西暦が 4で割り切れる年は閏年(1年が 366日)
  3. 西暦が 100で割り切れる年は平年(1年が 365日)
  4. 西暦が 400で割り切れる年は閏年(1年が 366日)

4年周期は平年、平年、平年、閏年のパターンです。

100年周期は 4年周期を 25回繰り返すだけですが、最後の 100年目だけは閏年ではありません。

400年周期は 100年周期を 4回繰り返すだけですが、最後の 400年目だけは閏年になります。400年以上を扱うルールはありませんので、以降 400年周期で同じパターンが続きます。

図示すると下記のようになります。オレンジの四角が閏年を表しています。


閏年のパターン

各周期の日数の計算式は下記の通りです。

  1. 1年は 365日
  2. 4で割り切れるときは閏年、つまり最後の年だけ +1日
    365+365+365+366
    =365*4+1
    =1461
  3. 100で割り切れるときは平年 、つまり最後の年だけ -1日
    1461+1461+…++1461+1460
    =1461*25-1
    =36524
  4. 400で割り切れるときは閏年 、つまり最後の年だけ +1日
    36524+36524+36524+36525
    =36524*4+1
    =146097

パターンがわかってしまえば、与えられた経過日にこのパターンがいくつ含まれているか?を計算するのみです。

経過日から年への変換

では実際に経過日から年を計算してみます。しつこいですがパターンは下記 4つです。経過日にこれらのパターンがいくつ含まれているか調べます。

  • パターン A: 400年 = 146097日
  • パターン B: 100年 = 36524日
  • パターン C: 4年 = 1461日
  • パターン D: 1年 = 365日

なぜ上記パターンの数を求めれば年数が出るのか?がわかる人は次の章を飛ばして読んでください。

桁の話

ここでは桁の概念についてと、年への換算にどう使うか?を補足させていただきます。

私たちにおなじみの 10進数は 1234 のように書きますが、これってどういう意味でしょうか?

10進数の 1桁目は 10^0 がいくつ含まれているか、2桁目は 10^1 がいくつ含まれているかを示します。n桁目は 10^(n-1) がいくつ含まれているか?を示します。

ですから 10進数で 1234 は(1 * 10^3 + 2 * 10^2 + 3 * 10^1 + 4 * 10^0 = 1234)という数(10進数表記)を表します。

当たり前?でもこれは m進数でも同様で、n桁目は m^(n-1) がいくつ含まれているか?を示します。

ですから 8進数の 1234 は(1 * 8^3 + 2 * 8^2 + 3 * 8^1 + 4 * 8^0 = 668)という数(10進数表記)を表します。

経過日から年を求める場合も、これと似た考え方ができます。各パターンの数を桁と見なして(400年の桁、100年の桁、4年の桁、1年の桁)、10進数へと換算してやればよいのです。

各パターンの数(400年、100年、4年、1年)が 1, 2, 3, 3 であれば(1 * 400 + 2 * 100 + 3 * 4 + 3 * 1 = 615)という年(10進数表記)を表します。

このように各パターンの数を求めてあげることで、年数が計算可能なのです。

経過日から年への計算式

パターン A の数は(経過日 / 146097)です。余りの経過日は 400年未満のどこか(0年3月1日〜399年2月29日)を表します。これはパターン B の計算に回します。

パターン B の数は(経過日 / 36524)です。余りの経過日は 100年未満のどこか(0年3月1日〜99年2月28日)を表します。余りは同様にパターン C の計算に回します。

しかし 146096日(399年2月29日)の場合に 3となるべきところが 4になり間違えてしまうため、特別に 3とします。その際の余り経過日は 99年2月29日ですから 36525 - 1 = 36524日(開始が 0日のため 1引く)となります。

パターン C の数は(経過日 / 1461)です。余りの経過日は 4年未満のどこか(0年3月1日〜3年2月29日)を表します。余りは同様にパターン D の計算に回します。

パターン D の数は(経過日 / 365)です。余りの経過日は 1年未満のどこか(0年3月1日〜0年2月28日)を表します。ここの余りは経過日から月日を求める計算に回します。

しかし 1460日(3年2月29日)の場合に 3となるべきところが 4になり間違えてしまうため、特別に 3とします。その際の余り経過日は 0年2月29日ですから 366 - 1 = 365日(開始が 0日のため 1引く)となります。

実装例

実装する関数の仕様は下記の通りです。

date
400で割り切れる西暦(1600年、2000年など)の3月1日からの経過日を渡します。値域は正の整数です。
year
年を返します。値域は正の整数です。
mod
1年に満たない経過日を返します。値域は 0〜365 です。
返り値
成功ならば 0 を返します。エラーが起きた場合は -1 を返します。

この処理を C 言語で書くと下記のようになります。

経過日から年への変換、実装例

int date_to_year(int date, int *year, int *mod)
{
        int a400, a100, a4, a;
        int m400, m100, m4, m;

        if (date < 0) {
                return -1;
        }

        a400 = date / 146097;
        m400 = date % 146097;
        if (m400 == 146096) {
                a100 = 3;
                m100 = 36524;
        } else {
                a100 = m400 / 36524;
                m100 = m400 % 36524;
        }
        a4 = m100 / 1461;
        m4 = m100 % 1461;
        if (m4 == 1460) {
                a = 3;
                m = 365;
        } else {
                a = m4 / 365;
                m = m4 % 365;
        }

        if (year) {
                *year = a400 * 400 + a100 * 100 + a4 * 4 + a;
        }
        if (mod) {
                *mod = m;
        }

        return 0;
}

これで経過日から年への変換は完成ですが、まだテストが終わっていません。

月日の変換のときは、入力に対する答えがたかだか 366通りと少なかったため、目でチェックしました。しかし今回は 400年つまり 146097日もの数をチェックしなければなりません。こんな数を目で一々チェックしていたら頭がおかしくなります。

テストの方法についてはまた次回に。

[編集者: すずき]
[更新: 2009年 11月 23日 02:32]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
   2009年 11月 18日 -
      2009年 11月 18日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDF ファイル RSS 1.0
QR コード QR コード

最終更新: 8/23 23:38

カレンダー

<2009>
<<<11>>>
1234567
891011121314
15161718192021
22232425262728
2930-----

最近のコメント 5件

  • link 19年07月18日
    hdk 「あっ、AAMはマニュアルのオペレーション...」
    (更新:07/25 00:02)
  • link 19年07月18日
    すずき 「AAM(ASCII Adjust AX ...」
    (更新:07/24 22:22)
  • link 19年07月18日
    hdk 「加算減算は符号のありなしどちらも命令が同...」
    (更新:07/24 07:25)
  • link 19年07月18日
    すずき 「OFをセットして例外を出したければINT...」
    (更新:07/20 11:02)
  • link 19年07月18日
    すずき 「MUL については、結果が倍のビット幅に...」
    (更新:07/20 10:56)

最近の記事 3件

link もっとみる
  • link 19年08月21日
    すずき 「[RockPro64 と linux-next とヘッドフォン] ...」
    (更新:08/23 23:38)
  • link 19年08月12日
    すずき 「[独自の apt サーバー - その 3 - apt の信頼シ] ...」
    (更新:08/12 12:13)
  • link 19年08月11日
    すずき 「[独自の apt サーバー - その 2 - apt-ftpa] ...」
    (更新:08/12 12:13)

こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報