首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言問答

C語言計算兩日期天數差:演算法原理、完整實現與陷阱避坑

     編輯:C 語言問答

核心演算法思路

將兩個日期分別轉換為「從某個基準日(如 1970-01-01 或 0000-01-01)起的總天數」,然後相減取絕對值。關鍵難點在於**閏年判斷**和**每月天數差異**的正確處理。

閏年判斷規則

// 格里高利曆閏年規則
bool is_leap_year(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

每月天數表

int days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 閏年2月為29天

方案一:累加法(直觀易懂,適合教學)

#include <stdio.h>
#include <stdlib.h>

int days_from_epoch(int year, int month, int day) {
    int total = 0;
    // 1. 完整年份天數
    for (int y = 1970; y < year; y++) {
        total += is_leap_year(y) ? 366 : 365;
    }
    // 2. 完整月份天數
    for (int m = 1; m < month; m++) {
        if (m == 2) {
            total += is_leap_year(year) ? 29 : 28;
        } else {
            total += days_in_month[m-1];
        }
    }
    // 3. 當月天數
    total += day - 1;  // 1號算第0天
    return total;
}

int main() {
    int y1, m1, d1, y2, m2, d2;
    printf("輸入日期1 (年 月 日): ");
    scanf("%d %d %d", &y1, &m1, &d1);
    printf("輸入日期2 (年 月 日): ");
    scanf("%d %d %d", &y2, &m2, &d2);
    
    int days1 = days_from_epoch(y1, m1, d1);
    int days2 = days_from_epoch(y2, m2, d2);
    int diff = abs(days1 - days2);
    
    printf("天數差: %d 天\n", diff);
    return 0;
}

方案二:儒略日數公式(O(1) 高效,工程首選)

// 將日期轉換為儒略日數 (Julian Day Number)
// 算法來源:Fliegel & Van Flandern (1968) / 《天文算法》
long date_to_jdn(int year, int month, int day) {
    int a = (14 - month) / 12;
    int y = year + 4800 - a;
    int m = month + 12*a - 3;
    // JDN = day + (153*m + 2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
    return day + (153*m + 2)/5 + 365*y + y/4 - y/100 + y/400 - 32045;
}

int main() {
    int y1, m1, d1, y2, m2, d2;
    scanf("%d%d%d%d%d%d", &y1,&m1,&d1,&y2,&m2,&d2);
    long jdn1 = date_to_jdn(y1, m1, d1);
    long jdn2 = date_to_jdn(y2, m2, d2);
    printf("天數差: %ld\n", labs(jdn1 - jdn2));
    return 0;
}

方案三:使用標準庫 <time.h> (C89 標準,跨平台)

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

time_t date_to_timestamp(int year, int month, int day) {
    struct tm t = {0};
    t.tm_year = year - 1900;  // 年份從1900開始
    t.tm_mon = month - 1;     // 月份 0-11
    t.tm_mday = day;
    t.tm_hour = 12;           // 設為中午避免夏令時邊界問題
    return mktime(&t);
}

int main() {
    int y1, m1, d1, y2, m2, d2;
    scanf("%d%d%d%d%d%d", &y1,&m1,&d1,&y2,&m2,&d2);
    
    time_t t1 = date_to_timestamp(y1, m1, d1);
    time_t t2 = date_to_timestamp(y2, m2, d2);
    
    if (t1 == -1 || t2 == -1) {
        printf("日期超出 time_t 範圍 (通常 1970-2038 或 1900-2099)\n");
        return 1;
    }
    
    double diff_days = difftime(t1, t2) / 86400.0;
    printf("天數差: %.0f 天\n", fabs(diff_days));
    return 0;
}

三種方案對比

方案優點缺點適用場景
累加法邏輯清晰、易於理解調試、無庫依賴O(Y) 時間、年份範圍受限於循環教學、小範圍年份、嵌入式無標準庫
儒略日數O(1) 常數時間、支持極大年份範圍、無運行時依賴公式較難記憶驗證高性能、歷史天文、跨世紀計算
time.h標準庫、自動處理閏年/時區、代碼最少受限於 time_t 範圍 (32位系統 1970-2038)、依賴系統時區設置現代應用、業務邏輯、快速開發

常見陷阱與測試用例

陷阱測試用例預期結果
閏年邊界2000-02-28 到 2000-03-012 天 (2000是閏年)
世紀非閏年1900-02-28 到 1900-03-011 天 (1900非閏年)
跨世紀1999-12-31 到 2000-01-011 天
同一天2024-01-01 到 2024-01-010 天
日期順序2024-12-31 到 2024-01-01365 天 (取絕對值)
大年份差1900-01-01 到 2100-01-0173049 天 (儒略日數可算)

輸入驗證建議

bool is_valid_date(int y, int m, int d) {
    if (y < 1 || m < 1 || m > 12 || d < 1) return false;
    int dim = days_in_month[m-1];
    if (m == 2 && is_leap_year(y)) dim = 29;
    return d <= dim;
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved