跳到主要内容

Java 遗留日期类

在Java 8引入新的日期时间API之前,Java提供了一组用于处理日期和时间的类,主要包括java.util.Datejava.util.Calendarjava.text.SimpleDateFormat。虽然这些类现在被认为是"遗留"API,但在许多现有代码中仍然广泛使用,因此了解它们的工作原理对于Java开发人员来说依然重要。

Date类

java.util.Date是Java最早用于表示日期和时间的类,创建于JDK 1.0。

基本用法

java
// 创建代表当前时间的Date对象
Date currentDate = new Date();
System.out.println("当前时间: " + currentDate);

// 创建特定时间点的Date对象(不推荐,已过时)
Date specificDate = new Date(121, 0, 15); // 2021年1月15日
System.out.println("特定日期: " + specificDate);

// 获取时间戳(毫秒)
long timeInMillis = currentDate.getTime();
System.out.println("时间戳(毫秒): " + timeInMillis);

// 从时间戳创建Date
Date dateFromTimestamp = new Date(timeInMillis);
System.out.println("从时间戳创建的日期: " + dateFromTimestamp);

输出示例:

当前时间: Mon Jul 11 15:30:45 CST 2023
特定日期: Fri Jan 15 00:00:00 CST 2021
时间戳(毫秒): 1689063045123
从时间戳创建的日期: Mon Jul 11 15:30:45 CST 2023
注意

Date类有许多过时的方法(如getYear()getMonth()等)不应再使用。此外,Date构造函数中的年份参数需要减去1900,月份从0开始(即0表示一月),这些设计容易导致错误。

Calendar类

java.util.Calendar是在JDK 1.1中引入的,目的是解决Date类的一些局限性。它是一个抽象类,提供了更丰富的日期操作功能。

基本用法

java
// 获取Calendar实例
Calendar calendar = Calendar.getInstance();

// 设置日期
calendar.set(2023, Calendar.JULY, 11); // 2023年7月11日

// 获取日期信息
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 月份从0开始,所以要加1
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println("日期: " + year + "-" + month + "-" + day);

// 日期计算
Calendar futureDate = Calendar.getInstance();
futureDate.add(Calendar.DAY_OF_MONTH, 30); // 30天后
System.out.println("30天后: " + futureDate.getTime());

// Calendar转Date
Date date = calendar.getTime();
System.out.println("Calendar转Date: " + date);

输出示例:

日期: 2023-7-11
30天后: Thu Aug 10 15:30:45 CST 2023
Calendar转Date: Tue Jul 11 15:30:45 CST 2023

时区处理

java
// 使用特定时区的Calendar
Calendar japanCalendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"));
System.out.println("东京时间: " + japanCalendar.getTime());

// 设置时区
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println("纽约时间: " + calendar.getTime());

SimpleDateFormat类

java.text.SimpleDateFormat用于格式化和解析日期字符串,是DateFormat的一个具体子类。

基本用法

java
// 创建日期格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

// 格式化日期
Date now = new Date();
String formattedDate = sdf.format(now);
System.out.println("格式化后的日期: " + formattedDate);

// 解析日期字符串
try {
String dateStr = "2023-07-11 10:30:00";
Date parsedDate = sdf.parse(dateStr);
System.out.println("解析后的日期: " + parsedDate);
} catch (ParseException e) {
e.printStackTrace();
}

输出示例:

格式化后的日期: 2023-07-11 15:30:45
解析后的日期: Tue Jul 11 10:30:00 CST 2023

常用格式模式

符号描述示例
y2023
M7或07或Jul
d11或11
H小时(24小时制)15
h小时(12小时制)3
m分钟30
s45
S毫秒678
E星期Tue或Tuesday
a上午/下午AM或PM
z时区CST或GMT+8:00

格式化示例

java
Date now = new Date();

// 不同的日期格式
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
System.out.println(sdf1.format(now)); // 2023/07/11

SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
System.out.println(sdf2.format(now)); // 2023年07月11日

SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
System.out.println(sdf3.format(now)); // 2023-07-11 15:30:45.678

SimpleDateFormat sdf4 = new SimpleDateFormat("E, MMMM dd yyyy");
System.out.println(sdf4.format(now)); // Tue, July 11 2023
警告

