Java 日期/时间

java.util.Calendar 类

java.util.Calendar 是 Java 中用于处理日期和时间的抽象类,旨在弥补 Date 类在日期字段(年、月、日等)操作上的不足。它提供了对日期字段的精细化控制,支持日期计算、时区处理等功能。

Calendar 类通过字段(如年、月、日、时、分、秒)来表示日期时间,允许对这些字段进行单独操作,并支持时区转换和日期计算(如加减天数、月份等)。

核心方法说明

获取 Calendar 实例

  • Calendar.getInstance():最常用,返回当前系统时间的实例,默认使用系统时区和本地语言环境(Locale)。

  • Calendar.getInstance(TimeZone zone):指定时区(如纽约、伦敦),适用于跨时区时间处理。

示例:获取当前日期

import java.util.Calendar;

public class CalendarExample {
    public static void main(String[] args) {
        // 获取当前时间的 Calendar 实例
        Calendar calendar = Calendar.getInstance();
        System.out.println("当前时间:" + calendar.getTime());
    }
}

注意:Calendar 是抽象类,不能直接通过 new 创建,必须通过工厂方法(如 getInstance())获取子类实例(实际是 GregorianCalendar,即公历)。

获取时间字段

通过 get() 方法获取指定字段的值,核心字段常量如下:

字段常量

含义

取值范围

注意事项

Calendar.YEAR

年份

4 位整数(如 2024)

Calendar.MONTH

月份

0~11

0=1 月,11=12 月(需加 1 使用)

Calendar.DATE

日期(月内天数)

1~31(根据月份变化)

同 DAY_OF_MONTH

Calendar.HOUR_OF_DAY

小时(24 小时制)

0~23

推荐使用(避免 12 小时制混淆)

Calendar.HOUR

小时(12 小时制)

0~11

需配合 AM_PM

判断上 / 下午

Calendar.MINUTE

分钟

0~59

Calendar.SECOND

0~59

Calendar.DAY_OF_WEEK

星期

1~7

1 = 周日,2 = 周一,...7 = 周六

示例:

Calendar calendar = Calendar.getInstance();

int year = calendar.get(Calendar.YEAR); // 年份(4位,如2023)
int month = calendar.get(Calendar.MONTH) + 1; // 月份(0~11,需+1)
int day = calendar.get(Calendar.DAY_OF_MONTH); // 日
int hour = calendar.get(Calendar.HOUR_OF_DAY); // 24小时制小时
int minute = calendar.get(Calendar.MINUTE); // 分钟

System.out.printf("当前日期:%d年%d月%d日 %d:%d%n", 
                 year, month, day, hour, minute);

设置时间字段

  • 单个字段设置:set(int field, int value),如 cal.set(Calendar.YEAR, 2024)。

  • 批量设置(年月日):set(int year, int month, int date),如 cal.set(2024, 6, 15)(6=7 月)。

  • 批量设置(年月日时分秒):set(int year, int month, int date, int hourOfDay, int minute, int second),如 cal.set(2024, 8, 25, 14, 30, 0)。

示例:

Calendar calendar = Calendar.getInstance();

// 设置为 2024年3月15日(月份需-1,因为0代表1月)
calendar.set(Calendar.YEAR, 2024);
calendar.set(Calendar.MONTH, Calendar.MARCH); // 等价于 set(Calendar.MONTH, 2)
calendar.set(Calendar.DAY_OF_MONTH, 15);

System.out.println("设置后的日期:" + calendar.getTime());

注意:设置字段时,Calendar 不会立即计算时间,直到调用 get()、add() 等方法时才会“延迟计算”,避免重复计算开销。

时间计算

add(int field, int amount) 方法用于对指定字段进行 “增减” 操作,amount 为正数表示 “加”,负数表示 “减”。若增减后字段超出范围,会自动向高位字段进位。例如:2024-12-31 加 1 天,会自动变为 2025-01-01。

示例:

Calendar calendar = Calendar.getInstance();
System.out.println("当前日期:" + calendar.getTime());

// 加 3 天
calendar.add(Calendar.DAY_OF_MONTH, 3);
System.out.println("3天后:" + calendar.getTime());

// 减 2 个月
calendar.add(Calendar.MONTH, -2);
System.out.println("再减2个月后:" + calendar.getTime());

