杰瑞科技汇

Java Calendar 如何正确计算日期差?

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)
时区处理 复杂且容易出错 内置强大的 ZonedDateTimeZoneId,处理更清晰
精度 同时包含日期和时间,难以单独处理日期或时间 提供专门的类: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);
    }
}
  1. Calendar 是基础:如果你正在维护旧的 Java 项目(Java 7 或更早),你必须掌握 Calendar,记住它的核心方法是 get(), set(), add(),并且月份从 0 开始
  2. java.time 是未来:对于所有新的 Java 项目(Java 8+),强烈推荐使用 java.time 包中的类,它们更安全、更易用、更不容易出错。
  3. 转换:如果你需要将旧的 CalendarDate 对象转换为新的 java.time 对象,可以使用 Instant 作为桥梁:
    • Date date -> Instant instant = date.toInstant();
    • Calendar calendar -> Instant instant = calendar.toInstant();
    • Instant instant -> ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
分享:
扫描分享到社交APP
上一篇
下一篇