Calendar 类
Calendar 是一个抽象类,它提供了一个类方法 getInstance() 来获取 Calendar 对象,这个对象会被初始化为当前的日期和时间,并使用默认的时区和地区设置。
// 获取一个代表当前日期和时间的 Calendar 实例 Calendar now = Calendar.getInstance();
Calendar 内部使用字段来表示日期和时间,
Calendar.YEAR- 年Calendar.MONTH- 月 (注意:0代表一月,11代表十二月)Calendar.DAY_OF_MONTH- 月中的天Calendar.HOUR- 小时 (12小时制)Calendar.HOUR_OF_DAY- 小时 (24小时制)Calendar.MINUTE- 分钟Calendar.SECOND- 秒Calendar.MILLISECOND- 毫秒
基本操作
获取日期时间字段
使用 get(int field) 方法来获取特定的字段值。
重要提示:月份是从 0 开始的!
Calendar calendar = Calendar.getInstance();
// 获取年
int year = calendar.get(Calendar.YEAR); // e.g., 2025
// 获取月 (0-11)
int month = calendar.get(Calendar.MONTH); // e.g., 9 (代表十月)
// 为了方便理解,通常需要 +1
int humanReadableMonth = calendar.get(Calendar.MONTH) + 1; // e.g., 10
// 获取日
int day = calendar.get(Calendar.DAY_OF_MONTH); // e.g., 27
// 获取小时 (24小时制)
int hour = calendar.get(Calendar.HOUR_OF_DAY); // e.g., 14
// 获取小时 (12小时制)
int hour12 = calendar.get(Calendar.HOUR); // e.g., 2
// 获取分钟
int minute = calendar.get(Calendar.MINUTE); // e.g., 30
// 获取秒
int second = calendar.get(Calendar.SECOND); // e.g., 45
System.out.printf("当前时间: %d-%02d-%02d %02d:%02d:%02d%n",
year, humanReadableMonth, day, hour, minute, second);
设置日期时间
使用 set(int field, int value) 方法来设置特定的字段。
Calendar birthday = Calendar.getInstance();
// 设置为 1990 年 5 月 20 日
birthday.set(1990, Calendar.MAY, 20); // 月份使用 Calendar.MAY 常量,值为 4
// 也可以分别设置
// birthday.set(Calendar.YEAR, 1990);
// birthday.set(Calendar.MONTH, Calendar.MAY);
// birthday.set(Calendar.DAY_OF_MONTH, 20);
System.out.println("生日: " + birthday.getTime());
添加或减少时间
这是 Calendar 最强大的功能之一,使用 add(int field, int amount) 方法。
amount为正数,则增加时间。amount为负数,则减少时间。add方法会自动处理字段的进位和借位,将月份加 1,如果当前是 12 月,年份会自动加 1,月份变为 1 月。
Calendar futureDate = Calendar.getInstance();
System.out.println("当前时间: " + futureDate.getTime());
// 增加 10 天
futureDate.add(Calendar.DAY_OF_MONTH, 10);
System.out.println("10天后: " + futureDate.getTime());
// 减少 3 个月
futureDate.add(Calendar.MONTH, -3);
System.out.println("3个月后: " + futureDate.getTime());
// 增加 2 年
futureDate.add(Calendar.YEAR, 2);
System.out.println("2年后: " + futureDate.getTime());
滚动时间
roll(int field, int amount) 方法与 add 类似,但它不会改变更大的字段。
roll(Calendar.MONTH, 1): 如果当前是 12 月,它会变成 1 月,但年份不会改变。roll(Calendar.DAY_OF_MONTH, 32): 如果某个月有 31 天,它会滚动到下个月的 1 日,但月份和年份不会改变。
这个方法用得相对较少,容易引起混淆。
Calendar rollDate = Calendar.getInstance();
rollDate.set(2025, Calendar.DECEMBER, 31); // 设置为 2025-12-31
System.out.println("滚动前: " + rollDate.getTime());
// 月份滚动 +1,年份不会变
rollDate.roll(Calendar.MONTH, 1);
System.out.println("月份滚动后: " + rollDate.getTime()); // 输出将是 Sun Jan 31 14:30:45 CST 2025
// 重置日期
rollDate.set(2025, Calendar.DECEMBER, 31);
// 日期滚动 +1,月份不会变
rollDate.roll(Calendar.DAY_OF_MONTH, 1);
System.out.println("日期滚动后: " + rollDate.getTime()); // 输出将是 Tue Jan 01 14:30:45 CST 2025
综合示例:计算两个日期之间的天数差
这是一个常见的需求,我们可以通过将两个日期都转换为毫秒数,然后相减得到。
import java.util.Calendar;
import java.util.Date;
public class CalendarDateDifference {
public static void main(String[] args) {
// 日期 1: 2025-10-01
Calendar date1 = Calendar.getInstance();
date1.set(2025, Calendar.OCTOBER, 1);
// 日期 2: 2025-10-27
Calendar date2 = Calendar.getInstance();
date2.set(2025, Calendar.OCTOBER, 27);
// 确保 date2 是较晚的日期
if (date1.after(date2)) {
Calendar temp = date1;
date1 = date2;
date2 = temp;
}
// 获取两个日期的毫秒时间戳
long timeInMillis1 = date1.getTimeInMillis();
long timeInMillis2 = date2.getTimeInMillis();
// 计算毫秒差
long diffInMillis = timeInMillis2 - timeInMillis1;
// 将毫秒转换为天 (1000ms * 60s * 60m * 24h)
long diffInDays = diffInMillis / (1000 * 60 * 60 * 24);
System.out.println("日期 1: " + date1.getTime());
System.out.println("日期 2: " + date2.getTime());
System.out.println("两个日期相差: " + diffInDays + " 天");
}
}
Calendar 的缺点与现代替代方案 (java.time)
尽管 Calendar 功能强大,但它存在很多设计上的缺陷,这也是为什么 Java 8 引入了全新的 java.time API。
| 特性 | java.util.Calendar |
java.time (现代 API) |
|---|---|---|
| 可变性 | 可变 (线程不安全) | 不可变 (线程安全) |
| 月份 | 从 0 开始 (0=1月, 11=12月),容易混淆 | 从 1 开始 (1=1月, 12=12月),符合直觉 |
| API 设计 | 方法分散 (get, set, add, roll),不够直观 | API 设计清晰,方法名更具描述性 (plusDays, minusMonths) |
| 时区处理 | 复杂且容易出错 | 内置强大的 ZonedDateTime 和 ZoneId,处理更清晰 |
| 精度 | 同时包含日期和时间,难以单独处理日期或时间 | 提供专门的类:LocalDate (日期), LocalTime (时间), LocalDateTime (日期时间) |
使用 java.time 重写上面的示例
计算两个日期之间的天数差:
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class JavaTimeDateDifference {
public static void main(String[] args) {
// 创建日期对象 (月份从1开始,非常直观)
LocalDate date1 = LocalDate.of(2025, 10, 1);
LocalDate date2 = LocalDate.of(2025, 10, 27);
// 使用 ChronoUnit 计算天数差,代码更简洁
long diffInDays = ChronoUnit.DAYS.between(date1, date2);
System.out.println("日期 1: " + date1);
System.out.println("日期 2: " + date2);
System.out.println("两个日期相差: " + diffInDays + " 天");
}
}
日期加减:
import java.time.LocalDate;
public class JavaTimeCalculation {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println(" " + today);
// 加10天
LocalDate futureDate = today.plusDays(10);
System.out.println("10天后: " + futureDate);
// 减3个月
LocalDate pastDate = today.minusMonths(3);
System.out.println("3个月前: " + pastDate);
}
}
Calendar是基础:如果你正在维护旧的 Java 项目(Java 7 或更早),你必须掌握Calendar,记住它的核心方法是get(),set(),add(),并且月份从 0 开始。java.time是未来:对于所有新的 Java 项目(Java 8+),强烈推荐使用java.time包中的类,它们更安全、更易用、更不容易出错。- 转换:如果你需要将旧的
Calendar或Date对象转换为新的java.time对象,可以使用Instant作为桥梁:Date date -> Instant instant = date.toInstant();Calendar calendar -> Instant instant = calendar.toInstant();Instant instant -> ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