roll(int field, int amount) 方法与 add() 类似,但只修改指定字段,不影响更高层级的字段(如月份滚动不影响年份)。示例:

import java.util.Calendar;

public class CalendarExample {
    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2023, Calendar.DECEMBER, 31); // 2023-12-31
        System.out.println("初始日期:" + calendar.getTime());
        
        // roll 加 1 天(只修改日期,月份不变)
        calendar.roll(Calendar.DAY_OF_MONTH, 1);
        System.out.println("roll加1天后:" + calendar.getTime()); 
        // 2023-12-01(而非2024-01-01)
    }
}

运行结果:

初始日期:Sun Dec 31 22:35:50 CST 2023
roll加1天后:Fri Dec 01 22:35:50 CST 2023

时间比较

compareTo(Calendar anotherCalendar) 方法用于比较两个 Calendar 的时间先后,返回 int 结果:

  • 若当前 Calendar 时间 早于 目标时间:返回 负数(如 - 1)。

  • 若当前 Calendar 时间 晚于 目标时间:返回 正数(如 1)。

  • 若时间 相等:返回 0。

示例:

import java.util.Calendar;

public class CalendarCompareExample {
    public static void main(String[] args) {
        // 创建第一个Calendar对象(2023年1月1日)
        Calendar cal1 = Calendar.getInstance();
        cal1.set(2023, Calendar.JANUARY, 1);
        
        // 创建第二个Calendar对象(2024年1月1日)
        Calendar cal2 = Calendar.getInstance();
        cal2.set(2024, Calendar.JANUARY, 1);
        
        // 比较两个日期
        int result1 = cal1.compareTo(cal2);
        int result2 = cal2.compareTo(cal1);
        int result3 = cal1.compareTo(cal1);
        
        // 输出结果
        System.out.println("cal1 与 cal2 比较: " + result1);  // 输出负数,cal1早于cal2
        System.out.println("cal2 与 cal1 比较: " + result2);  // 输出正数,cal2晚于cal1
        System.out.println("cal1 与自身比较: " + result3);    // 输出0,时间相同
    }
}

运行结果:

cal1 与 cal2 比较: -1
cal2 与 cal1 比较: 1
cal1 与自身比较: 0

注意:比较前需确保两个 Calendar 的时区一致,否则可能出现逻辑错误。

与 Date 类互转

  • Calendar -> Date:calendar.getTime(),返回对应的 java.util.Date 对象(Date 类是更基础的时间类,常用于接口交互)。

  • Date -> Calendar:calendar.setTime(Date date),将 Date 对象的时间设置到 Calendar 中。

示例:

import java.util.Calendar;
import java.util.Date;

public class CalendarDateConversion {
    public static void main(String[] args) {
        // 1. 创建Calendar对象并设置时间(2023年10月1日)
        Calendar calendar = Calendar.getInstance();
        calendar.set(2023, Calendar.OCTOBER, 1);
        System.out.println("Calendar时间: " + calendar.getTime());  // 直接打印会调用toString()
        
        // 2. Calendar -> Date:使用getTime()方法
        Date dateFromCalendar = calendar.getTime();
        System.out.println("从Calendar转换得到的Date: " + dateFromCalendar);
        
        // 3. 创建一个新的Calendar对象,用于演示Date -> Calendar
        Calendar newCalendar = Calendar.getInstance();
        
        // 4. Date -> Calendar:使用setTime()方法
        newCalendar.setTime(dateFromCalendar);
        System.out.println("从Date转换得到的Calendar时间: " + 
                          newCalendar.get(Calendar.YEAR) + "年" +
                          (newCalendar.get(Calendar.MONTH) + 1) + "月" +  // 月份从0开始,需+1
                          newCalendar.get(Calendar.DATE) + "日");
    }
}

运行结果:

Calendar时间: Sun Oct 01 01:29:17 UTC 2023
从Calendar转换得到的Date: Sun Oct 01 01:29:17 UTC 2023
从Date转换得到的Calendar时间: 2023年10月1日

注意:Date 类本身不包含时区信息,互转时需依赖 Calendar 的时区。

其他实用方法

  • getActualMaximum(int field):获取指定字段的 “实际最大值”(根据当前时间动态计算)。例如:2024 年 2 月的最大天数是 29(闰年),3 月是 31(大月)。

  • clear(int field):清除指定字段的值(设为默认值,如 clear(Calendar.HOUR_OF_DAY) 会将小时设为 0)。

  • getTimeInMillis():返回当前 Calendar 对应的 “时间戳”(从 1970-01-01 00:00:00 UTC 到当前时间的毫秒数),便于时间戳计算。

