java.util.Calendar 简介
Calendar 是 Java 1.1 引入的一个抽象类,它提供了一个比 Date 类更强大、更灵活的方式来操作日期和时间,它主要用于:

- 获取和设置日期/时间的各个字段(如年、月、日、时、分、秒)。
- 在日期上进行加减操作(计算“30天后”的日期)。
- 处理不同时区。
- 进行日期格式化和解析(虽然更推荐使用
SimpleDateFormat)。
Calendar 是一个工厂类,你不能直接通过 new Calendar() 来创建实例,而是需要调用其静态工厂方法 getInstance()。
// 获取一个 Calendar 实例,默认使用当前时间和系统默认时区 Calendar calendar = Calendar.getInstance();
Calendar 的核心使用方法
获取日期/时间字段
Calendar 使用一组常量来表示日期/时间的各个字段,这些常量定义在 Calendar 类中,
Calendar.YEARCalendar.MONTH⚠️ 重要:月份是从 0 开始的!0 代表一月,11 代表十二月。Calendar.DAY_OF_MONTH或Calendar.DATE(表示当前月的第几天)Calendar.HOUR(12小时制)Calendar.HOUR_OF_DAY(24小时制)Calendar.MINUTECalendar.SECONDCalendar.DAY_OF_WEEK(星期几,1 是星期日,7 是星期六)
使用 get(int field) 方法来获取这些字段的值。
Calendar calendar = Calendar.getInstance();
// 获取年份
int year = calendar.get(Calendar.YEAR); // e.g., 2025
// 获取月份 (注意:0代表一月)
int month = calendar.get(Calendar.MONTH); // e.g., 9 (代表十月)
// 获取月份 (更友好的方式)
int friendlyMonth = calendar.get(Calendar.MONTH) + 1; // e.g., 10
// 获取当月的第几天
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); // e.g., 27
// 获取小时 (24小时制)
int hour = calendar.get(Calendar.HOUR_OF_DAY); // e.g., 14
// 获取星期几 (1=星期日, 2=星期一, ..., 7=星期六)
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // e.g., 6 (代表星期五)
System.out.println("当前日期: " + year + "年" + friendlyMonth + "月" + dayOfMonth + "日");
System.out.println("星期: " + dayOfWeek);
设置日期/时间字段
使用 set(int field, int value) 方法来设置字段。

Calendar calendar = Calendar.getInstance(); // 设置日期为 2025年5月20日 calendar.set(Calendar.YEAR, 2025); calendar.set(Calendar.MONTH, Calendar.MAY); // 注意使用常量 calendar.set(Calendar.DAY_OF_MONTH, 20); // 也可以一次性设置 // calendar.set(2025, Calendar.MAY, 20);
日期的加减操作
这是 Calendar 的一个强大功能,使用 add(int field, int amount) 方法。
amount为正数,则增加。amount为负数,则减少。- 一个重要的特性是,当某个字段超出其正常范围时,会向上或向下“进位”。
月份 + 13会变成下一年的第一个月。
Calendar calendar = Calendar.getInstance();
System.out.println(" " + calendar.getTime());
// 计算 10 天后的日期
calendar.add(Calendar.DAY_OF_MONTH, 10);
System.out.println("10天后: " + calendar.getTime());
// 重置回今天
calendar = Calendar.getInstance();
// 计算 3 个月前的日期
calendar.add(Calendar.MONTH, -3);
System.out.println("3个月前: " + calendar.getTime());
// 重置回今天
calendar = Calendar.getInstance();
// 计算 2 小时后
calendar.add(Calendar.HOUR_OF_DAY, 2);
System.out.println("2小时后: " + calendar.getTime());
比较日期
Calendar 提供了三个方法来比较两个 Calendar 实例:
after(Object when): 如果此Calendar表示的时间晚于when指定的时间,则返回true。before(Object when): 如果此Calendar表示的时间早于when指定的时间,则返回true。equals(Object obj): 如果此Calendar表示的时间与obj相同,则返回true。
Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
// 设置 calendar2 为明天
calendar2.add(Calendar.DAY_OF_MONTH, 1);
System.out.println("calendar1 在 calendar2 之前? " + calendar1.before(calendar2)); // true
System.out.println("calendar1 在 calendar2 之后? " + calendar1.after(calendar2)); // false
Calendar 的问题与局限性
尽管 Calendar 功能强大,但它存在一些严重的设计缺陷,导致使用起来容易出错且不直观:
- 月份从 0 开始:这是最常见的陷阱。
Calendar.MAY的值是 4,而不是 5,这非常反直觉。 - 可变性:
Calendar对象是可变的,当你对一个Calendar实例调用add()或set()方法时,它会修改对象本身,这在多线程环境下是不安全的,也容易导致意外副作用。 - 命名混乱:
HOUR和HOUR_OF_DAY容易混淆。Date类的很多方法也已经废弃,与Calendar的配合使用令人困惑。 - 线程不安全:由于其可变性,多个线程同时修改一个
Calendar实例会导致数据不一致。
现代 Java 的解决方案:java.time API (Java 8+)
为了解决 Date 和 Calendar 的问题,Java 8 引入了全新的 java.time API,这个 API 设计优秀、不可变、线程安全,并且功能更强大。所有新的 Java 项目都应该优先使用 java.time API。

