Java 时间日期类笔记
1. 核心时间日期API发展历程
1.1 各版本主要API
- Java 8之前:
Date、Calendar、SimpleDateFormat - Java 8+:
java.time包 (JSR-310) - 推荐: 新项目统一使用
java.time包
2. Java 8+ 时间日期API (java.time)
2.1 核心类概览
| 类名 | 描述 | 示例 |
|---|---|---|
LocalDate |
日期 (年月日) | 2023-10-25 |
LocalTime |
时间 (时分秒纳秒) | 14:30:15.123 |
LocalDateTime |
日期时间 | 2023-10-25T14:30:15 |
ZonedDateTime |
带时区的日期时间 | 2023-10-25T14:30:15+08:00[Asia/Shanghai] |
Instant |
时间戳 (Unix时间) | 1698215415 |
Duration |
时间间隔 (秒、纳秒) | PT1H30M |
Period |
日期间隔 (年、月、日) | P1Y2M3D |
2.2 基础使用示例
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class DateTimeExamples {
public static void main(String[] args) {
// 1. 获取当前时间
LocalDate currentDate = LocalDate.now();
LocalTime currentTime = LocalTime.now();
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("当前日期: " + currentDate);
System.out.println("当前时间: " + currentTime);
System.out.println("当前日期时间: " + currentDateTime);
// 2. 创建特定时间
LocalDate specificDate = LocalDate.of(2023, 10, 25);
LocalTime specificTime = LocalTime.of(14, 30, 15);
LocalDateTime specificDateTime = LocalDateTime.of(2023, 10, 25, 14, 30, 15);
// 3. 日期计算
LocalDate tomorrow = currentDate.plusDays(1);
LocalDate lastWeek = currentDate.minusWeeks(1);
LocalDate nextMonth = currentDate.plusMonths(1);
// 4. 日期比较
boolean isAfter = specificDate.isAfter(currentDate);
boolean isBefore = specificDate.isBefore(currentDate);
boolean isEqual = specificDate.isEqual(currentDate);
// 5. 获取日期组成部分
int year = currentDate.getYear();
Month month = currentDate.getMonth();
int dayOfMonth = currentDate.getDayOfMonth();
DayOfWeek dayOfWeek = currentDate.getDayOfWeek();
}
}
2.3 时区处理
public class TimeZoneExamples {
public static void main(String[] args) {
// 1. 时区日期时间
ZonedDateTime beijingTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("北京时间: " + beijingTime);
System.out.println("纽约时间: " + newYorkTime);
// 2. 时区转换
ZonedDateTime convertedTime = beijingTime.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println("UTC时间: " + convertedTime);
// 3. 获取所有可用时区
// ZoneId.getAvailableZoneIds().forEach(System.out::println);
// 4. Instant (时间戳)
Instant instant = Instant.now();
System.out.println("当前时间戳: " + instant.toEpochMilli());
// 从时间戳创建
Instant fromTimestamp = Instant.ofEpochMilli(1698215415000L);
}
}
2.4 时间间隔计算
public class DurationPeriodExamples {
public static void main(String[] args) {
// 1. Duration - 时间间隔 (精确到纳秒)
LocalTime startTime = LocalTime.of(9, 0, 0);
LocalTime endTime = LocalTime.of(17, 30, 0);
Duration duration = Duration.between(startTime, endTime);
System.out.println("工作时间: " + duration.toHours() + "小时");
System.out.println("总分钟数: " + duration.toMinutes());
// 2. Period - 日期间隔 (年、月、日)
LocalDate birthDate = LocalDate.of(1990, 5, 15);
LocalDate currentDate = LocalDate.now();
Period age = Period.between(birthDate, currentDate);
System.out.println("年龄: " + age.getYears() + "年" +
age.getMonths() + "月" + age.getDays() + "天");
// 3. 使用ChronoUnit计算差值
long daysBetween = ChronoUnit.DAYS.between(birthDate, currentDate);
long monthsBetween = ChronoUnit.MONTHS.between(birthDate, currentDate);
System.out.println("总天数: " + daysBetween);
System.out.println("总月数: " + monthsBetween);
}
}
2.5 格式化与解析
public class FormattingExamples {
public static void main(String[] args) {
// 1. 预定义格式器
LocalDateTime now = LocalDateTime.now();
String isoFormat = now.format(DateTimeFormatter.ISO_DATE_TIME);
String basicFormat = now.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println("ISO格式: " + isoFormat);
System.out.println("基本ISO格式: " + basicFormat);
// 2. 自定义格式
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String customFormat = now.format(customFormatter);
System.out.println("自定义格式: " + customFormat);
// 3. 解析字符串为日期
String dateString = "2023-10-25 14:30:15";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateString, customFormatter);
System.out.println("解析结果: " + parsedDateTime);
// 4. 本地化格式
DateTimeFormatter germanFormatter =
DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.GERMAN);
String germanFormat = now.format(germanFormatter);
System.out.println("德语格式: " + germanFormat);
}
}
3. 传统日期API (Java 8之前)
3.1 Date 和 Calendar
import java.util.*;
import java.text.SimpleDateFormat;
public class LegacyDateTime {
public static void main(String[] args) throws Exception {
// 1. Date 类
Date now = new Date();
System.out.println("当前时间: " + now);
// 2. Calendar 类
Calendar calendar = Calendar.getInstance();
calendar.set(2023, Calendar.OCTOBER, 25, 14, 30, 15);
Date specificDate = calendar.getTime();
// 日历计算
calendar.add(Calendar.DAY_OF_MONTH, 7); // 加7天
calendar.add(Calendar.MONTH, -1); // 减1个月
// 3. SimpleDateFormat
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formatted = sdf.format(now);
System.out.println("格式化: " + formatted);
// 解析
Date parsedDate = sdf.parse("2023-10-25 14:30:15");
// 注意: SimpleDateFormat 非线程安全!
}
}
3.2 线程安全问题
// 错误的用法 - 多线程环境下会有问题
public class UnsafeDateFormat {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 多线程调用时会抛出异常或返回错误结果
}
// 正确的用法 - 使用ThreadLocal
public class SafeDateFormat {
private static final ThreadLocal<SimpleDateFormat> threadLocal =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public static String format(Date date) {
return threadLocal.get().format(date);
}
}
4. 新旧API转换
4.1 互相转换方法
public class ConversionExamples {
public static void main(String[] args) {
// 1. Date -> Instant -> LocalDateTime
Date oldDate = new Date();
Instant instant = oldDate.toInstant();
LocalDateTime newDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
// 2. LocalDateTime -> Instant -> Date
LocalDateTime localDateTime = LocalDateTime.now();
Instant instant2 = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Date newDate = Date.from(instant2);
// 3. Calendar -> ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(
calendar.toInstant(), calendar.getTimeZone().toZoneId());
// 4. 时间戳转换
long timestamp = System.currentTimeMillis();
Instant instantFromTimestamp = Instant.ofEpochMilli(timestamp);
LocalDateTime dateTimeFromTimestamp = LocalDateTime.ofInstant(
instantFromTimestamp, ZoneId.systemDefault());
}
}
5. 最佳实践和注意事项
5.1 推荐做法
public class BestPractices {
// 1. 使用不可变对象
public void processOrder(LocalDateTime orderTime) {
LocalDateTime processedTime = orderTime.plusMinutes(30); // 返回新对象
// orderTime 保持不变
}
// 2. 使用常量格式器
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 3. 明确处理时区
public ZonedDateTime convertToTimezone(LocalDateTime localDateTime, String zoneId) {
return localDateTime.atZone(ZoneId.systemDefault())
.withZoneSameInstant(ZoneId.of(zoneId));
}
// 4. 使用合适的类
public void demonstrateAppropriateUse() {
// 只需要日期
LocalDate birthday = LocalDate.of(1990, 5, 15);
// 只需要时间
LocalTime meetingTime = LocalTime.of(14, 30);
// 需要日期时间但不需要时区
LocalDateTime createdTime = LocalDateTime.now();
// 需要时区信息
ZonedDateTime publishTime = ZonedDateTime.now(ZoneId.of("UTC"));
// 时间戳存储
Instant timestamp = Instant.now();
}
}
5.2 常见陷阱
public class CommonPitfalls {
public static void main(String[] args) {
// 1. 不要使用已废弃的构造方法
// Date date = new Date(123, 9, 25); // 已废弃!
// 2. Calendar 的月份从0开始
Calendar cal = Calendar.getInstance();
cal.set(2023, 9, 25); // 10月 (0=1月, 9=10月)
// 3. 时区处理要一致
LocalDateTime local = LocalDateTime.now();
// 错误的时区假设
// ZonedDateTime zoned = local.atZone(ZoneId.of("UTC"));
// 正确的做法
ZonedDateTime zoned = local.atZone(ZoneId.systemDefault())
.withZoneSameInstant(ZoneId.of("UTC"));
// 4. 格式化解析要匹配
String input = "2023/10/25";
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 错误!
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); // 正确
}
}
总结
- 新项目优先使用
java.time包 - 根据需求选择合适的类:
LocalDate、LocalTime、LocalDateTime、ZonedDateTime - 时区处理要明确, 避免隐式时区转换
- 格式化器尽量重用, 避免重复创建
- 注意线程安全问题, 特别是传统API
- 利用不可变特性, 避免意外的状态修改
这套新的日期时间API设计更加合理,解决了传统API的很多问题,是Java日期时间处理的现代解决方案。

浙公网安备 33010602011771号