Cによる日付処理関数 (戻る)
私は 2038対策として time_t を使わない事を選択しました。時間処理は簡単ですが、日付処理関数は少しだけ難しいです。そこで、私のライブラリを公開します。
■一覧
int DaysInYear(int y) 西暦y年は何日あるか
char DaysInMonth(int y,char m) 西暦y年m月は何日あるか
char YMD_AddB(int *Y,char *M,char *D,char a) 西暦にa日を加算
int YMD_AddD(int *Y,char *M,char *D,long d) 西暦にd日を加算
long YearMMDDtoCount(int Year,int MM,int DD) 西暦から相対日を求める
int YearMMDDtoWeek( int Year,int MM,int DD) 曜日を得る(0:日曜日)
YMMDD 関係
3バイトで年月日を表現し、年も1バイトですが、移動窓という手法で当年さえ設定すれば何年先でも問題ありません。
int setNearYear(int Year) 西暦の当年 yearWはこの年を基準に見る
int yWtoYYYY(yearW yW) YearWから西暦年に取り出す<
int incYMMDD(YMMDD *day) 次の日にする加算
int YMMDDgetWeek(YMMDD day) その日の曜日を 0〜6で得る(0:日曜日)
■簡単な説明
YMMDD
移動窓処理
組込み用途では年に8bit以上使用するのは無駄な事が多いです。
8bitでも当年から±120年は十分処理出来ます。 移動窓という手法です。当年から±char範囲/2という窓で時間データを見ているというイメージです。
年に限らず、相対日をこの方法で記録する事もあります
YMD_AddD指定した西暦にlong dで何日先かを加算します。
YearMMDDtoCountで求めた相対日を西暦に変換するのにも使えます
負数を与える事も出来ます。が、結果が17世紀とか昔になると意味が無いでしょう。
大きな dを与えた時に効率が良いようにしています。
この関数だけは除算剰余を使っています。
短日の場合はYMD_AddBを使って下さい。
YearMMDDtoCount1970年1月1日から何日経過しているかを求めます。(time_tがこの日を基準にしているのであわせました)
2000年1月1日を基準にした方が良いかもしれませんね。
日付と日付が何日離れているかを求めたりするのに使うのでしょう。
ダウンロードしたいなら days.c (以下と同じものです)
ヘッダファイル
days.h
/************************************************************************
 日付処理  (C)裏目小僧 2000年1月  著作権は主張しますが、ご使用は自由です
                       ご使用は各自の責任の下に自由に利用・改変して下さい。
                       ただし、動作保証は行いませんし、利用上の責任も負い
                       ません。
                       権利関係を明確にしたい場合は私に連絡して下さい。
                        GGA03463@nifty.ne.jp 
                         

 組込み用途では年に8bit以上使用するのは無駄な事が多いです。
 8bitでも当年から±120年は十分処理出来ます。   移動窓という手法
 です。当年から±char範囲/2という窓で時間データを見ているという
 イメージです。年に限らず、相対日をこの方法で記録する事もあります。

int  NearYear                  西暦の当年 yearWはこの年を基準に見る  
int  yWtoYYYY(yearW yW)        YearWから西暦年に取り出す
int incYMMDD(YMMDD *day)       次の日にする加算
int YMMDDgetWeek(YMMDD day)    その日の曜日を 0〜6で得る(0:日曜日)


YMMDD 形式でなく、直接を年月日を扱う関数です。
int  DaysInYear(int y)                        西暦y年は何日あるか
char DaysInMonth(int y,char m)                西暦y年m月は何日あるか
char YMD_AddB(int *Y,char *M,char *D,char a)  西暦にa日を加算 
int  YMD_AddD(int *Y,char *M,char *D,long d)  西暦にd日を加算
long YearMMDDtoCount(int Year,int MM,int DD)  西暦から相対日を求める
int  YearMMDDtoWeek( int Year,int MM,int DD)  曜日を得る(0:日曜日)

////////////////////////days.h////////////////////////////////////////
typedef char  yearW ; /*年用のメモリサイズ定義 shortにする事も可 */
struct _YMMDD {
yearW yW;       //yWはyW=(yearW)2000のように設定し 参照はyWtoYYYYを使う 
char MM, DD;} ; //月1〜12と日を示す

int  SetNearYear(int Year)  ; // 西暦の当年 yearWはこの年を基準に見る 
int  yWtoYYYY(yearW yW)     ; // YearW日付  から 現在西暦年を取り出す  
int  incYMMDD(YMMDD *day)   ; // 次の日にする加算                     
int  YMMDDgetWeek(YMMDD day); // その日の曜日を 0〜6で得る(0:日曜日)  


//       YMMDD 形式でなく、直接を年月日を扱う関数です。               
int  DaysInYear(int y)                      ; //西暦y年は何日あるか   
char DaysInMonth(int y,char m)              ; //西暦y年m月は何日あるか
char YMD_AddB(int *Y,char *M,char *D,char a); //西暦にa日を加算       
int  YMD_AddD(int *Y,char *M,char *D,long d); //西暦にd日を加算       
long YearMMDDtoCount(int Year,int MM,int DD); //西暦から相対日を求める
int  YearMMDDtoWeek( int Year,int MM,int DD); //曜日を得る(0:日曜日)  

