【C言語】
ツェラーの公式だけじゃない
-年月日から曜日を求める方法-

■この記事の概要

この記事では、ツェラーの公式を使用して、指定した年月日から曜日を計算する方法を解説しています。また、mktime関数を用いた計算手法も紹介。具体例を交え、C言語での効率的な実装方法を説明し、閏年処理のポイントも取り上げています。


■ツェラー(Zeller)の公式で曜日を求める

#include    <stdio.h>
/* 曜日の算出関数(ツェラーの公式版)  0:日〜6:土 */
char *zeller(int year, int mon, int day){
    static char *w[] = {"日","月","火","水","木","金","土"};
    if ((mon == 1) || (mon == 2)){
        year--;
        mon += 12;
    }
    int idx = (year + year/4 - year/100 + year/400 +
            (13 * mon + 8)/5 + day) % 7;
    return  w[idx];
}
static inline int isleap(int y) {
    return  (y%4==0 && y%100!=0) || y%400==0 ;
}
static const int daytbl[2][13] = {
    {-1,31,28,31,30,31,30,31,31,30,31,30,31,},  //平年
    {-1,31,29,31,30,31,30,31,31,30,31,30,31,},  //閏年
};
int main(void){
    for(int year = 1582; year <= 3999;year++){
        for(int mon = 1; mon <= 12; mon++){
            int mondays = daytbl[isleap(year)][mon] ;
            for(int day = 1; day <= mondays; day++){
                printf("%04d/%02d/%02dは%s曜日\n",
                    year,
                    mon,
                    day,
                    zeller(year,mon,day)
                );
            }
        }   
    }
}

年月日から曜日を求めるのに有名な
ツェラーの公式をC言語化してます。

1月2月が前年の13月14月になったり、
4,100,400の閏年関連の定数が
筆者には難解です。


■mktime()関数で曜日を求める

#include    <stdio.h>
#include    <time.h>
/* 曜日の算出関数(mktime版) */
char    *ymd2week(int year,int mon,int day){
    struct tm tm = {
        .tm_sec     =0,         /* 秒 [0-61] 最大2秒までのうるう秒を考慮 */
        .tm_min     =0,         /* 分 [0-59] */
        .tm_hour    =0,         /* 時 [0-23] */
        .tm_mday    =day,       /* 日 [1-31] */
        .tm_mon     =mon-1,     /* 月 [0-11] 0から始まることに注意 */
        .tm_year    =year-1900, /* 年 [1900からの経過年数] */
        .tm_wday    =0,         /* 曜日 [0:日 1:月 ... 6:土] */
        .tm_yday    =0,         /* 年内の通し日数 [0-365] 0から始まることに注意*/
        .tm_isdst   =-1         /* 夏時間が無効であれば 0 */
    };
    if(mktime(&tm) == (time_t)-1){
        return  "mktimeで失敗しました";    
    }
    //1970/01/01 は木曜日
    static  char *w[] = {"日","月","火","水","木","金","土"};
    return  w[tm.tm_wday];
}
static inline int isleap(int y) {
    return  (y%4==0 && y%100!=0) || y%400==0 ;
}
static const int daytbl[2][13] = {
    {-1,31,28,31,30,31,30,31,31,30,31,30,31,},  //平年
    {-1,31,29,31,30,31,30,31,31,30,31,30,31,},  //閏年
};
int main(void){
    for(int year = 1582; year <= 3999;year++){
        for(int mon = 1; mon <= 12; mon++){
            int mondays = daytbl[isleap(year)][mon] ;
            for(int day = 1; day <= mondays; day++){
                printf("%04d/%02d/%02dは%s曜日\n",
                    year,
                    mon,
                    day,
                    ymd2week(year,mon,day)
                );
            }
        }   
    }
}

tm構造体にはtm_wdayという
曜日を表す便利なメンバがあります。
これを使うと分かりやすいですが
ちょっと処理が重たいです。

■坂本智彦氏方式で曜日を求める

#include <stdio.h>
#include <string.h>
/* 曜日の算出関数(坂本智彦氏版) */
//http://www.kouno.jp/home/c_faq/c20.html#31
/* 0 = Sunday, 1 <= m <= 12, y > 1752 or so */
char *dayofweek(int y, int m,int d)    
{
    static char *w[] = {"日","月","火","水","木","金","土"};
    static int  t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    y -= m < 3;
    int     n = (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
    return  w[n];
}
static inline int isleap(int y) {
    return  (y%4==0 && y%100!=0) || y%400==0 ;
}
static const int daytbl[2][13] = {
    {-1,31,28,31,30,31,30,31,31,30,31,30,31,},  //平年
    {-1,31,29,31,30,31,30,31,31,30,31,30,31,},  //閏年
};
int main(void){
    for(int year = 1582; year <= 3999;year++){
        for(int mon = 1; mon <= 12; mon++){
            int mondays = daytbl[isleap(year)][mon] ;
            for(int day = 1; day <= mondays; day++){
                printf("%04d/%02d/%02dは%s曜日\n",
                    year,
                    mon,
                    day,
                    dayofweek(year,mon,day)
                );
            }
        }   
    }
}

筆者にはさっぱりわかりませんが、
一番高速で軽量です
(C-FAQ 20.31で紹介されていました)

参考:

C-FAQ20.31:日付から曜日を求める方法は。