网站首页 > 精选教程 正文
以下是Java日期时间API的全面解析,涵盖新旧API对比、核心类使用、时区处理及最佳实践:
一、Java日期时间演进史
二、新旧API核心对比
特性 | java.util.Date | java.time API |
可变性 | 可变(setTime方法) | 不可变(线程安全) |
月份表示 | 0-11(反直觉) | 1-12(符合现实) |
时区处理 | 依赖Calendar | 内置ZoneId系统 |
格式化 | SimpleDateFormat | DateTimeFormatter |
扩展性 | 无法扩展 | 支持自定义调节器 |
纳秒精度 | 不支持 | 支持到纳秒级 |
三、核心类体系结构
四、基础操作全解析
1. 日期创建与获取
// 获取当前日期时间
LocalDate today = LocalDate.now();
LocalTime currentTime = LocalTime.now();
LocalDateTime currentDateTime = LocalDateTime.now();
// 指定日期
LocalDate nationalDay = LocalDate.of(2023, Month.OCTOBER, 1);
LocalTime preciseTime = LocalTime.of(14, 30, 45, 123_456_789);
// 解析字符串
LocalDate parsedDate = LocalDate.parse("2023-08-15");
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-08-15T20:30:45");
2. 日期时间运算
// 加减操作
LocalDateTime nextWeek = currentDateTime.plusWeeks(1);
LocalDate prevMonth = today.minus(Period.ofMonths(1));
// 使用TemporalAdjusters
LocalDate firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate nextWorkingDay = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
// 自定义调节器
LocalDate customAdjust = today.with(t -> {
LocalDate date = (LocalDate) t;
return date.getDayOfWeek() == DayOfWeek.FRIDAY ? date.plusDays(3) : date.plusDays(1);
});
3. 时段与周期计算
// 计算两个日期之间
Period period = Period.between(LocalDate.of(2023, 1, 1), today);
System.out.printf("间隔:%d年%d月%d日%n",
period.getYears(), period.getMonths(), period.getDays());
// 精确时间差
Duration duration = Duration.between(
LocalTime.of(8, 0),
LocalTime.of(17, 30)
);
System.out.println("工作时间:" + duration.toHours() + "小时");
五、时区处理深度剖析
1. 时区转换示例
// 上海时间转纽约时间
ZonedDateTime shanghaiTime = ZonedDateTime.of(
LocalDateTime.parse("2023-08-15T09:00"),
ZoneId.of("Asia/Shanghai")
);
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(
ZoneId.of("America/New_York")
);
// 结果:2023-08-14T21:00-04:00[America/New_York]
2. 夏令时处理
// 测试夏令时转换(纽约时区)
ZonedDateTime beforeDst = ZonedDateTime.of(
LocalDateTime.of(2023, 3, 12, 1, 30),
ZoneId.of("America/New_York")
);
ZonedDateTime afterDst = beforeDst.plusHours(1);
// 结果:2023-03-12T03:30-04:00(跳过2点到3点)
六、格式化与解析
1. 预定义格式
// ISO标准格式
String isoDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE);
// 自定义格式
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.systemDefault());
String formatted = LocalDateTime.now().format(formatter);
LocalDateTime parsed = LocalDateTime.parse("2023-08-15 14:30:00", formatter);
2. 本地化格式
DateTimeFormatter frenchFormatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.FULL)
.withLocale(Locale.FRANCE);
ZonedDateTime.now().format(frenchFormatter);
// 输出:"mercredi 16 ao^ut 2023 à 15:30:00 heure normale de la Chine"
七、与传统API互操作
// Date转LocalDateTime
Date legacyDate = new Date();
LocalDateTime newDateTime = legacyDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
// Calendar转ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime fromCalendar = ZonedDateTime.ofInstant(
calendar.toInstant(),
calendar.getTimeZone().toZoneId()
);
八、最佳实践指南
1. 存储与传输建议
- 数据库存储:使用TIMESTAMP WITH TIME ZONE类型
- API传输:统一使用ISO-8601格式(如2023-08-15T14:30:45+08:00)
- 序列化:Jackson配置示例:
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ISO_DATE));
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ISO_DATE_TIME));
};
}
2. 性能优化
// 重用不可变对象
private static final DateTimeFormatter CACHED_FORMATTER
= DateTimeFormatter.ofPattern("yyyy/MM/dd");
// 避免频繁创建ZoneId
private static final ZoneId UTC_ZONE = ZoneId.of("UTC");
3. 常见陷阱规避
// 错误:忽略时区信息
LocalDateTime local = LocalDateTime.now();
ZonedDateTime zoned = local.atZone(ZoneId.systemDefault()); // 正确关联时区
// 错误:直接比较瞬时时间
Instant inst1 = Instant.now();
Instant inst2 = Instant.now().plusSeconds(1);
assert inst1.isBefore(inst2); // 正确比较方式
九、高级应用场景
1. 工作日计算
public static LocalDate addWorkingDays(LocalDate start, int days) {
return Stream.iterate(start, date -> date.plusDays(1))
.filter(date -> date.getDayOfWeek().getValue() < 6)
.skip(days)
.findFirst()
.orElseThrow();
}
2. 生日提醒系统
public static List<LocalDate> getUpcomingBirthdays(LocalDate today, List<LocalDate> birthdays) {
return birthdays.stream()
.map(bd -> bd.withYear(today.getYear()))
.filter(bd -> {
Period period = Period.between(today, bd);
return period.getMonths() == 0 && period.getDays() >= 0 && period.getDays() <= 7;
})
.sorted()
.collect(Collectors.toList());
}
十、高频面试问题
Q1:为什么需要新的日期API?
- 旧API存在设计缺陷(可变性、月份从0开始等)
- 缺乏直观的日期计算API
- 时区处理复杂且易错
- 线程安全问题(如SimpleDateFormat)
Q2:LocalDateTime与ZonedDateTime区别?
- LocalDateTime:不带时区的日期时间(如会议时间)
- ZonedDateTime:带完整时区信息(如国际航班时刻)
Q3:如何计算两个日期间的工作日?
long workDays = startDate.datesUntil(endDate)
.filter(d -> d.getDayOfWeek().getValue() < 6)
.count();
掌握Java日期时间API的关键在于:
- 理解不可变设计哲学
- 严格区分时间概念(本地时间、时区时间、瞬时时间)
- 熟练使用TemporalAdjusters进行复杂计算
- 正确处理时区和夏令时问题
建议在实际项目中:
- 完全弃用java.util.Date和Calendar
- 统一使用java.time进行所有日期时间操作
- 建立严格的日期格式规范
猜你喜欢
- 2025-04-30 Java程序是如何运行的(java怎么运行的)
- 2025-04-30 人与人相处:尊重是标配,靠谱是高配,厚道是顶配
- 2025-04-30 Java日期处理太头疼?Hutool的DateUtils让你三行代码搞定!
- 2025-04-30 Java中1927年时间戳相减结果异常原因解析
- 2025-04-30 Java面试中时间复杂度的那些事儿(java算法时间复杂度 空间复杂度)
- 2025-04-30 java:日期时间操作方法(java 日期操作)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)