示例:

import java.util.Calendar;

public class CalendarMethodsExample {
    public static void main(String[] args) {
        // 创建Calendar对象并设置为2024年2月
        Calendar calendar = Calendar.getInstance();
        calendar.set(2024, Calendar.FEBRUARY, 1); // 2月(月份参数为1,因为从0开始)
        
        // 1. getActualMaximum():获取指定字段的实际最大值
        int maxDaysInFeb2024 = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        System.out.println("2024年2月的最大天数: " + maxDaysInFeb2024); 
        // 2024是闰年,输出29
        
        // 切换到3月
        calendar.set(2024, Calendar.MARCH, 1);
        int maxDaysInMar2024 = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        System.out.println("2024年3月的最大天数: " + maxDaysInMar2024); 
        // 大月,输出31
        
        // 2. clear():清除指定字段的值
        System.out.println("清除前的月份: " + calendar.get(Calendar.MONTH));
        calendar.clear(Calendar.MONTH); // 清除小时字段(设为0)
        System.out.println("清除后的月份: " + calendar.get(Calendar.MONTH));
        // 输出0
        
        // 3. getTimeInMillis():获取时间戳(毫秒数)
        long timestamp = calendar.getTimeInMillis();
        System.out.println("当前Calendar的时间戳: " + timestamp + " 毫秒");
    }
}

运行结果:

2024年2月的最大天数: 29
2024年3月的最大天数: 31
清除前的月份: 2
清除后的月份: 0
当前Calendar的时间戳: 1704073557727 毫秒

注意:clear() 方法对 HOUR_OF_DAY(一天中的小时,24 小时制)、HOUR(12 小时制的小时)和 AM_PM(上午 / 下午标识)这三个字段的处理是相互独立的,且会应用一天中时间的解析规则。清除这三个字段中的任意一个,都不会重置此 Calendar 的“一天中的小时”值。若需重置小时值,请使用 set (Calendar.HOUR_OF_DAY, 0) 方法。

综合示例

