2024年7月30日 星期二

簡單利用查表法顯示農曆春節的月曆

#include <stdio.h>
const char *title_weekday[7] = {"  星期日", "  星期一", "  星期二", "  星期三", "  星期四", "  星期五", "  星期六"};
const char *sky_terms[10]    = {"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};// 天干
const char *gnd_terms[12]    = {"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};// 地支
const char *animal_terms[12] = {"鼠", "牛", "虎", "兔", "龍", "蛇", "馬", "羊", "猴", "雞", "狗", "豬"};// 生肖
const unsigned int y1900_2099[] = {
    0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,
    0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,
    0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,
    0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,
    0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,
    0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,
    0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,
    0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,
    0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,
    0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,
    0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,
    0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,
    0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,
    0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,
    0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,
    0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50,0x06b20,0x1a6c4,0x0aae0,
    0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,
    0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,
    0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,
    0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,
    0x0d520
};  // 西元 1900 - 2100 年農曆資料表
    // 位元:  16       ,  15:4    ,   3:0
    // 編碼: 閏月 大/小, 各大/小月, 潤月月份
const int lunar_base_y1900[4]   = {5, 11, 1, 31}; // 1900 年初(1/31前)是己(5)亥(11)年, 1/31 開始是庚子年正月初一
const int  year_normal_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 常(規)年 = 365 天
const int  year_leap_days[12]   = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 閏(補)年 = 366 天
bool is_leap_year(int ad_year) { // 判斷公(西)元是潤年的條件: (4 倍數且非 100 倍數) 或是 400 倍數
    return (ad_year % 4 == 0) && (ad_year % 100 != 0) || (ad_year % 400 == 0);
}
int days_of_lunar_year(int ad_year) {// 當年農曆天數
    if (ad_year < 1900 || ad_year > 2100) return -1;
    unsigned int idx  = ad_year - 1900;
    unsigned int info = y1900_2099[idx];// 查表
    int lunar_days = 29 * 12 + (info & 0xf ? (info & 0x10000 ? 30 : 29) : 0); // 小月 29 天, 共 12 個月, 外加閏月天數
    for (unsigned int bit_test = 0x8000; bit_test >= 0x10; bit_test >>= 1) {// 補大月的天數
        if (info & bit_test) lunar_days ++;
    }
    return lunar_days;
}
bool next_lunar_new_year(int ad_year, int *ad_month, int *ad_day) { // 明年的春節日
    if (ad_year < 1900 || ad_year > 2100) return false;
    *ad_day += days_of_lunar_year(ad_year) - (is_leap_year(ad_year) ? 366 : 365); // 新的春節日, 只需補足陰陽曆日差天數
    if (*ad_day > 31) {// 當日期超過 31 天(1月)需修正
        *ad_day -= 31;
        *ad_month = 2;// 春節會跨到 2 月
    } else if (*ad_day <= 0) {// 或是日期不足時也需修正
        *ad_day += 31;
        *ad_month = 1;// 春節回落至 1 月
    }
    return true;
}
int *find_lunar_new_year(int ad_year) {
    if (ad_year < 1900 || ad_year > 2100) return nullptr;
    static int day_info[2] = {1, 31};
    int month = lunar_base_y1900[2];
    int day = lunar_base_y1900[3];
    for (int year = 1900; year < ad_year; year++) {
        if (! next_lunar_new_year(year, &month, &day)) break;
    }
    day_info[0] = month;
    day_info[1] = day;
    return day_info;
}
int main() {
    int year = 2024;
    int *day_info = find_lunar_new_year(year);
    if (day_info == nullptr) return -1;
    int month = day_info[0];
    unsigned int idx  = year - 1900;
    int sky = (idx + 1 + lunar_base_y1900[0]) % 10;
    int gnd = (idx + 1 + lunar_base_y1900[1]) % 12;
    printf("  %s%s(%s)年-月曆\t\t\t\t%8d 月\t\t\t\t西元%8d 年\n",
        sky_terms[sky], gnd_terms[gnd], animal_terms[gnd],
        month, year
    );
    int y0 = year - 1;// 註: 公(西)元 1 年 1 月 1 日 是 星期日 (weekday = 0)
    int weekday = year + y0/4 + y0/400 - y0/100;// 潤年補天數
    const int *days_of_month = is_leap_year(year) ? year_leap_days : year_normal_days;
    for (int i = 0; i + 1 < month; i ++) weekday += days_of_month[i];
    weekday %= 7;
    for (int i = 0; i < 7; i++) {
        printf("%s\t", title_weekday[i]);
    }
    if (weekday != 0) {
        printf("\n");
    }
    int position = 0;
    while (position < weekday) {
        if (++ position % 7 == 0) printf("\n");
        printf("          \t");
    }
    for (int day = 1; day <= days_of_month[month - 1]; day ++) {
        if (position ++ % 7 == 0) printf("\n");
        printf(day == day_info[1] ? "春節%4d\t" :  "%8d\t", day);
    }
    printf("\n\n");
    return 0;
}

2024年7月22日 星期一

簡單的萬年曆 c 程式碼

#include <stdio.h>
const char *title_weekday[7]= {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
const int  year_normal_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 常(規)年
const int  year_leap_days[12]   = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 閏(補)年
bool is_leap_year(int ad_year) { // 公(西)元是潤年的條件: (4 倍數且非 100 倍數) 或是 400 倍數
    return (ad_year % 4 == 0) && (ad_year % 100 != 0) || (ad_year % 400 == 0);
}

int main() {
    int year  = 2024;
    int month = 1;

    int y0 = year - 1;// 註: 公(西)元 1 年 1 月 1 日 是 星期日 (weekday = 0)
    int leap_weekday = year + y0/4 + y0/400 - y0/100;// 潤年補天數
    const int *days_of_month = is_leap_year(year) ? year_leap_days : year_normal_days;
    for (int i = 0; i + 1 < month; i ++) leap_weekday += days_of_month[i];
    leap_weekday %= 7;

    for (int to_show = month; to_show <= 12; to_show ++) {
        printf("\t\t%6d 月\t\t西元 %6d 年\n", to_show, year);
        for (int i = 0; i < 7; i++) printf("%s\t", title_weekday[i]);
        
        int position = 0;
        if (leap_weekday != 0) printf("\n");
        while (position < leap_weekday) {
            if (++ position % 7 == 0) printf("\n");
            printf("      \t");
        }

        for (int day = 1; day <= days_of_month[to_show - 1]; day ++) {
            printf(position ++ % 7 == 0 ? "\n%6d\t" : "%6d\t", day);
        }
        printf("\n\n");
        leap_weekday = position % 7;
    }
}

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...