萬年曆是 Java 初學者練習日期計算、控制流、GUI 開發的經典項目。本文將由淺入深,展示三種不同複雜度的實現方案:
CalendarUtil,支援「某年第幾天/第幾周/星期幾」等查詢。(year%4==0 && year%100!=0) || year%400==0。public class ConsoleCalendar {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("請輸入年份:");
int year = sc.nextInt();
System.out.print("請輸入月份:");
int month = sc.nextInt();
int totalDays = 0;
// 1. 累加 1900 年至 year-1 的天數
for (int y = 1900; y < year; y++) {
totalDays += isLeap(y) ? 366 : 365;
}
// 2. 累加 year 年 1 月至 month-1 月的天數
for (int m = 1; m < month; m++) {
totalDays += daysInMonth(year, m);
}
// 3. 1 號星期幾(0=周日...6=周六,這裡以周一為起始調整)
int weekDay = (totalDays + 1) % 7; // 1900-01-01 是周一
// 4. 打印表頭與空格佔位
System.out.println("日\t一\t二\t三\t四\t五\t六");
for (int i = 0; i < weekDay; i++) System.out.print("\t");
// 5. 打印日期並換行
int days = daysInMonth(year, month);
for (int d = 1; d <= days; d++) {
System.out.print(d + "\t");
if ((weekDay + d) % 7 == 0) System.out.println();
}
}
static boolean isLeap(int y) {
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
static int daysInMonth(int y, int m) {
if (m == 2) return isLeap(y) ? 29 : 28;
if (m == 4 || m == 6 || m == 9 || m == 11) return 30;
return 31;
}
}
封裝 CalendarUtil,提供以下靜態方法:
cptDay(year, month, day):該日期是當年的第幾天。getWeek(year, month, day):基姆拉爾森公式計算星期(0=周一...6=周日)。weekCount(year, month, day):該日期屬於當年的第幾周。printCal(year):打印全年日曆。// 注意:1、2 月視作上一年的 13、14 月
public static int getWeek(int year, int month, int day) {
if (month < 3) { month += 12; year--; }
return (day + 2*month + 3*(month+1)/5 + year + year/4 - year/100 + year/400) % 7;
}
HH:mm:ss),獨立線程每秒刷新。public class CalendarApp {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MainFrame().setVisible(true));
}
}
class MainFrame extends JFrame {
private JComboBox yearBox, monthBox;
private JLabel[] dayLabels = new JLabel[49];
private Calendar today = Calendar.getInstance();
MainFrame() {
initUI();
initData();
startClock();
}
// ... 初始化下拉框 1-9999 年、1-12 月
// ... 根據 yearBox/monthBox 選擇重繪日曆
// ... 內部類 Clock extends Canvas implements Runnable 繪製時鐘
}
實際項目中,建議直接使用 java.util.Calendar 或 Java 8+ 的 java.time 包,避免手寫算法出錯:
public class JdkCalendarDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("輸入年份:");
int year = sc.nextInt();
Calendar cal = Calendar.getInstance();
for (int m = 0; m < 12; m++) {
cal.set(year, m, 1);
printMonth(cal);
}
}
static void printMonth(Calendar c) {
c.set(Calendar.DAY_OF_MONTH, 1);
System.out.printf("\n========= %d 月 =========\n", c.get(Calendar.MONTH)+1);
String[] ws = {"日","一","二","三","四","五","六"};
for (String w : ws) System.out.print(w + "\t");
System.out.println();
int offset = c.get(Calendar.DAY_OF_WEEK) - 1; // 周日=1
for (int i = 0; i < offset; i++) System.out.print("\t");
int month = c.get(Calendar.MONTH);
while (c.get(Calendar.MONTH) == month) {
System.out.print(c.get(Calendar.DAY_OF_MONTH) + "\t");
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) System.out.println();
c.add(Calendar.DAY_OF_MONTH, 1);
}
}
}
| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| 手寫算法 | 教學、面試、嵌入式無標準庫環境 | 深度理解日期本質 | 易出錯、維護成本高 |
| 工具類封裝 | 項目內部通用日期查詢 | API 統一、可測試 | 仍需自行維護算法正確性 |
| Swing GUI | 桌面工具、教學演示 | 直觀、功能全 | Swing 技術棧較老舊 |
| JDK Calendar / java.time | 所有生產環境 | 標準、線程安全、時區支持完善 | 需學習新 API |
建議:學習階段請手寫算法理解原理;實際開發務必使用 java.time.LocalDate、YearMonth、DateTimeFormatter 等現代 API,配合 TemporalAdjusters 可一行代碼搞定「本月第一天/最後一天/下個工作日」等複雜需求。