以下示例将通过具体代码演示 Calendar 类中常用方法的用法:

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class CalendarDemo {
    public static void main(String[] args) {
        // 1. 获取 Calendar 实例(核心:不能直接 new,需通过工厂方法)
        // 方法1:获取当前系统时间的 Calendar 实例(默认时区、默认Locale)
        Calendar cal1 = Calendar.getInstance();
        System.out.println("1. 当前系统时间的 Calendar 实例:" + cal1);

        // 方法2:指定时区的 Calendar 实例(如:纽约时区)
        TimeZone nyTimeZone = TimeZone.getTimeZone("America/New_York");
        Calendar cal2 = Calendar.getInstance(nyTimeZone);
        System.out.println("2. 纽约时区的 Calendar 实例:" + cal2);


        // 2. 常用字段常量(理解字段含义,避免硬编码)
        // Calendar 用常量定义了所有时间字段,例如:
        // YEAR(年)、MONTH(月,注意:0=1月,11=12月)、DATE(日)、HOUR(12小时制)、HOUR_OF_DAY(24小时制)
        // MINUTE(分)、SECOND(秒)、MILLISECOND(毫秒)、DAY_OF_WEEK(星期,1=周日,2=周一,...,7=周六)
        System.out.println("\n3. 常用字段说明(以 cal1 为例):");
        System.out.println("   - 当前年份:" + cal1.get(Calendar.YEAR));
        System.out.println("   - 当前月份(0=1月):" + (cal1.get(Calendar.MONTH) + 1)); // 加1后符合日常习惯
        System.out.println("   - 当前日期:" + cal1.get(Calendar.DATE));
        System.out.println("   - 当前小时(24小时制):" + cal1.get(Calendar.HOUR_OF_DAY));
        System.out.println("   - 当前分钟:" + cal1.get(Calendar.MINUTE));
        System.out.println("   - 当前秒:" + cal1.get(Calendar.SECOND));
        System.out.println("   - 当前星期(1=周日):" + convertWeekToChinese(cal1.get(Calendar.DAY_OF_WEEK))); // 转换为中文显示


        // 3. 设置时间字段(set() 方法的3种用法)
        Calendar cal3 = Calendar.getInstance();
        System.out.println("\n4. 设置时间字段演示:");

        // 方法1:单个字段设置(参数:字段常量,值)
        cal3.set(Calendar.YEAR, 2024); // 设置年份为2024
        cal3.set(Calendar.MONTH, Calendar.JULY); // 设置月份为7月(Calendar.JULY=6,对应实际7月)
        cal3.set(Calendar.DATE, 15); // 设置日期为15日
        System.out.println("   - 单个字段设置后:" + formatCalendar(cal3)); // 格式化显示,便于阅读

        // 方法2:批量设置(年、月、日)
        cal3.set(2024, Calendar.AUGUST, 20); // 直接设置 年=2024,月=8月,日=20日
        System.out.println("   - 批量设置(年/月/日)后:" + formatCalendar(cal3));

        // 方法3:批量设置(年、月、日、时、分、秒)
        cal3.set(2024, Calendar.SEPTEMBER, 25, 14, 30, 0); // 年=2024,月=9月,日=25日,时=14,分=30,秒=0
        System.out.println("   - 批量设置(年/月/日/时/分/秒)后:" + formatCalendar(cal3));


        //4. 时间计算(add() 方法:基于当前字段增减)
        Calendar cal4 = Calendar.getInstance();
        cal4.set(2024, Calendar.OCTOBER, 5, 10, 0, 0); // 初始时间:2024-10-05 10:00:00
        System.out.println("\n5. 时间计算演示(初始时间:" + formatCalendar(cal4) + "):");

        // 增加 3 天
        cal4.add(Calendar.DATE, 3);
        System.out.println("   - 增加3天后:" + formatCalendar(cal4));

        // 减少 2 个月
        cal4.add(Calendar.MONTH, -2);
        System.out.println("   - 减少2个月后:" + formatCalendar(cal4));

        // 增加 1 小时 15 分钟
        cal4.add(Calendar.HOUR_OF_DAY, 1);
        cal4.add(Calendar.MINUTE, 15);
        System.out.println("   - 增加1小时15分钟后:" + formatCalendar(cal4));


        // 5. 时间比较(compareTo() 方法)
        // 初始化两个 Calendar 对象
        Calendar calA = Calendar.getInstance();
        calA.set(2024, Calendar.NOVEMBER, 10); // 时间A:2024-11-10
        Calendar calB = Calendar.getInstance();
        calB.set(2024, Calendar.DECEMBER, 10); // 时间B:2024-12-10

        System.out.println("\n6. 时间比较演示:");
        System.out.println("   - 时间A:" + formatCalendar(calA) + ",时间B:" + formatCalendar(calB));
        
        int compareResult = calA.compareTo(calB);
        if (compareResult < 0) {
            System.out.println("   - 比较结果:时间A < 时间B");
        } else if (compareResult > 0) {
            System.out.println("   - 比较结果:时间A > 时间B");
        } else {
            System.out.println("   - 比较结果:时间A = 时间B");
        }


        // 6. 转换方法(与 Date 类互转)
        Calendar cal5 = Calendar.getInstance();
        System.out.println("\n7. 与 Date 类互转演示:");

        // Calendar -> Date:通过 getTime() 方法
        Date dateFromCal = cal5.getTime();
        System.out.println("   - Calendar 转 Date:" + dateFromCal);

        // Date -> Calendar:通过 setTime() 方法
        Date targetDate = new Date(); // 假设一个目标 Date 对象
        cal5.setTime(targetDate);
        System.out.println("   - Date 转 Calendar:" + formatCalendar(cal5));


        // 7. 其他实用方法
        Calendar cal6 = Calendar.getInstance();
        cal6.set(2024, Calendar.FEBRUARY, 28); // 2024年2月28日(2024是闰年)
        System.out.println("\n8. 其他实用方法演示:");

        // 获取当月最大天数(判断闰年/大月/小月)
        int maxDaysInMonth = cal6.getActualMaximum(Calendar.DATE);
        System.out.println("   - 2024年2月最大天数:" + maxDaysInMonth); // 输出29(闰年)

        // 获取当年最大天数
        int maxDaysInYear = cal6.getActualMaximum(Calendar.DAY_OF_YEAR);
        System.out.println("   - 2024年最大天数:" + maxDaysInYear); // 输出366(闰年)

        // 重置时间字段(将时分秒毫秒设为0)
        cal6.clear(Calendar.HOUR_OF_DAY);
        cal6.clear(Calendar.MINUTE);
        cal6.clear(Calendar.SECOND);
        cal6.clear(Calendar.MILLISECOND);
        System.out.println("   - 重置时分秒后:" + formatCalendar(cal6));
    }


    // 将 Calendar 格式化为 "yyyy-MM-dd HH:mm:ss" 字符串
    private static String formatCalendar(Calendar calendar) {
        if (calendar == null) {
            return null;
        }
        // 获取各字段值
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1; // 月份0~11,加1后为1~12
        int day = calendar.get(Calendar.DATE);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);

        // 格式化(补0,确保两位数)
        return String.format("%d-%02d-%02d %02d:%02d:%02d",
                year, month, day, hour, minute, second);
    }


    // 将星期字段(1~7)转换为中文(1=周日,2=周一...7=周六)
    private static String convertWeekToChinese(int week) {
        String[] weekNames = {"", "周日", "周一", "周二", "周三", "周四", "周五", "周六"};
        return weekNames[week]; // 索引对应:1→周日,2→周一...7→周六
    }
}