***********************************************************************/
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include "days.h"

const int NearYear=2000;     /*西暦の当年 yearWはこの年を基準に見る   */

int yWtoYYYY(yearW yW) 
{                      /* yearWがshortならこの関数の中身は不要  */
yearW y8f=yW-NearYear; 
return NearYear+y8f;
}
int SetNearYear(int Year)
{
 return  NearYear=Year;
}


#ifdef FirstMOD
int DaysInYear(int y) /*西暦y年は何日あるか*/
{
 if( (y%4)!=0) return 365;  /*4で割れなければ必ず1年は365日*/
 if( (y%100)==0) /*and*/
 if( (y%400)!=0) return 365;/*100で割れて400で割れない年も 365日*/
 return 366;                /*でなければ閏*/
}
#else
int DaysInYear(int y) /*西暦y年は何日あるか*/
{
char y4=y&3;//4で割った余り
 if( (y4)!=0 ) return 365;   /*4で割れなければ必ず1年は365日       */
 switch( 2 ){                /* y%400を命令速度に応じて選んで下さい */
   case 0: y %= 400;  break; /* 剰余が高速なら switch(0) に         */
   case 1: y =  (112*(y>> 9) +(y& 511));   /* これは速くなかった    */
           while( y>=400  )y -=  400;break;                  
   case 2: while( y>=1600 )y -= 1600;  
           while( y>=400  )y -=  400;      
  };
 if( y == 100 
  || y == 200 
  || y == 300 ) return 365;/*100で割れて400で割れない年も 365日*/
 return 366;                /*でなければ閏*/
}
#endif

char DaysInMonth(int y,char m) /*西暦y年m月は何日あるか*/
{                          /* 1  2  3  4  5  6  7  8  9 10 11 12月*/
 const static char tbl[]={29,31,28,31,30,31,30,31,31,30,31,30,31};
 if(m!=2) return tbl[m];
 if(DaysInYear(y)==365)return tbl[2];
                       return tbl[0];
}

static int _addYMMDD(YMMDD *day,char a) /* 日付に a(28以下)を 加算*/
{
 if(a>=0)
  {
  while(a>28){_addYMMDD(day,28); a-=28;};
   day->DD+=a;
  if(day->DD <= 28) return 0;
   { char DD=DaysInMonth( yWtoYYYY(day->yW),day->MM); /*西暦y年m月は何日あるか*/
    if(DD >= day->DD) return 0;
     day->DD-=DD;
     if(++(day->MM)<=12) return 1;
     day->MM=1;
     ++(day->yW);
     return 2;
   }
  }else{
   while(a<-28){_addYMMDD(day,-28); a+=28;};
   day->DD-=a;
  if(day->DD > 0 ) return 0;
    if(--(day->MM)<=0)
     {
     day->MM+=12;
     --(day->yW);
     }

    { char DD=DaysInMonth( yWtoYYYY(day->yW),day->MM); /*西暦y年m月は何日あるか*/
     day->DD-=DD;
    }
     return 1;
  }

}
int incYMMDD(YMMDD *day) /* 日付に 1 加算*/
{   return _addYMMDD(day,1);
}

int YMMDDgetWeek(YMMDD day) /* その日の曜日を 0:日 〜 6:土*/
{      /* ツェラーの公式 変形 */
 int y=yWtoYYYY(day.yW);
 int m=day.MM;
  if (m<3) {y-=1;m+=12;};
  while(y>=2000)y-=2000;
  while(y>=400 )y-=400;
  {/* yが0〜399の範囲で y+y/4-y/100 を求める */
   unsigned  short int y4  =y>>2;
   unsigned  short int y100=(y4*41)>>10;
  y=y+y4-y100;
  }
  m=m*30+ ((153*m+450)>>8 ) ;
  return (y+m+day.DD) % 7 ; 
}

int YMMDDgetWeek2(YMMDD day) /* その日の曜日を 0:日 〜 6:土*/
{ /* ツェラーの公式  参考:奥村晴彦著:コンピュータアルゴリズム事典 P17*/
int y=yWtoYYYY(day.yW);
int m=day.MM;
  if (m<3) {y-=1;m+=12;};
  return (y+(y/4)-(y/100)+(y/400)+((13*m+8)/5)+day.DD) % 7 ; 
}

////////////////////////////////////////////////////////////
// 西暦年月日に (char)a日  を加算します
// aに ±28 以上 を渡すと内部で28日に分割して処理します
// 
char YMD_AddB(int *Y,char *M,char *D,char a) /* 西暦にa日を加算 */
{
 char ret=0;
 if(a>=0)
  {
  while(a>28){ ret |= YMD_AddB( Y,M,D,28 ); a -= 28;};
   *D += a;
  if(*D <= 28) return ret;
   { char days = DaysInMonth( *Y , *M );
    if( days >= *D ) return ret;
      ret|=1;
     *D -= days;
     if( ++(*M) <= 12 ) return ret;
     *M = 1;
     ++(*Y);
     return 3;
   }
  }else{
   while(a<-28){ ret |= YMD_AddB( Y,M,D,-28 ); a += 28;};
     *D += a;
  if(*D > 0 ) return ret;
      ret|=1;
    if( --(*M) <= 0 )  { (*M) += 12;  --(*Y); ret=3; }

    { char days=DaysInMonth( *Y , *M );
     *D+=days;
    }
     return ret;
  }

}
/////////////////////////////////////////////////////////////////////
// 指定した日付に long加算します。 相対日を 西暦に戻す時に使用します
// 
int YMD_AddD(int *Y,char *M,char *D,long d)
{
//if( d>-50L && d<50L ) { YMD_AddB( Y,M,D,(char)d); return *Y; };
  {
/* 3月1日から何日かを示すテーブル */
                         /* 13  14  3  4  5  6   7   8   9  10  11  12月*/
/*                          31, 28,31,30,31,30, 31, 31, 30, 31, 30, 31  */
  static const int tblT[]={0,306,337, 0,31,61,92,122,153,184,214,245,275,306,337};
  int ay , ad , y400 , y100 , y4 , dd , m ;
  int nd = tblT[*M] + (*D) -1;
  int y  = *Y ;
    if( nd >= 306 ) y--;
    while( d <0 ){ y -= 400; d+=146097L; } /*dが負数なら400年循環を使って正に*/
  
    ay= d/365L;  ad=d-ay*365L;    /* ay=d*365 で およその年差 */
  
    y400 = y % 400 ; 
    y100 = y % 100 ; 
    y4   = y %   4 ; 
    ad-=(y4+ay)/4 - (y100+ay)/100 + (y400+ay)/400; /* 閏年のあった数を求める */
  
    y  += ay;
    while( ad+nd >= (dd=DaysInYear( y+1 )) ){ad-=dd;y++;}
    while( ad+nd <   0                     ){ad+=DaysInYear(y--);};
    ad+=nd;
    m = 3 + ( (22+ad*67)>>11 ); /* ad=336の時だけmが1大きいミスが生じる */
    if(m>12){m-=12;y++;};
    lp2:
     dd = 1 + ad - tblT[m];
    if( dd<=0 ){ m-- ; goto lp2; }; 
    *D    = dd;
    *M    = m;
    *Y    = y;
  return  y;
  }
}


/***************************************************************
 * 西暦の日付から 1970年元旦からの相対日を求めます
 * 年月日データ同士が何日離れているか計算するときに使用します。
 * まあ私は実際には使った事は無いのですが                       
 ***************************************************************/
long YearMMDDtoCount(int Year,int MM,int DD) /*西暦から相対日を求める*/
{ 
long y=-730485L;
int m=MM; /*  MM = 1〜12 */
  /*146097 = 400*365.2425 つまり400年毎に同じカレンダとなる*/
  if( m<3 ){ Year--; m+=12; }; /* 3月1日を基準に*/
  while( Year <    0 ){Year+=  400; y-=146097L;   }/*146097 = 400*365.2425*/
  while( Year>=4*400 ){Year-=4*400; y+=146097L*4L;}/*最近のデータ処理を速く*/
  while( Year>=  400 ){Year-=  400; y+=146097L;   }

  {/* yが0〜399の範囲で年/4 年/100 を求める */
   unsigned  short int y4  =Year>>2;
   unsigned  short int y100=(y4*41)>>10;
   y += (long)Year*365L+(long)(y4-y100); /*年を365.2425 倍する*/
  }
  m=m*30+ ((153*m+190-8448)>>8 ) ; /* m=(153*m+3)/5 +1 */
  return y+(long)m + (long)DD +10957L;  /*1970年1月1日を基準に*/
                  /*2000年1月1日を基準にするなら+10957を無くす*/
}

int YearMMDDtoWeek(int Year,int MM,int DD) /* その日の曜日を 0:日 〜 6:土*/
{ 
 int y=0;
 int m=MM;
  if (m<3) {Year-=1;m+=12;};

  while(Year>=1600)Year-=1600; /*カレンダは400年毎に繰返す */
  while(Year>=400 )Year-= 400; /* 0〜399年の範囲にする     */
  while(Year <   0)Year+= 400; /*紀元前を入力しないなら不要*/

 /*年 y = (年*(365%7)+年/4-年/100) */
    while(Year>=100 ){Year-=100;y+=5;};
     y+=Year+(Year>>2);
 /*月 日*/
 { /* (m*(30%7)+(3*m+3)/5+1)%7=  3  4  5  6  7  8  9 10 11 12  1 2月*/
 const static char tbl[]={0,0,0, 2, 5, 0, 3, 5, 1 ,4, 6, 2, 4, 0,3};
  y= y+ DD + tbl[m] ; /* m*(30%7)+ (3*m+3)/5+1 */
 }

 /* CPUのモジュラ演算が十分高速なら return y % 7 で良い */ 
  while( y>=21 ) y= (y>>3) + (y&7); /*最大は180よって最大2回*/
  while( y>=7  )y-=7;               /*これのループ も最大2回 */
  return y;
}


// 本資料参照数 =