在 Java 中,日期和时间的处理是常见的任务之一。为了更灵活地处理日期和时间,Java 提供了
java.util.Calendar
类,它提供了一种面向对象的方式来操作日期和时间。本篇博客将详细介绍Calendar
类的使用方法,特别是面向基础小白的读者。
目录
什么是 Calendar 类?
Calendar
类是 Java 中用于处理日期和时间的抽象类。它提供了一种独立于特定日历系统的方式来处理日期和时间。通过 Calendar
类,你可以执行许多常见的日期和时间操作,如计算日期差异、添加或减去时间单位等。
常量字段
Calendar类的常量字段是非常重要的参数,在set()、add()、get()方法中都会用到。
Calendar类中常用的常量字段如下:
常量字段 | 说明 |
---|---|
Calendar.ERA | ERA表示纪元,只能为0或1。0表示BC(“before Christ”,即公元前);1表示AD(拉丁语“Anno Domini”,即公元)。 |
Calendar.YEAR | 表示年份 |
Calendar.MONTH | 表示月份,需要加1,因为0表示1月,1表示2月,以此类推 |
Calendar.HOUR | 小时(12小时制) |
Calendar.HOUR_OF_DAY | 一天中第几个小时(24小时制) |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.MILLISECOND | 毫秒 |
Calendar.DAY_OF_YEAR | 一年中第几天 |
DAY_OF_MONTH | 一月中第几天 |
DAY_OF_WEEK | 一周中第几天,注意,周日是1,周一是2,... |
Calendar.DATE | 一月中第几天,同DAY_OF_MONTH的值是一样的 |
Calendar.DAY_OF_WEEK_IN_MONTH | 当月内一周中的某天的序号 |
Calendar.WEEK_OF_YEAR | 一年中的星期数,即本年中第几个星期 |
Calendar.WEEK_OF_MONTH | 当前月中的星期数,即本月中第几个星期 |
Calendar.DAY_OF_WEEK_IN_MONTH | 当前月中的第几个星期 |
Calendar.AM_PM | AM_PM是在中午之前还是在中午之后,在中午12点之前返回0,在中午12点(包括12点)之后返回1 |
Calendar.JANUARY | 1月 |
Calendar.FEBRUARY | 2月 |
Calendar.MARCH | 3月 |
Calendar.APRIL | 4月 |
Calendar.MAY | 5月 |
Calendar.JUNE | 6月 |
Calendar.JULY | 7月 |
Calendar.AUGUST | 8月 |
Calendar.SEPTEMBER | 9月 |
Calendar.OCTOBER | 10月 |
Calendar.NOVEMBER | 11月 |
Calendar.DECEMBER | 12月 |
获取Calendar对象
不需要通过new来创建Calendar对象,而是通过Calendar类的getInstance()静态方法。代码如下:
// 获取日历对象
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());// 将Date对象设置到Calendar对象中
常用方法
常用方法就是get、set和add方法,它们的API说明如下:
方法 | 说明 |
---|---|
int get(int field) | 用于获取日历字段的值,返回值是int类型,其中field参数的值可以是Calendar.YEAR 、Calendar.MONTH 等常量字段。 |
void set(int field, int value) | 将给定的日历字段设置为指定的值。 |
void set(int year, int month, int date) | 设置Calendar中的年、月、日字段 |
void set(int year, int month, int date, int hourOfDay, int minute) | 设置Calendar中的年、月、日、时、分字段 |
void set(int year, int month, int date, int hourOfDay, int minute, int second) | 设置Calendar中的年、月、日、时、分、秒字段 |
void add(int field, int amount) | 可以对指定的一个日历字段进行整数的偏移,比如在今年的日期基础上加上一年,就是明年这个时候的日期。其中amount可以为负数,可以表示如去年、上个月等。 |
其他方法
方法 | 说明 |
---|---|
boolean after(Object when) | 使用例如:c4.after(c3) ,判断c4的日期是否在c3的日期之后。传入的参数必须是Calendar对象。 |
boolean before(Object when) | 使用例如:c3.before(c4) ,判断c3的日期是否在c4的日期之前。传入的参数必须是Calendar对象。 |
boolean equals(Object obj) | 使用例如:c3.equals(c4) ,判断c3的日期是否等于c4所表示的日期。传入的参数必须是Calendar对象。 |
int compareTo(Calendar anotherCalendar) | 使用例如:c3.compareTo(c4) ,如果比较c3和c4,如果c3<c4则返回-1,如果c3==c4则返回0,c3>c4则返回1。 |
int getActualMaximum(int field) | 返回指定日历字段可能具有的最大值,例如:c5.getActualMaximum(Calendar.DAY_OF_MONTH) ,获取本月最后一天的号数。 |
int getActualMinimum(int field) | 返回指定日历字段可能具有的最小值,例如:c5.getActualMinimum(Calendar.DAY_OF_MONTH) ,获取本月第一天的号数。 |
long getTimeInMillis() | 返回此日历对象所表示的毫秒值。 |
int getFirstDayOfWeek() | 获取一周的第一天是什么。 |
TimeZone getTimeZone() | 获取时区。 |
Calendar对象与Date对象的相互转换
- Date对象转换成Calendar对象
// Date对象转换成Calendar对象
Calendar c2 = Calendar.getInstance();
c2.setTime(new Date());
- Calendar对象转换成Date对象
Calendar c2 = Calendar.getInstance();
// Calendar对象转换成Date对象
Date c2Date = c2.getTime();// 类似于new Date()
实例
Calendar类的实例代码如下:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* @描述: Calendar 测试
* @Author: Duys
**/
@RunWith(SpringRunner.class)
@SpringBootTest
public class CalendarTest {
@Test
public void test01() {
// 获取日历对象
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
// 获取日期字段值
System.out.println("纪元,公元前还是公元 >>> " + calendar.get(Calendar.ERA));// 纪元,公元前还是公元
System.out.println("年份 >>> " + calendar.get(Calendar.YEAR));// 年份
System.out.println("月份,需要加1,因为0表示1月,1表示2月,以此类推 >>> " + calendar.get(Calendar.MONTH));// 月份,需要加1,因为0表示1月,1表示2月,以此类推
System.out.println("小时(12小时制) >>> " + calendar.get(Calendar.HOUR));// 小时(12小时制)
System.out.println("一天中第几个小时(24小时制) >>> " + calendar.get(Calendar.HOUR_OF_DAY));// 一天中第几个小时(24小时制)
System.out.println("分钟 >>> " + calendar.get(Calendar.MINUTE));// 分钟
System.out.println("秒 >>> " + calendar.get(Calendar.SECOND));// 秒
System.out.println("毫秒 >>> " + calendar.get(Calendar.MILLISECOND));// 毫秒
System.out.println("一年中第几天 >>> " + calendar.get(Calendar.DAY_OF_YEAR));// 一年中第几天
System.out.println("一月中第几天 >>> " + calendar.get(Calendar.DAY_OF_MONTH));// 一月中第几天
System.out.println("一周中第几天,注意,周日是1,周一是2,... >>> " + calendar.get(Calendar.DAY_OF_WEEK));// 一周中第几天,注意,周日是1,周一是2,...
System.out.println("一月中第几天,同DAY_OF_MONTH的值是一样的 >>> " + calendar.get(Calendar.DATE));// 一月中第几天,同DAY_OF_MONTH的值是一样的
System.out.println("当月内一周中的某天的序号 >>> " + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));// 当月内一周中的某天的序号
System.out.println("Calendar.WEEK_OF_YEAR=" + calendar.get(Calendar.WEEK_OF_YEAR));// 一年中的星期数,即本年中第几个星期
System.out.println("Calendar.WEEK_OF_MONTH=" + calendar.get(Calendar.WEEK_OF_MONTH));// 当前月中的星期数,即本月中第几个星期
System.out.println("当前月中的第几个星期 >>> " + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));// 当前月中的第几个星期
System.out.println("Calendar.AM_PM=" + calendar.get(Calendar.AM_PM));// AM_PM是在中午之前还是在中午之后,在中午12点之前返回0,在中午12点(包括12点)之后返回1
System.out.println("1月 >>> " + Calendar.JANUARY);// 1月
System.out.println("2月 >>> " + Calendar.FEBRUARY);// 2月
System.out.println("3月 >>> " + Calendar.MARCH);// 3月
System.out.println("4月 >>> " + Calendar.APRIL);// 4月
System.out.println("5月 >>> " + Calendar.MAY);// 5月
System.out.println("6月 >>> " + Calendar.JUNE);// 6月
System.out.println("7月 >>> " + Calendar.JULY);// 7月
System.out.println("8月 >>> " + Calendar.AUGUST);// 8月
System.out.println("9月 >>> " + Calendar.SEPTEMBER);// 9月
System.out.println("10月 >>> " + Calendar.OCTOBER);// 10月
System.out.println("11月 >>> " + Calendar.NOVEMBER);// 11月
System.out.println("12月 >>> " + Calendar.DECEMBER);// 12月
// 设置日期字段值
calendar.set(2021, Calendar.JUNE, 3);// 设定指定年月日,其中Calendar.JUNE为5实际上是六月,所以0表示1月,1表示2月,以此类推
System.out.println("设定指定年月日 >>> " + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(calendar.getTime()));
calendar.set(2021, Calendar.FEBRUARY, 3, 23, 55);// 设置年月日时分
System.out.println("设置年月日时分 >>> " + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(calendar.getTime()));
calendar.set(2021, Calendar.MARCH, 3, 23, 55, 30);// 设置年月日时分秒
calendar.set(Calendar.YEAR, 2005);// 单独设置日历字段,设置年份
calendar.set(Calendar.MONTH, 7);// 单独设置日历字段,设置月份,注意0代表1月
calendar.set(Calendar.DATE, 11);// 单独设置日历字段,设置天数
calendar.set(Calendar.HOUR_OF_DAY, 22);// 单独设置日历字段,设置小时
calendar.set(Calendar.MINUTE, 45);// 单独设置日历字段,设置分钟
calendar.set(Calendar.SECOND, 30);// 单独设置日历字段,设置秒
calendar.set(Calendar.MILLISECOND, 235);// 单独设置日历字段,设置毫秒
System.out.println("单独设置日历字段 >>> " + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(calendar.getTime()));
// 计算日期字段值,既可以为正数,又可以为负数
calendar.add(Calendar.YEAR, 2);// 在当前日期的基础上添加年份,后年
System.out.println("在当前日期的基础上添加年份,后年 >>>> " + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(calendar.getTime()));
calendar.add(Calendar.YEAR, -1);// 在当前日期的基础上减去年份,昨年
System.out.println("在当前日期的基础上减去年份,昨年 >>>> " + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(calendar.getTime()));
calendar.add(Calendar.MONTH, 1);// 添加月份,下个月
calendar.add(Calendar.MONTH, -1);// 添加月份,上个月
calendar.add(Calendar.DATE, 2);// 添加天数,后天
calendar.add(Calendar.DATE, -1);// 添加天数,昨天
calendar.add(Calendar.HOUR_OF_DAY, 1);// 添加小时,下一个小时
calendar.add(Calendar.HOUR_OF_DAY, -1);// 添加小时,上一个小时
calendar.add(Calendar.MINUTE, 20);// 添加分钟,下20分钟
calendar.add(Calendar.MINUTE, -20);// 添加分钟,上20分钟
calendar.add(Calendar.SECOND, 30);// 添加秒数,下30秒
calendar.add(Calendar.SECOND, -30);// 添加秒数,上30秒
calendar.add(Calendar.DATE, 7);// 添加周数,即添加7天,下一周
calendar.add(Calendar.DATE, -7);// 添加周数,上一周
System.out.println("添加 >>>> " + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(calendar.getTime()));
// Calendar对象与Date对象的相互转换
// Date对象转换成Calendar对象
Calendar c2 = Calendar.getInstance();
c2.setTime(new Date());
// Calendar对象转换成Date对象
Date c2Date = c2.getTime();// 类似于new Date()
// 其他方法,如after、before
Calendar c3 = Calendar.getInstance();
c3.set(Calendar.YEAR, 2005);
Calendar c4 = Calendar.getInstance();
boolean after = c4.after(c3);// 判断c4的日期是否在c3之后
System.out.println(after);
boolean before = c3.before(c4);
System.out.println(before);// 判断c3的日期是否在c4之前
boolean equals = c3.equals(c4);// 判断c3的日期是否等于c4
System.out.println(equals);
int result = c3.compareTo(c4);// 如果比较c3和c4,如果c3<c4则返回-1,如果c3==c4则返回0,c3>c4则返回1
System.out.println(result);
Calendar c5 = Calendar.getInstance();
c5.clear();
c5.set(Calendar.YEAR, 2017);
c5.set(Calendar.MONTH, 1);
c5.set(Calendar.DAY_OF_MONTH, 30);
System.out.println(c5.getTime());
int lastDay = c5.getActualMaximum(Calendar.DAY_OF_MONTH);// 获取本月最后一天的号数
int firstDay = c5.getActualMinimum(Calendar.DAY_OF_MONTH);// 获取本月第一天的号数
System.out.println("本月最后一天:" + lastDay + ", 本月第一天:" + firstDay);
// 获取毫秒数
long timeInMillis = calendar.getTimeInMillis();// 返回此Calendar的时间值,以毫秒为单位
int firstDayOfWeek = calendar.getFirstDayOfWeek();// 获取一个星期中的第一天
System.out.println("获取一个星期中的第一天 >>> " + firstDayOfWeek);
TimeZone timeZone = calendar.getTimeZone();// 获取时区
System.out.println("获取时区 >>> " + timeZone.getDisplayName());
}
}
日志输出:
纪元,公元前还是公元 >>> 1
年份 >>> 2023
月份,需要加1,因为0表示1月,1表示2月,以此类推 >>> 10
小时(12小时制) >>> 11
一天中第几个小时(24小时制) >>> 11
分钟 >>> 29
秒 >>> 8
毫秒 >>> 512
一年中第几天 >>> 332
一月中第几天 >>> 28
一周中第几天,注意,周日是1,周一是2,... >>> 3
一月中第几天,同DAY_OF_MONTH的值是一样的 >>> 28
当月内一周中的某天的序号 >>> 4
Calendar.WEEK_OF_YEAR=48
Calendar.WEEK_OF_MONTH=5
当前月中的第几个星期 >>> 4
Calendar.AM_PM=0
1月 >>> 0
2月 >>> 1
3月 >>> 2
4月 >>> 3
5月 >>> 4
6月 >>> 5
7月 >>> 6
8月 >>> 7
9月 >>> 8
10月 >>> 9
11月 >>> 10
12月 >>> 11
设定指定年月日 >>> 2021-06-03 11:29:08
设置年月日时分 >>> 2021-02-03 11:55:08
单独设置日历字段 >>> 2005-08-11 10:45:30
在当前日期的基础上添加年份,后年 >>>> 2007-08-11 10:45:30
在当前日期的基础上减去年份,昨年 >>>> 2006-08-11 10:45:30
添加 >>>> 2006-08-12 10:45:30
true
true
false
-1
Thu Mar 02 00:00:00 CST 2017
本月最后一天:31, 本月第一天:1
获取一个星期中的第一天 >>> 1
获取时区 >>> 中国标准时间
根据参数获取时间范围
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @描述:
* @Author: Duys
**/
@Slf4j
@Component
public class ReqParamUtil {
/**
* @描述: 根据day, hour, min获取时间段
**/
public static Map<String, String> requestTime(int day, int hour, int min) {
/**请求时间**/
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, -day);
/* HOUR_OF_DAY 指示一天中小时 */
calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - hour);
/* MINUTE 指示一天中的某分 */
calendar.set(Calendar.MINUTE, (calendar.get(Calendar.MINUTE) - min));
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.info("beginTime: " + df.format(calendar.getTime()));
log.info("endTime: " + df.format(new Date()));
Map<String, String> map = new HashMap<>(2);
map.put("beginTime", df.format(calendar.getTime()));
map.put("endTime", df.format(new Date()));
return map;
}
}
Calendar 类使用的注意事项
在使用 Calendar
类时,需要注意以下一些使用注意事项,以确保日期和时间的操作正确、安全,避免潜在的问题:
- 月份从0开始:Calendar 类中的月份是从0开始的,即0表示一月,1表示二月,以此类推,11表示十二月。这与人们通常的月份表示方式不同,要格外注意月份的设置和获取。
- 时区和区域设置:Calendar 类的行为受到默认时区和区域设置的影响。在涉及跨时区操作时,应该特别注意时区的设置,以避免出现意外的偏移。
- 线程安全性:Calendar 类不是线程安全的,因此在多线程环境下使用时需要额外小心。通常情况下,最好将 Calendar 对象限定在单个线程内,或使用线程安全的日期和时间库,如 java.time(Java 8及更高版本引入)。
- 日期和时间范围:Calendar 类的日期和时间范围受到系统实现的限制。例如,一些操作系统可能无法表示某些日期(特别是在1970年之前或2038年之后),因此在处理非常早期或非常晚期的日期时要格外小心。
- 月份的天数:要谨慎处理月份的天数,不同月份的天数是不同的,如果不考虑这一点可能会导致日期计算的错误。
- 避免魔法数值:在代码中避免硬编码日期和时间的数值,而是使用 Calendar 常量和方法来表示日期和时间部分,以提高可读性和可维护性。
- 异常处理:在进行日期和时间计算时,要格外小心异常情况,如日期不存在、时间超出范围等。在必要的情况下,要使用异常处理机制来处理这些情况,以确保程序的稳定性。
- 不建议使用:Java 8 引入了新的日期和时间 API(java.time 包),如 LocalDate、LocalTime 和 ZonedDateTime 等类,这些类更简单、更安全,推荐在新项目中使用。Calendar 类在新 API 出现后逐渐被淘汰,不建议在新项目中使用。
总结
Calendar 类是 Java 中用于处理日期和时间的重要工具之一。通过创建 Calendar 对象,你可以获取和设置日期和时间的各个部分,执行日期和时间的计算,以及计算日期差异。然而,需要注意的是,Calendar 类的使用可能较为繁琐,而且并不是线程安全的。在 Java 8 之后,引入了新的日期和时间 API(java.time 包),提供了更好的日期和时间处理方式,推荐在新项目中使用。