「給定日期,求第二天」是曆法計算的入門經典,涉及閏年規則、大小月邊界、年末跨年等邊界條件。本文提供一個健壯的 C 實現,包含輸入解析、合法性校驗、日期遞增、錯誤時調用系統 cal 命令輔助查看,並支持交互式循環測試。
// 閏年判斷:400 整除 或 4 整除但不整除 100
int is_leap(int y) { return (y%4==0 && y%100!=0) || (y%400==0); }
// 月份天數表(索引 1~12)
const int mdays[13] = {0, 31,28,31,30,31,30,31,31,30,31,30,31};
// 獲取指定年月的天數
int month_days(int y, int m) {
if (m == 2) return is_leap(y) ? 29 : 28;
return mdays[m];
}
next_day()
// 返回 0 成功,-1 輸入非法
int next_day(int *y, int *m, int *d) {
if (*m < 1 || *m > 12) return -1;
int dim = month_days(*y, *m);
if (*d < 1 || *d > dim) return -1;
(*d)++;
if (*d > dim) { // 月底跨月
*d = 1;
(*m)++;
if (*m > 12) { // 年底跨年
*m = 1;
(*y)++;
}
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main() {
int y, m, d;
char cmd[64], ch, c;
time_t now;
do {
printf("輸入年月日(YYYY-MM-DD):");
if (scanf("%d-%d-%d", &y, &m, &d) != 3) {
while ((c=getchar())!='\n' && c!=EOF);
printf("格式錯誤,請用 YYYY-MM-DD\n"); continue;
}
if (next_day(&y, &m, &d) == 0) {
printf("第二天為 %04d-%02d-%02d。\n", y, m, d);
} else {
printf("日期輸入錯誤,調用 cal 查看 %d 年 %d 月:\n\n", y, m);
sprintf(cmd, "/usr/bin/cal %d %d", m, y);
system(cmd);
// system("clear"); // 可選清屏
}
while ((c=getchar())!='\n' && c!=EOF);
printf("繼續測試(y/n):");
ch = getchar();
} while (ch=='y' || ch=='Y');
time(&now);
printf("Now is %s", ctime(&now));
puts("Have a good day!");
return 0;
}
| 輸入 | 輸出 | 驗證點 |
|---|---|---|
| 2023-01-31 | 2023-02-01 | 大月底跨月 |
| 2023-02-28 | 2023-03-01 | 平年 2 月底 |
| 2024-02-29 | 2024-03-01 | 閏年 2 月底 |
| 2023-12-31 | 2024-01-01 | 年末跨年 |
| 2023-04-31 | 錯誤 + cal 4月 | 小月 31 日非法 |
| 2023-13-01 | 錯誤 | 月份越界 |
<time.h> struct tm + mktime() 自動處理閏年/跨月/跨年/時區/DST,避免手寫邏輯 Bug。cal:Windows 無 cal,可改用 system("cmd /c date") 或自行打印月曆。DateRange(start, end) 實現 ++it 自動遞增,支援範圍 for 循環。JDN = (1461*(Y+4800+(M-14)/12))/4 + (367*(M-2-12*((M-14)/12)))/12 - (3*((Y+4900+(M-14)/12)/100))/4 + D - 32075。手寫「下一天」是理解曆法邊界條件的最佳練習;實際項目中,**請毫不猶豫使用成熟的時間庫**。理解底層邏輯,是為了在庫出 Bug 時能精準定位、在庫不支持場景時能手寫兜底。