2024年8月13日 星期二

推算 24 節氣日期

用來推算未來 24 節氣日期, 雖然推算出來的詳細時間不準, 但日期很準, 若改用今年的小寒時間當基底, 推算未 50 或過去 50 年內的 24 節氣日期, 應該堪用.

#include <stdio.h>
#include <time.h>
const char *climate24_terms[24]  = {
    "小寒", "大寒", "立春", "雨水", "驚蟄", "春分",//: 節氣索引  5, 日時 = 夜時
    "清明", "穀雨", "立夏", "小滿", "芒種", "夏至",//: 節氣索引 11, 日時 > 夜時
    "小暑", "大暑", "立秋", "處暑", "白露", "秋分",//: 節氣索引 17, 日時 = 夜時
    "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" //: 節氣索引 23, 日時 < 夜時
};//24節氣始於年初小寒,北半球:4(X)季始於立(X), 春分秋分日夜等長, 夏至日長夜短, 冬至夜長日短

bool is_leap_year(int ad_year) { // 公(西)元是潤年的條件: (4 倍數且非 100 倍數) 或是 400 倍數
    return (ad_year % 4 == 0) && (ad_year % 100 != 0) || (ad_year % 400 == 0);
}

int *find_climate24(int ad_year) {// 推算當年農曆 24 節氣的陽曆日期
    static const int table_2024[24][4] = {
        { 1,  6,  4, 49}, { 1, 20, 22,  7}, { 2,  4, 16, 27}, { 2, 19, 12, 13}, { 3,  5, 10, 23}, { 3, 20, 11,  6},// [ 5][]: 春分
        { 4,  4, 15,  2}, { 4, 19, 22,  0}, { 5,  5,  8, 10}, { 5, 20, 21,  0}, { 6,  5, 12, 10}, { 6, 21,  4, 51},// [11][]: 夏至
        { 7,  6, 22, 20}, { 7, 22, 15, 44}, { 8,  7,  8,  9}, { 8, 22, 22, 55}, { 9,  7, 11, 11}, { 9, 22, 20, 44},// [17][]: 秋分
        {10,  8,  3,  0}, {10, 23,  6, 15}, {11,  7,  6, 20}, {11, 22,  3, 56}, {12,  6, 23, 17}, {12, 21, 17, 21} // [23][]: 冬至
    }; // 2024 年 24 節氣的時間點 {月, 日, 時, 分} 參考資料 https://www.hko.gov.hk/tc/gts/astronomy/Solar_Term.htm
    static long climate24_table[24] = {0}; // 24 節氣時間曲線, 需製表一次
    static int days_of_solar_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    static int climate24_date[24 * 4]; // 回傳陣列 [月, 日, 時, 分, ...]
    if (climate24_table[0] == 0)  {
        days_of_solar_month[1] = is_leap_year(2024) ? 29 : 28;// 更新 2 月天數
        for (int i = 0; i < 24 ; i ++) {
            int month  = table_2024[i][0]; // 月
            long days  = table_2024[i][1]; // 日
            for (int j = 1; j < month; j ++) days += days_of_solar_month[j - 1]; // 計算總天數
            climate24_table[i] = days * 1440 + table_2024[i][2] * 60 + table_2024[i][3];// 分鐘
        }
    }
    days_of_solar_month[1] = is_leap_year(ad_year) ? 29 : 28;// 更新當年 2 月天數
    int *target = climate24_date; // 填入 [月, 日, 時, 分, ...]
    for (int n = 0; n < 24; n ++) {
        int &solar_month = target[0];
        int &solar_day     = target[1];
        int &solar_hour    = target[2];
        int &solar_min      = target[3];
        long dT = climate24_table[n];
        if (ad_year >= 2024) { // 推算未來
            for (int year = 2024; year < ad_year; year ++) {
                dT += (365.2421990741f - (is_leap_year(year) ? 366 : 365)) * 1440;
            }
        } else { // 回推過往
            for (int year = ad_year; year < 2024; year ++) {
                dT -= (365.2421990741f - (is_leap_year(year) ? 366 : 365)) * 1440;
            }
        }
        solar_day    = dT / 1440;// 日
        solar_hour  = dT % 1440 / 60; // 小時
        solar_min    = dT % 1440 % 60; // 分鐘
        solar_month = 1;// 日期從 1 月 1 日開始運算, 準備轉回當年日期
        for (int i = 1; i <= 12; i ++) { // 一年共 12 月
            int month_days = days_of_solar_month[solar_month - 1];
            if (solar_day <= month_days) break;
            solar_day -= month_days;
            solar_month ++;
        }
        target += 4; // 下一節氣
    }
    return climate24_date;
}


int main() {
    time_t now = time(0);// 現在時間
    struct tm *today = localtime(&now); // 取得今天的資料, todo: GMT + 8
    int ad_year = today->tm_year + 1900;// 今年: 西元年
    int ad_month = today->tm_mon + 1;   // 當月
    int ad_day = today->tm_mday;    
    printf("今天 西元%d 年 %d 月 %d 日\n\n", ad_year, ad_month, ad_day);
    ad_year -= 50;
    int *date = find_climate24(ad_year);
    for(int i = 0; i < 24; i++) {
        printf("%d 年 %s %2d 月 %2d 日 %2d:%2d\n", ad_year, climate24_terms[i], date[0], date[1], date[2], date[3]);
        date += 4;
    }    
}

沒有留言:

張貼留言

使用 pcie 轉接器連接 nvme SSD

之前 AM4 主機板使用 pcie ssd, 但主機板故障了沒辦法上網, 只好翻出以前買的 FM2 舊主機板, 想辦法讓老主機復活, 但舊主機板沒有 nvme 的界面, 因此上網買了 pcie 轉接器用來連接 nvme ssd, 遺憾的是 grub2 bootloader 無法識...