日期时间API的使用整合 - java.util包 和 java.text包
一、java.util包和java.text包
(一)java.util.Date
在Java开发中,处理日期和时间是一个非常常见的需求。Java提供了多种API来处理日期和时间,主要包括传统的java.util.Date
类和引入Java 8的现代日期时间API(java.time
包)。现代API被广泛推荐使用,因为它们更加直观、线程安全且功能强大。
1. 传统的 java.util.Date
类
创建 Date
对象
java.util.Date
类表示一个特定的时间,精确到毫秒。可以通过以下几种方式创建Date
对象:
import java.util.Date;
// 当前日期和时间
Date now = new Date();
System.out.println(now);
// 指定时间(自 1970 年 1 月 1 日 00:00:00 GMT 以来的毫秒数)
Date specificDate = new Date(1633039200000L); // 2021-10-01 00:00:00 GMT
System.out.println(specificDate);
格式化和解析日期
需要使用 java.text.SimpleDateFormat
类来格式化和解析 Date 对象:
import java.text.SimpleDateFormat;
import java.util.Date;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = formatter.format(now);
System.out.println(formattedDate);
// 解析字符串到日期
String dateStr = "2021-10-01 12:00:00";
Date parsedDate = formatter.parse(dateStr);
System.out.println(parsedDate);
Date的局限性
- 易于误解 :
Date
类的方法命名不够直观,容易造成混淆。 - 可变性 :
Date
是可变的,不适合多线程环境。 - 缺乏时区支持 :
Date
本身不包含时区信息。
2. Java 8引入的现代日期时间API(java.time
包)
主要类和接口
LocalDate
:表示日期,不含时间LocalTime
:表示时间,不含日期LocalDateTime
:表示日期和时间,不含时区ZonedDateTime
:表示日期和时间,包含时区Instant
:表示时间戳(1970-01-01 00:00:00 UTC以来的秒数)
创建日期和时间对象
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.Instant;
//当前日期
LocalDate currentDate = LocalDate.now();
System.out.println(currentDate);
//当前时间
LocalTime currentTime = LocalTime.now();
System.out.println(currentTime);
//当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println(currentDateTime);
//当前日期和时间(有时区)
ZonedDateTime currentZonedDateTime = ZonedDateTime.now();
System.out.println(currentZonedDateTime);
//时间戳
Instant timestamp = Instant.now();
System.out.println(timestamp);
自定义日期和时间
LocalDate myBirthday = LocalDate.of(1990, 1, 1);
LocalTime meetingTime = LocalTime.of(13, 30);
LocalDateTime appointment = LocalDateTime.of(2021, 10, 1, 10, 0);
ZonedDateTime zonedDateTime = ZonedDateTime.of(appointment, ZoneId.of("America/New_York"));
System.out.println(myBirthday);
System.out.println(meetingTime);
System.out.println(appointment);
System.out.println(zonedDateTime);
格式化和解析日期时间
使用 DateTimeFormatter
类来格式化和解析日期和时间:
import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = currentDateTime.format(formatter);
System.out.println(formattedDateTime);
//解析字符串到LocalDateTime
String dateTimeStr = "2021-10-01 12:00:00";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter);
System.out.println(parsedDateTime);
日期时间的操作
现代API还提供丰富的方法来操作日期和时间,例如加减日期、比较日期:
import java.time.LocalDate;
import java.time.Period;
//加一天
LocalDate tomorrow = currentDate.plusDays(1);
System.out.println(tomorrow);
//减一个月
LocalDate lastMonth = currentDate.minusMonths(1);
System.out.println(lastMonth);
//比较日期
boolean isBefore = myBirthday.isBefore(tomorrow);
System.out.println(isBefore);
//计算两个日期之间的间隔
Period period = Period.between(myBirthday, currentDate);
System.out.println(period.getYears() + " years " + period.getMonths() + " months " + period.getDays() + " days");
综合对比
现代日期时间API解决了传统Date
类的诸多局限性,提供了更直观、功能强大且线程安全的方式来处理日期和时间。因此,强烈推荐在Java开发中使用java.time
包中的类。
(二)java.util.Calendar
在 Java 8 之前,java.util.Calendar
是用于处理日期和时间的一种主要方式。尽管自 Java 8 引入了新的 java.time
包之后,Calendar
类逐渐被更现代和易用的时间 API 取代,但了解 Calendar
仍然是处理遗留系统或与旧代码库兼容的必要技能。
这里我们将详细介绍 Calendar
类的各种功能及其使用方法。
1. 创建 Calendar
对象
获取当前日期和时间
Calendar
类是一个抽象类,不能直接实例化。我们通常使用其静态方法 getInstance()
获取当前日期和时间的 Calendar
对象:
import java.util.Calendar;
Calendar calendar = Calendar.getInstance();
System.out.println("Current date and time: " + calendar.getTime());
使用特定时间
可以通过设置年、月、日、时、分、秒来创建一个特定时间的 Calendar
对象:
import java.util.Calendar;
Calendar calendar = Calendar.getInstance();
calendar.set(2023, Calendar.OCTOBER, 5, 14, 30, 0); // 注意月份从0开始,OCTOBER为9
System.out.println("Specific date and time: " + calendar.getTime());
2. 操作 Calendar
对象
获取日期和时间的各个部分
可以通过 Calendar
类的许多常量来获取日期和时间的各个部分:
import java.util.Calendar;
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH); // 注意月份从0开始
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
System.out.println("Year: " + year);
System.out.println("Month: " + (month + 1)); // 月份从0开始,所以需要加1
System.out.println("Day: " + day);
System.out.println("Hour: " + hour);
System.out.println("Minute: " + minute);
System.out.println("Second: " + second);
修改日期和时间
可以通过 set
方法来修改 Calendar
对象的日期和时间:
import java.util.Calendar;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2025);
calendar.set(Calendar.MONTH, Calendar.DECEMBER); // 12月
calendar.set(Calendar.DAY_OF_MONTH, 25);
System.out.println("Modified date: " + calendar.getTime());
增加或减少日期和时间
可以使用 add
和 roll
方法来增加或减少日期和时间。add
方法会调整较大的字段,而 roll
方法只会调整指定的字段,不会影响更大的字段。
import java.util.Calendar;
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 10); // 增加 10 天
System.out.println("Date after adding 10 days: " + calendar.getTime());
calendar.add(Calendar.MONTH, -2); // 减少 2 个月
System.out.println("Date after subtracting 2 months: " + calendar.getTime());
calendar.roll(Calendar.DAY_OF_MONTH, 15); // 滚动 15 天
System.out.println("Date after rolling 15 days: " + calendar.getTime());
3. 格式化和解析日期和时间
尽管 Calendar
类本身并没有提供直接的格式化和解析功能,但可以与 SimpleDateFormat
类一起使用。
格式化日期和时间
使用 SimpleDateFormat
将 Calendar
对象的时间格式化为指定格式的字符串:
import java.text.SimpleDateFormat;
import java.util.Calendar;
Calendar calendar = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = sdf.format(calendar.getTime());
System.out.println("Formatted date: " + formattedDate);
解析字符串为日期和时间
同样可以使用 SimpleDateFormat
将字符串解析为 Date
对象,然后设置到 Calendar
对象中:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
String dateStr = "2023-10-05 14:30:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date date = sdf.parse(dateStr);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
System.out.println("Parsed date: " + calendar.getTime());
} catch (ParseException e) {
e.printStackTrace();
}
4. 时间比较和计算
时间比较
可以使用 Calendar
类的 before
、after
和 equals
方法来比较两个 Calendar
对象:
import java.util.Calendar;
Calendar calendar1 = Calendar.getInstance();
calendar1.set(2023, Calendar.OCTOBER, 5);
Calendar calendar2 = Calendar.getInstance();
calendar2.set(2023, Calendar.NOVEMBER, 5);
boolean isBefore = calendar1.before(calendar2); // true
boolean isAfter = calendar1.after(calendar2); // false
System.out.println("calendar1 is before calendar2: " + isBefore);
System.out.println("calendar1 is after calendar2: " + isAfter);
计算两个日期之间的时间差
可以通过计算两个 Calendar
对象的时间戳之差来获得时间差:
import java.util.Calendar;
Calendar calendar1 = Calendar.getInstance();
calendar1.set(2023, Calendar.OCTOBER, 5);
Calendar calendar2 = Calendar.getInstance();
calendar2.set(2023, Calendar.NOVEMBER, 5);
long diffMillis = calendar2.getTimeInMillis() - calendar1.getTimeInMillis();
long diffDays = diffMillis / (24 * 60 * 60 * 1000); // 毫秒转换为天
System.out.println("Difference in days: " + diffDays);
5. 时区处理
获取和设置时区
可以使用 Calendar
的 getTimeZone
和 setTimeZone
方法来处理不同的时区:
import java.util.Calendar;
import java.util.TimeZone;
Calendar calendar = Calendar.getInstance();
// 获取当前时区
TimeZone timeZone = calendar.getTimeZone();
System.out.println("Current TimeZone: " + timeZone.getID());
// 设置为其他时区
calendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println("Time in New York: " + calendar.getTime());
小结
尽管 java.util.Calendar
已逐渐被 java.time
包下的现代日期时间 API 取代,但它仍然在某些遗留系统和旧代码库中广泛使用。理解和熟练使用 Calendar
类对于处理与旧系统兼容以及维护遗留代码非常重要。
(三)java.text.SimpleDateFormat
SimpleDateFormat
是 Java 中旧的日期和时间 API (java.text
包) 的一部分,用于格式化和解析日期和时间。尽管 Java 8 引入了新的时间 API (java.time
包),但 SimpleDateFormat
仍然在某些旧代码或特定场景中被广泛使用。
1. 创建 SimpleDateFormat
对象
SimpleDateFormat
是 DateFormat
的子类,用于格式化和解析日期的具体类。通过模式字符串可以创建 SimpleDateFormat
对象:
import java.text.SimpleDateFormat;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
2. 格式化日期
使用 SimpleDateFormat
对象的 format
方法可以将 Date
对象格式化为字符串:
import java.text.SimpleDateFormat;
import java.util.Date;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now = new Date();
String formattedDate = dateFormat.format(now);
System.out.println("Formatted date: " + formattedDate);
3. 解析日期字符串
使用 SimpleDateFormat
对象的 parse
方法可以将字符串解析为 Date
对象:
import java.text.SimpleDateFormat;
import java.util.Date;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2023-10-05 14:30:45";
try {
Date date = dateFormat.parse(dateString);
System.out.println("Parsed date: " + date);
} catch (Exception e) {
System.out.println("Failed to parse date: " + e.getMessage());
}
4. 模式字符串详解
SimpleDateFormat
的模式字符串使用特定的字母表示日期和时间的各个部分:
y
:年M
:月d
:天H
:小时(24小时制)h
:小时(12小时制)m
:分钟s
:秒S
:毫秒E
:星期几D
:一年中的天数F
:一个月中的星期几w
:一年中的周数W
:一个月中的周数a
:AM/PM 标记k
:小时(1-24)K
:小时(0-11)z
:时区
示例:
import java.text.SimpleDateFormat;
import java.util.Date;
SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM dd, yyyy 'at' hh:mm:ss a z");
String formattedDate = dateFormat.format(new Date());
System.out.println("Custom formatted date: " + formattedDate);
5. 线程安全性
SimpleDateFormat
不是线程安全的。如果在多线程环境中使用,应该创建独立的实例,或使用同步机制,或使用线程局部变量:
// 使用 ThreadLocal 存储 SimpleDateFormat 实例
private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String formatDate(Date date) {
return dateFormatThreadLocal.get().format(date);
}
public static Date parseDate(String dateString) throws Exception {
return dateFormatThreadLocal.get().parse(dateString);
}
6. 对比 Java 8 新的时间 API
Java 8 引入了新的时间 API,提供了更好的线程安全性和功能性,推荐使用 DateTimeFormatter
代替 SimpleDateFormat
:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
String formattedDate = now.format(formatter);
System.out.println("Formatted date: " + formattedDate);
// 解析
String dateString = "2023-10-05 14:30:45";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateString, formatter);
System.out.println("Parsed date and time: " + parsedDateTime);
7. 示例:日期格式化和解析的综合应用
以下是一个综合示例,展示如何使用 SimpleDateFormat
进行日期格式化和解析:
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatExample {
public static void main(String[] args) {
try {
// 定义日期格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 获取当前日期和时间
Date now = new Date();
System.out.println("Current date and time: " + now);
// 格式化当前日期和时间
String formattedDate = dateFormat.format(now);
System.out.println("Formatted date: " + formattedDate);
// 定义一个日期字符串
String dateString = "2023-10-05 14:30:45";
System.out.println("Date string to parse: " + dateString);
// 解析日期字符串为 Date 对象
Date parsedDate = dateFormat.parse(dateString);
System.out.println("Parsed date: " + parsedDate);
// 使用自定义格式
SimpleDateFormat customDateFormat = new SimpleDateFormat("EEEE, MMMM dd, yyyy 'at' hh:mm:ss a z");
String customFormattedDate = customDateFormat.format(now);
System.out.println("Custom formatted date: " + customFormattedDate);
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
}
}
}
小结
SimpleDateFormat
是 Java 旧日期和时间处理的主要工具之一,用于格式化和解析日期和时间。尽管 Java 8 提供了更现代化和线程安全的 DateTimeFormatter
,但在许多遗留系统中仍然广泛使用 SimpleDateFormat
。掌握其使用方法和注意事项,有助于在处理旧系统中涉及日期和时间的代码时更加得心应手。
(四)java.util.TimeZone
在 Java 开发中,TimeZone
类是处理时区的重要工具。它是 java.util
包的一部分,提供了处理时间和日期相关任务时所需的时区支持。虽然 Java 8 引入了更先进和简化的时区处理类(如 ZoneId
和 ZoneOffset
),但 TimeZone
仍然在许多应用中被广泛使用。
1. TimeZone
类概述
TimeZone
类表示一个时区的偏移量,它与 GMT(格林威治标准时间)的差异可以是一个固定的时间量。了解和使用 TimeZone
类对处理不同地区的时间非常有用。
2. 创建 TimeZone
对象
可以通过多种方法来创建 TimeZone
对象:
使用时区 ID
时区 ID 是一个 String,可以是时区名称(如 “America/New_York”)或时间偏移量(如 “GMT+8”):
import java.util.TimeZone;
TimeZone tz1 = TimeZone.getTimeZone("America/New_York");
TimeZone tz2 = TimeZone.getTimeZone("GMT+8");
System.out.println("TimeZone 1: " + tz1.getID());
System.out.println("TimeZone 2: " + tz2.getID());
使用默认时区
可以获取系统默认的时区:
TimeZone defaultTimeZone = TimeZone.getDefault();
System.out.println("Default TimeZone: " + defaultTimeZone.getID());
3. 获取时区信息
通过 TimeZone
对象可以获取各种时区信息:
获取时区偏移量
时区偏移量指的是与 GMT 的时差,可以通过以下方法获取:
import java.util.TimeZone;
import java.util.Calendar;
TimeZone tz = TimeZone.getTimeZone("America/New_York");
int offsetMillis = tz.getOffset(Calendar.ZONE_OFFSET, 2023, Calendar.OCTOBER, 1, Calendar.SUNDAY, 0);
int offsetHours = offsetMillis / (60 * 60 * 1000);
System.out.println("TimeZone offset (hours): " + offsetHours);
判断是否使用夏令时
boolean isDst = tz.inDaylightTime(new Date());
System.out.println("Is Daylight Saving Time: " + isDst);
获取夏令时变更信息
import java.util.TimeZone;
TimeZone tz = TimeZone.getTimeZone("America/New_York");
boolean useDaylightTime = tz.useDaylightTime();
System.out.println("Uses Daylight Saving Time: " + useDaylightTime);
// 获取夏令时时段的偏移量
int dstSavings = tz.getDSTSavings();
System.out.println("Daylight Saving Time offset (milliseconds): " + dstSavings);
4. 设置时区
可以通过 TimeZone
对象修改 Calendar
和 DateFormat
对象的时区:
import java.util.TimeZone;
import java.util.Calendar;
import java.text.SimpleDateFormat;
TimeZone tz = TimeZone.getTimeZone("America/New_York");
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(tz);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(tz);
System.out.println("Current time in TimeZone: " + dateFormat.format(calendar.getTime()));
5. 获取可用时区 ID
可以获取所有可用的时区 ID:
import java.util.TimeZone;
String[] availableIDs = TimeZone.getAvailableIDs();
for (String id : availableIDs) {
System.out.println(id);
}
6. Java 8 中的 TimeZone
替代品
Java 8 引入了新的时区类 ZoneId
和 ZoneOffset
,它们位于 java.time
包中,提供了更好的时区处理支持:
使用 ZoneId
import java.time.ZoneId;
import java.time.ZonedDateTime;
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTime = ZonedDateTime.now(zoneId);
System.out.println("Current date and time in New York: " + zonedDateTime);
使用 ZoneOffset
import java.time.ZoneOffset;
import java.time.OffsetDateTime;
ZoneOffset offset = ZoneOffset.ofHours(-5);
OffsetDateTime offsetDateTime = OffsetDateTime.now(offset);
System.out.println("Current date and time with offset -5: " + offsetDateTime);
7. 综合实例
以下是一个综合实例,展示如何在实际应用中使用 TimeZone
类:
import java.util.TimeZone;
import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TimeZoneExample {
public static void main(String[] args) {
// 创建 TimeZone 对象
TimeZone timeZoneNY = TimeZone.getTimeZone("America/New_York");
TimeZone timeZoneLA = TimeZone.getTimeZone("America/Los_Angeles");
// 获取当前时间
Calendar calendar = Calendar.getInstance();
Date currentDate = calendar.getTime();
// 格式化当前时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(timeZoneNY);
String formattedDateNY = sdf.format(currentDate);
System.out.println("Current time in New York: " + formattedDateNY);
sdf.setTimeZone(timeZoneLA);
String formattedDateLA = sdf.format(currentDate);
System.out.println("Current time in Los Angeles: " + formattedDateLA);
// 获取时区偏移量
int offsetNY = timeZoneNY.getOffset(currentDate.getTime());
int offsetNYHours = offsetNY / (60 * 60 * 1000);
System.out.println("TimeZone offset in New York (hours): " + offsetNYHours);
// 判断是否使用夏令时
boolean isDstNY = timeZoneNY.inDaylightTime(currentDate);
System.out.println("Is New York in Daylight Saving Time: " + isDstNY);
// 获取所有可用的时区 ID
String[] availableIDs = TimeZone.getAvailableIDs();
System.out.println("Available TimeZone IDs:");
for (String id : availableIDs) {
System.out.println(id);
}
}
}
小结
TimeZone
类是处理时区的关键工具,尽管 Java 8 引入了更现代化的替代方案(如 ZoneId
和 ZoneOffset
),但 TimeZone
仍然在许多遗留系统或特定场景中广泛使用。掌握 TimeZone
的创建、获取信息、设置和使用方法,可以帮助开发者更好地处理时区相关的任务。