概述
在Java开发中,日期处理是极为常见的操作。Date和Calendar作为早期JDK提供的日期类,虽然在现代开发中已被java.time包取代,但在维护遗留系统或理解底层原理时仍具有重要价值。本文将系统讲解这两个类的核心用法与相互转换机制。
核心类解析
Date类
Date类用于表示某个特定的时刻,精度可达毫秒级别。在JDK 1.1之前的版本中,Date类承担了日期解析和格式化的双重职能。然而,由于其API设计不利于国际化支持,从JDK 1.1开始,官方推荐使用Calendar类进行日期字段的转换操作,同时将Date类中相关的方法标记为已废弃。
Calendar类
Calendar是一个抽象基类,它在时间戳与日历字段(如年、月、日、时、分、秒等)之间提供了一套完整的转换机制。该类还支持对日历字段进行各种算术运算,例如计算指定日期若干天后的日期。Calendar内部采用毫秒值表示时间点,该值表示自1970年1月1日00:00:00 GMT(格里高利历元)以来的毫秒数。
简而言之,Date主要用于存储日期数据,而Calendar则专注于日期相关的计算与操作。
常用操作详解
获取当前时间
以下代码展示了如何获取系统的当前时间:
// 使用Date类获取当前时间
Date currentDate = new Date();
System.out.println(currentDate.toString());
// 使用Calendar类获取当前时间
Calendar currentCalendar = Calendar.getInstance();
System.out.println(currentCalendar.toString());
从输出结果可以清晰看出两者的本质区别:Date输出的是易于阅读的日期字符串,而Calendar则提供了完整的时间字段信息。
创建指定日期
在实际开发中,经常需要根据字符串创建特定日期对象:
// 定义日期格式解析器
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date specifiedDate = null;
try {
// 将字符串解析为Date对象
specifiedDate = dateFormatter.parse("2020-05-20 14:30:00");
} catch (ParseException e) {
e.printStackTrace();
}
// 将Date转换为Calendar进行操作
Calendar calendarFromDate = Calendar.getInstance();
calendarFromDate.setTime(specifiedDate);
需要特别注意的是,格式字符串中的月份和分钟必须使用小写的"mm",而小时部分的大小写不影响解析结果。
类型相互转换
Date与Calendar之间的转换是日常开发中的高频操作:
// 初始化Date对象
Date originalDate = new Date();
// Date转Calendar
Calendar calendarInstance = Calendar.getInstance();
calendarInstance.setTime(originalDate);
// Calendar转Date
Date convertedDate = calendarInstance.getTime();
转换的核心在于Calendar类提供的setTime()和getTime()方法。setTime()接收Date类型参数并填充Calendar的各个字段,getTime()则从Calendar字段组装并返回Date对象。值得注意的是,Date类本身并未提供直接转换到Calendar的方法。
日期格式化输出
将Date对象格式化为指定格式的字符串:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String formattedString = formatter.format(new Date());
日期算术运算
使用Calendar的add()方法可以对日期进行加减操作:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 获取当前时间并输出
Date today = new Date();
System.out.println(sdf.format(today));
// 创建Calendar并进行月份加一操作
Calendar calendar = Calendar.getInstance();
calendar.setTime(today);
calendar.add(Calendar.MONTH, 1);
Date nextMonth = calendar.getTime();
System.out.println(sdf.format(nextMonth));
add()方法的官方定义为:根据日历规则,为指定的日历字段添加或减去指定的时间量。
参数说明:
- field:指定要操作的时间字段,支持的常量包括YEAR(年)、MONTH(月)、DAY_OF_MONTH(日)、HOUR(小时)、MINUTE(分钟)、SECOND(秒)等
- amount:正数表示在当前时间基础上增加,负数表示减少
Calendar类提供了丰富的字段常量,不仅限于时间单位,还包括WEEK_OF_YEAR、DAY_OF_YEAR等周期性字段。
获取当天结束时刻
以下工具方法可用于获取当天23:59:59.000这一精确时刻:
public static Date obtainDayEndTime() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
日期工具类实现
以下是一个实用的日期处理工具类,封装了常见的日期操作需求:
package com.example.core.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class DateTimeUtil {
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String COMPACT_FORMAT = "yyyyMMdd";
public static final String TIME_ONLY = "HH:mm";
private static final Logger log = LoggerFactory.getLogger(DateTimeUtil.class);
/**
* 时间偏移计算
* @param baseDate 基础时间
* @param direction true为正向偏移,false为反向
* @param unit 时间单位
* @param delta 偏移量
* @return 偏移后的时间
*/
public static Date shiftTime(Date baseDate, boolean direction, TimeUnit unit, int delta) {
if (baseDate == null || delta == 0) {
return baseDate;
}
Calendar cal = Calendar.getInstance();
cal.setTime(baseDate);
cal.add(unit.getField(), direction ? delta : -delta);
return cal.getTime();
}
/**
* 日期单位枚举
*/
public enum TimeUnit {
YEAR(Calendar.YEAR),
MONTH(Calendar.MONTH),
DAY(Calendar.DAY_OF_YEAR),
HOUR(Calendar.HOUR_OF_DAY),
MINUTE(Calendar.MINUTE),
SECOND(Calendar.SECOND);
private final int field;
TimeUnit(int field) {
this.field = field;
}
public int getField() {
return field;
}
}
/**
* 将Date格式化为指定格式的字符串
*/
public static String toString(Date target, String pattern) {
if (target == null) {
return null;
}
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.format(target);
} catch (Exception e) {
log.error("日期格式化失败: {}", target, e);
return null;
}
}
/**
* 将字符串解析为Date对象
*/
public static Date fromString(String source, String pattern) {
if (source == null || source.trim().isEmpty()) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
try {
return sdf.parse(source.trim());
} catch (Exception e) {
log.warn("日期解析异常, source={}, pattern={}", source, pattern, e);
return null;
}
}
/**
* 获取指定时间在当天的指定时分秒时刻
*/
public static Date getSpecificTimeOfDay(Date base, int hour, int minute, int second) {
if (base == null) {
return null;
}
Calendar cal = Calendar.getInstance();
cal.setTime(base);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, second);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
/**
* 获取日期中的小时数(24小时制)
*/
public static int extractHour(Date target) {
Calendar cal = Calendar.getInstance();
cal.setTime(target);
return cal.get(Calendar.HOUR_OF_DAY);
}
}