运行结果:

1. 当前系统时间的 Calendar 实例:java.util.GregorianCalendar[time=1756461588689,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Etc/UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2025,MONTH=7,WEEK_OF_YEAR=35,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=241,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=9,HOUR_OF_DAY=9,MINUTE=59,SECOND=48,MILLISECOND=689,ZONE_OFFSET=0,DST_OFFSET=0]
2. 纽约时区的 Calendar 实例:java.util.GregorianCalendar[time=1756461588739,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/New_York",offset=-18000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2025,MONTH=7,WEEK_OF_YEAR=35,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=241,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=5,HOUR_OF_DAY=5,MINUTE=59,SECOND=48,MILLISECOND=739,ZONE_OFFSET=-18000000,DST_OFFSET=3600000]
3. 常用字段说明(以 cal1 为例):
   - 当前年份:2025
   - 当前月份(0=1月):8
   - 当前日期:29
   - 当前小时(24小时制):9
   - 当前分钟:59
   - 当前秒:48
   - 当前星期(1=周日):周五
4. 设置时间字段演示:
   - 单个字段设置后:2024-07-15 09:59:48
   - 批量设置(年/月/日)后:2024-08-20 09:59:48
   - 批量设置(年/月/日/时/分/秒)后:2024-09-25 14:30:00
5. 时间计算演示(初始时间:2024-10-05 10:00:00):
   - 增加3天后:2024-10-08 10:00:00
   - 减少2个月后:2024-08-08 10:00:00
   - 增加1小时15分钟后:2024-08-08 11:15:00
6. 时间比较演示:
   - 时间A:2024-11-10 09:59:48,时间B:2024-12-10 09:59:48
   - 比较结果:时间A < 时间B
7. 与 Date 类互转演示:
   - Calendar 转 Date:Fri Aug 29 09:59:48 UTC 2025
   - Date 转 Calendar:2025-08-29 09:59:48
8. 其他实用方法演示:
   - 2024年2月最大天数:29
   - 2024年最大天数:366
   - 重置时分秒后:2024-02-28 09:00:00

注意事项

  • 月份取值问题:Calendar.MONTH 是从 0 开始(即 0=1 月,11=12 月),使用时需加 1 才能符合日常习惯,避免踩坑。

  • 星期取值问题:Calendar.DAY_OF_WEEK 是从 1 开始(1 = 周日,2 = 周一,...7 = 周六),需注意与实际星期的对应关系。

  • 时区问题:默认 Calendar 使用系统时区,跨时区场景需显式指定时区(如 TimeZone.getTimeZone("Asia/Shanghai"))。

  • 替代方案:Java 8 及以后推荐使用 java.time 包(如 LocalDateTime、ZonedDateTime),API 更简洁、线程安全,但 Calendar 在老项目中仍广泛使用,需掌握其用法。

总结

Calendar 虽然比 Date 提供了更丰富的日期字段操作,但仍存在设计缺陷。JDK 1.8 之后推荐使用 java.time 包(如 LocalDateTime、ZonedDateTime),这些类线程安全、API 简洁,且避免了 Calendar 的诸多问题。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号