SimpleDateFormat不是线程安全的,如果在多线程环境中使用同一个实例可能会导致意外结果。在多线程环境中,应该为每个线程创建单独的实例,或使用线程安全的替代方案,如Java 8中的DateTimeFormatter

遗留日期API的问题

Java的遗留日期API存在许多问题,这也是为什么在Java 8中引入了全新的日期时间API:

  1. 设计不直观:月份从0开始(0=一月),年份需要加上1900。
  2. 可变性DateCalendar对象是可变的,这在多线程环境中可能导致问题。
  3. 格式化不灵活:日期格式化和解析的支持有限。
  4. 线程安全性SimpleDateFormat不是线程安全的。
  5. API不一致:方法命名和行为不一致。
  6. 时区处理复杂:处理不同时区的日期时间比较繁琐。

实际应用案例

案例1:计算两个日期之间的天数

java
public static long daysBetween(Date startDate, Date endDate) {
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
// 重置时分秒为0
startCal.set(Calendar.HOUR_OF_DAY, 0);
startCal.set(Calendar.MINUTE, 0);
startCal.set(Calendar.SECOND, 0);
startCal.set(Calendar.MILLISECOND, 0);

Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
// 重置时分秒为0
endCal.set(Calendar.HOUR_OF_DAY, 0);
endCal.set(Calendar.MINUTE, 0);
endCal.set(Calendar.SECOND, 0);
endCal.set(Calendar.MILLISECOND, 0);

// 计算毫秒差,然后转换为天数
long diffMillis = endCal.getTimeInMillis() - startCal.getTimeInMillis();
return diffMillis / (24 * 60 * 60 * 1000);
}

// 使用示例
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date start = sdf.parse("2023-07-01");
Date end = sdf.parse("2023-07-11");
long days = daysBetween(start, end);
System.out.println("两个日期之间的天数: " + days);
} catch (ParseException e) {
e.printStackTrace();
}

输出:

两个日期之间的天数: 10

案例2:生成指定格式的日期报表

java
public static void generateDateReport(Date startDate, int days) {
SimpleDateFormat dateSdf = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat daySdf = new SimpleDateFormat("EEEE", Locale.ENGLISH);

Calendar cal = Calendar.getInstance();
cal.setTime(startDate);

System.out.println("日期报表");
System.out.println("--------------------");
for (int i = 0; i < days; i++) {
String dateStr = dateSdf.format(cal.getTime());
String dayOfWeek = daySdf.format(cal.getTime());
System.out.println(dateStr + " (" + dayOfWeek + ")");
cal.add(Calendar.DAY_OF_MONTH, 1);
}
System.out.println("--------------------");
}

// 使用示例
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date start = sdf.parse("2023-07-10");
generateDateReport(start, 5);
} catch (ParseException e) {
e.printStackTrace();
}

输出:

日期报表
--------------------
2023-07-10 (Monday)
2023-07-11 (Tuesday)
2023-07-12 (Wednesday)
2023-07-13 (Thursday)
2023-07-14 (Friday)
--------------------

总结

Java的遗留日期API由DateCalendarSimpleDateFormat三个主要类组成:

  1. Date类:提供基本的日期时间表示,但大多数方法已经过时。
  2. Calendar类:提供了更多的日期操作功能,如日期计算和字段提取。
  3. SimpleDateFormat类:用于日期格式化和解析,但不是线程安全的。

虽然这些类存在设计缺陷,但在许多遗留系统中仍然使用。对于新的开发,建议使用Java 8引入的日期时间API(java.time包),它提供了更直观、不可变且线程安全的类来处理日期和时间。

练习

  1. 使用Calendar类创建一个方法,计算给定日期是一年中的第几天。
  2. 创建一个方法,使用SimpleDateFormat将日期格式从"yyyy-MM-dd"转换为"MM/dd/yyyy"。
  3. 编写一个程序,使用Calendar计算下个月的第一个星期一的日期。
  4. 创建一个方法,判断给定的两个日期是否在同一周内。

附加资源

提示

学习完遗留日期API后,建议进一步学习Java 8中新引入的java.time包下的日期时间API,如LocalDate、LocalTime、LocalDateTime、ZonedDateTime等类,它们设计更合理且易用。