日付処理TIPS
(C)裏目小僧
(戻る)
プログラムで日付処理を書く場合に必要(あると便利な)知識集
(このTIPSを使ったライブラリへ)
1年は何日
  1. 平年なら1年は365日、閏年は366日
  2. 閏年は4で割れる年。ただし 100で割れ、かつ400で割れない年は平年
  3. 累計日は year*365+int(year/4)-int(year/100)+int(year/400)
    • この式から翌年累計日-当年累計日で求まる
累計日
  1. 1月1日からの累計日
    31, 28 ,31,30,31,30,31,31,30,31,30,31を累計したテーブルを用意し
    閏年で3月以上なら1を加算する

  2. 3月1日からの累計日
    2月末が年末になるようにすると 閏判断が不要になり処理し易い。

  3. 3月1日からの累計日を計算式で求める
    y年m月d日から
    • if(m<=2){m+=12;y--;}
    • int((153*m+3)/5) -92+(d-1)
これ書いた後で、見つけた
http://www.si.gunma-u.ac.jp/~aoki/Hanasi/Algo/day.html
週を求める
  1. ツェラーの公式
    2月末が年末になるようにして 1年1月1日よりの累計日からモジュラ7を求める
    • if (MM<3) {y-=1;MM+=12;};
    • return (y+(y/4)-(y/100)+(y/400)+((13*MM+8)/5)+DD) % 7
    纏めてテーブルで引く格好良いソースがあった
    http://www.si.gunma-u.ac.jp/~aoki/Hanasi/Algo/dayOfWeek.html

  2. 割算を使わない方法
    • まず年が400以上なら400を減じる事を繰り返す
    • さらに100以上なら100を減じる(ただし減じた回数x5を記録)
    • 残った年から 年/4+(100を減じた回数x5)として年補正値とする
    • 累計日を求め(累計日+年補正値)%7 を得ればよい
カレンダ
  1. 1年分のカレンダは閏日(2月末日)と曜日の組み合せで14種類ある
    • 400年前毎に完全に繰返す(146097日 = 400*365.2425)
    • (西暦 MOD 400)が100,200,300を跨がない限り28年(10227日)前と同じ
    • (西暦 MOD 400)が100,200,300を跨ぐなら12年(4382日)前と同じ
    • (西暦 MOD 400)が100,200,300丁度なら6年(2191日)前と同じ
    • 1ヶ月分のカレンダは(28,29,30,31)と曜日の組み合せで28種類ある
和暦とかリンク リンク先に許可を貰っていないので、自分で貼り付けて下さい。
  • 「海上保安庁 水路部航法測地課」 2010年迄の新暦旧暦の対応表
    http://www.jhd.go.jp/cue/KOHO/

  • 暦と新暦対応の資料名
    http://www.obs.misato.wakayama.jp/kyoiku/faq/kyu-shin.html

  • 国立天文台が21世紀は 2001年1月1日からと
    http://www.nao.ac.jp/pio/qa/

  • こよみハンドブック 大阪市立科学館
    http://photon.sci-museum.kita.osaka.jp/publish/text/koyomi.html

  • 「雑念の塊」2000年のこよみ
    http://member.nifty.ne.jp/zatsunen/koyomi/koyomi.htm

  • 太陰暦について
    http://member.nifty.ne.jp/such/shumi/shumi_index.html

  • 「本能寺の変」新旧暦換算表
    http://www.inv.co.jp/~yoshio/HJ/HJKoyomi.htm

  • 「GyouseiNet」歴史と暦
    http://homepage1.nifty.com/gyouseinet/koyomi.htm

  • 「おいでませ、吉原遊郭」むかしの暦と時間
    http://www.d2.dion.ne.jp/~kaworu_t/yosiwara/koyomi.html

時間処理 HH:MM:SS

1秒経過
incHHMMSS
SS = SS + 1; /*秒を増やして 後は正規化する */
 if( SS >= 60){	SS = SS -60; MM = MM +1;
  if( MM >= 60){	MM = MM -60; HH = HH +1;
   if( HH >= 24){	HH = HH -24; incDate();/*日付をカウントアップ*/
   }
  }
 }

時間差
a-b
日付差をdDD
/* まず、時分秒をそれぞれ減算 */
int dHH = a.HH -b.HH; /* -23〜+23*/
int dMM = a.MM -b.MM; /* -59〜 59*/
int dSS = a.SS -b.SS; /* -59〜 59*/
/* 各桁を正規化 下位から正規化すれば効率的 */
if( 0 > dSS ){ dSS = dSS+60; dMM = dMM -1; }; 
if( 0 > dMM ){ dMM = dMM+60; dHH = dHH -1; };
if( 0 > dHH ){ dHH = dHH+24; dDD = dDD -1; };
 /*この時点で dDDが正なら必ず未来か 等しい よって大小比較ならここで終わり */

/*秒での差が正負で必要なら*/ difference = dDD*(60L*60L*24L) + dHH*(60L*60L) + dMM*60L + dSS;
/*前後関係を印刷する目的なら */ if(dDD>=0) { /* dDD日と dHH:dMM:dSS秒 先 */ } else { /* 過去を示す為に 1日=24時間=23時間59分60秒から減じ*/ dHH = dHH-23; dMM = dMM-59; dSS = dSS-60; /* 1日減じた分を日付に補正 */ ++dDD; /* 0と負数範囲で正規化 */ if( -60 >= dSS ){ dSS = dSS+60; dMM = dMM -1; }; if( -60 >= dMM ){ dMM = dMM+60; dHH = dHH -1; }; if( -24 >= dHH ){ dHH = dHH+24; dDD = dDD -1; }; /* -dDD日と -dHH:-dMM:-dSS秒 前 */ }

関連技術
モジュラ演算を割算を使わないで実現する方法(aが正として)
a = a % 7 while( a>=14 ) a=(a>>3)+(a&7) ; if( a>=7 )a-=7;
   ループするよりも高速にしたいなら(aは負数でも良い)
  a = ( a>>12 ) + ( a &4095 ) ;
  a = ( a>> 6 ) + ( a &  63 ) ;  /* この後は0〜130程度*/
  a = ( a>> 3 ) + ( a &   7 ) ;  /* この後は0〜18程度 */
  a = ( a>> 3 ) + ( a &   7 ) ;  /* 分岐が速ければ while文にすればよい */
  a++;/*最後に0〜7にする為に1を足して1を後でひく*/
  a = ( a>> 3 ) + ( a &   7 ) ; 
  a--;
a = a % 25 while( a>=50 ) a=7*(a>>5)+(a&31) ; if( a>=25 )a-=25;
while( a>=50 ) a=((a>>2)&(-8))-(a>>5)+a&31;if( a>=25 )a-=25;
a = a % 60 while( a>=120) a=((a>>4)&(-4))+(a&63) ; if( a>=60 )a-=60;

一般的に、a=a%b を求める時は(aが正の範囲で)
なんでこんな方法がというと、組込みに使うプアなCPUでは割算や剰余は遅い為だ。

---2038年問題へ---

本資料参照数 =