在 C++ 開發中,處理日期時間是常見需求。雖然標準庫提供了 chrono 與 C 風格的 time.h,但手寫一個日期類別能深入理解日期驗證、閏年計算與跨月跨年邏輯。本文將完整實作一個 Date 類別,包含初始化驗證、年月日增減運算,並處理閏年、大小月等邊界情況。
我們定義三個私有整數成員儲存日、月、年:
class Date {
private:
int day, month, year;
public:
void InitDate(int d, int m, int y);
void AddYear(int y);
void AddMonth(int m);
void AddDay(int d);
};
InitDate 需檢查年份範圍(-2000 ~ 3000)、月份(1~12)、日期是否符合該月天數(含閏年二月)。
bool IsLeapYear(int y) {
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
int DaysInMonth(int m, int y) {
static const int days[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
if (m == 2 && IsLeapYear(y)) return 29;
return days[m-1];
}
void Date::InitDate(int d, int m, int y) {
if (y < -2000 || y > 3000) throw std::invalid_argument("年份超出範圍");
if (m < 1 || m > 12) throw std::invalid_argument("月份無效");
int maxDay = DaysInMonth(m, y);
if (d < 1 || d > maxDay) throw std::invalid_argument("日期無效");
day = d; month = m; year = y;
}
增加年份時,若原日期為 2 月 29 日且目標年非閏年,需調整為 2 月 28 日。
void Date::AddYear(int y) {
year += y;
if (month == 2 && day == 29 && !IsLeapYear(year)) {
day = 28; // 非閏年無 2/29
}
}
月份增減可能跨年,且需調整日期不超過目標月天數(如 1/31 加一個月變 2/28 或 2/29)。
void Date::AddMonth(int m) {
int totalMonth = year * 12 + (month - 1) + m;
year = totalMonth / 12;
month = totalMonth % 12 + 1;
int maxDay = DaysInMonth(month, year);
if (day > maxDay) day = maxDay;
}
日期增減最複雜,需逐日遞增/遞減,或用「轉換為絕對天數」演算法。這裡示範逐日調整:
void Date::AddDay(int d) {
if (d >= 0) {
while (d--) {
day++;
if (day > DaysInMonth(month, year)) {
day = 1;
month++;
if (month > 12) { month = 1; year++; }
}
}
} else {
while (d++) {
day--;
if (day == 0) {
month--;
if (month == 0) { month = 12; year--; }
day = DaysInMonth(month, year);
}
}
}
}
以下主程式示範各種邊界情況:
int main() {
Date dt;
dt.InitDate(29, 2, 2012); // 閏年 2/29
dt.AddYear(1); // 變 2013/2/28
dt.InitDate(31, 1, 2014);
dt.AddMonth(1); // 變 2014/2/28
dt.InitDate(31, 12, 2020);
dt.AddDay(1); // 變 2021/1/1
return 0;
}
自行實作日期類別能強化對曆法邏輯的掌握。實務上建議使用 std::chrono(C++20)或成熟庫(如 Howard Hinnant's date.h),但理解底層運作對除錯與效能調優大有裨益。