杰瑞科技汇

Java日期Calendar如何正确使用与处理?

java.util.Calendar 简介

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

Java日期Calendar如何正确使用与处理?-图1
(图片来源网络,侵删)
  • 获取和设置日期/时间的各个字段(如年、月、日、时、分、秒)。
  • 在日期上进行加减操作(计算“30天后”的日期)。
  • 处理不同时区。
  • 进行日期格式化和解析(虽然更推荐使用 SimpleDateFormat)。

Calendar 是一个工厂类,你不能直接通过 new Calendar() 来创建实例,而是需要调用其静态工厂方法 getInstance()

// 获取一个 Calendar 实例,默认使用当前时间和系统默认时区
Calendar calendar = Calendar.getInstance();

Calendar 的核心使用方法

获取日期/时间字段

Calendar 使用一组常量来表示日期/时间的各个字段,这些常量定义在 Calendar 类中,

  • Calendar.YEAR
  • Calendar.MONTH ⚠️ 重要:月份是从 0 开始的!0 代表一月,11 代表十二月。
  • Calendar.DAY_OF_MONTHCalendar.DATE(表示当前月的第几天)
  • Calendar.HOUR (12小时制)
  • Calendar.HOUR_OF_DAY (24小时制)
  • Calendar.MINUTE
  • Calendar.SECOND
  • Calendar.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) 方法来设置字段。

Java日期Calendar如何正确使用与处理?-图2
(图片来源网络,侵删)
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 功能强大,但它存在一些严重的设计缺陷,导致使用起来容易出错且不直观:

  1. 月份从 0 开始:这是最常见的陷阱。Calendar.MAY 的值是 4,而不是 5,这非常反直觉。
  2. 可变性Calendar 对象是可变的,当你对一个 Calendar 实例调用 add()set() 方法时,它会修改对象本身,这在多线程环境下是不安全的,也容易导致意外副作用。
  3. 命名混乱HOURHOUR_OF_DAY 容易混淆。Date 类的很多方法也已经废弃,与 Calendar 的配合使用令人困惑。
  4. 线程不安全:由于其可变性,多个线程同时修改一个 Calendar 实例会导致数据不一致。

现代 Java 的解决方案:java.time API (Java 8+)

为了解决 DateCalendar 的问题,Java 8 引入了全新的 java.time API,这个 API 设计优秀、不可变、线程安全,并且功能更强大。所有新的 Java 项目都应该优先使用 java.time API。

Java日期Calendar如何正确使用与处理?-图3
(图片来源网络,侵删)

以下是 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.time API,它更安全、更易用,是未来的标准。
  • 维护旧项目 (Java 7 或更早):如果你必须使用 Calendar,请务必:
    1. 注意 Calendar.MONTH 从 0 开始。
    2. 尽量不要共享 Calendar 实例,每次使用都重新通过 getInstance() 获取。
    3. 考虑将 Calendar 转换为 java.time 类型进行复杂计算,如果可能的话。
  • Calendar 迁移到 java.timeCalendarjava.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 中的日期时间处理!

分享:
扫描分享到社交APP
上一篇
下一篇