以下是 java.time 中与 Calendar 功能对应的类:
| 功能/概念 | java.util.Calendar |
java.time (现代 API) |
优点 |
|---|---|---|---|
| 日期 | Calendar |
LocalDate |
不可变,只包含日期(年月日) |
| 时间 | (无直接对应) | LocalTime |
不可变,只包含时间(时分秒纳秒) |
| 日期+时间 | Calendar |
LocalDateTime |
不可变,包含日期和时间 |
| 带时区的日期+时间 | Calendar + TimeZone |
ZonedDateTime |
不可变,处理时区更清晰 |
| 时间差 | 手动计算 | Duration, Period |
专门用于计算时间间隔 |
| 格式化/解析 | SimpleDateFormat (线程不安全) |
DateTimeFormatter (线程安全) |
线程安全,API 更优雅 |
java.time 使用示例
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class JavaTimeExample {
public static void main(String[] args) {
// 1. 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("今天的日期: " + today); // e.g., 2025-10-27
// 2. 获取当前日期和时间 (无时区)
LocalDateTime now = LocalDateTime.now();
System.out.println("现在的时间: " + now); // e.g., 2025-10-27T14:30:15.123
// 3. 获取特定日期 (月份从 1 开始!)
LocalDate specificDate = LocalDate.of(2025, 5, 20);
System.out.println("特定日期: " + specificDate); // 2025-05-20
// 4. 日期加减 (不可变!会返回一个新的对象)
LocalDate tenDaysLater = today.plusDays(10);
System.out.println("10天后: " + tenDaysLater);
LocalDate threeMonthsAgo = today.minus(3, ChronoUnit.MONTHS);
System.out.println("3个月前: " + threeMonthsAgo);
// 5. 日期比较
System.out.println("今天在 10天后 之前吗? " + today.isBefore(tenDaysLater)); // true
// 6. 处理时区
ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("东京时间: " + tokyoTime);
System.out.println("纽约时间: " + newYorkTime);
// 7. 格式化日期
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
String formattedDateTime = now.format(formatter);
System.out.println("格式化后的时间: " + formattedDateTime); // e.g., 2025年10月27日 14:30
}
}
总结与建议
| 特性 | java.util.Calendar |
java.time API |
|---|---|---|
| 设计理念 | 可变、面向过程 | 不可变、函数式 |
| 线程安全 | 不安全 | 安全 |
| API 设计 | 繁琐、易错 (如月份0开始) | 清晰、直观 |
| 核心功能 | 基础日期时间操作 | 功能强大,支持时区、间隔等 |
| 推荐度 | 不推荐,仅用于维护旧代码 | 强烈推荐,所有新项目的首选 |
如何选择?
- 新项目 (Java 8+):毫不犹豫地使用
java.timeAPI,它更安全、更易用,是未来的标准。 - 维护旧项目 (Java 7 或更早):如果你必须使用
Calendar,请务必:- 注意
Calendar.MONTH从 0 开始。 - 尽量不要共享
Calendar实例,每次使用都重新通过getInstance()获取。 - 考虑将
Calendar转换为java.time类型进行复杂计算,如果可能的话。
- 注意
- 从
Calendar迁移到java.time:Calendar和java.time之间没有直接的转换方法,但可以通过java.util.Date作为桥梁。
// Calendar -> Date Calendar calendar = Calendar.getInstance(); Date date = calendar.getTime(); // Date -> Instant -> ZonedDateTime (推荐) Instant instant = date.toInstant(); ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault()); // 如果只需要日期部分 LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
希望这份详细的指南能帮助你全面理解 Java 中的日期时间处理!
