当前位置: 首页 > web >正文

在 Java 8 中 常用时间日期类

在 Java 8 中 常用时间日期类

在 Java 8 及以后的版本中,引入了全新的日期和时间 API(JSR 310),这些类位于 java.time 包下,提供了更简洁、更安全、更易用的日期时间处理方式。除了 LocalDate,以下是一些常用的日期时间类及其主要功能:

1. LocalTime

表示不带时区的时间(时、分、秒、纳秒),适用于只需要时间而不关心日期的场景。

import java.time.LocalTime;LocalTime now = LocalTime.now(); // 当前时间(例如:14:30:45.123)
LocalTime specificTime = LocalTime.of(14, 30); // 指定时间:14:30:00
LocalTime parsedTime = LocalTime.parse("14:30:45"); // 从字符串解析// 时间运算
LocalTime later = now.plusHours(2).plusMinutes(30); // 2小时30分钟后
boolean isBefore = now.isBefore(specificTime); // 比较时间先后

2. LocalDateTime

表示不带时区的日期和时间(年、月、日、时、分、秒、纳秒),是 LocalDateLocalTime 的组合。

import java.time.LocalDateTime;LocalDateTime now = LocalDateTime.now(); // 当前日期时间
LocalDateTime specificDateTime = LocalDateTime.of(2023, 10, 15, 14, 30); // 指定日期时间
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-10-15T14:30:45"); // 从ISO格式解析// 与LocalDate/LocalTime互转
LocalDate date = now.toLocalDate(); // 提取日期部分
LocalTime time = now.toLocalTime(); // 提取时间部分

3. ZonedDateTime

表示带时区的完整日期时间,包含时区信息(ZoneId)和偏移量(ZoneOffset),适用于需要处理不同时区的场景。

import java.time.ZonedDateTime;
import java.time.ZoneId;ZonedDateTime nowInUTC = ZonedDateTime.now(ZoneId.of("UTC")); // UTC时间
ZonedDateTime nowInShanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); // 上海时间// 时区转换
ZonedDateTime timeInNewYork = nowInShanghai.withZoneSameInstant(ZoneId.of("America/New_York"));// 获取时区信息
ZoneId zone = nowInShanghai.getZone(); // Asia/Shanghai

4. OffsetDateTime

表示带时区偏移量的日期时间(如 2023-10-15T14:30:45+08:00),但不包含时区名称(如 Asia/Shanghai)。

import java.time.OffsetDateTime;
import java.time.ZoneOffset;OffsetDateTime nowWithOffset = OffsetDateTime.now(); // 当前时间+系统默认偏移量
OffsetDateTime specificOffsetTime = OffsetDateTime.of(2023, 10, 15, 14, 30, 0, 0, ZoneOffset.of("+08:00")); // 指定偏移量

5. Instant

表示时间线上的一个点(精确到纳秒),主要用于生成时间戳(如数据库记录的时间戳)或与 java.util.Date 互转。

import java.time.Instant;Instant now = Instant.now(); // 当前时刻(UTC时间戳)
long epochSeconds = now.getEpochSecond(); // 自1970-01-01T00:00:00Z以来的秒数
long epochMilli = now.toEpochMilli(); // 毫秒数(适用于兼容旧API)// 与java.util.Date互转
java.util.Date legacyDate = java.util.Date.from(now);
Instant instantFromDate = legacyDate.toInstant();

6. Duration

表示两个时间点之间的差值(如 “2小时30分钟”),适用于计算时间间隔。

import java.time.Duration;
import java.time.LocalTime;LocalTime start = LocalTime.of(10, 0);
LocalTime end = LocalTime.of(14, 30);
Duration duration = Duration.between(start, end); // PT4H30M(4小时30分钟)long hours = duration.toHours(); // 4
long minutes = duration.toMinutes(); // 270

7. Period

表示两个日期之间的差值(如 “3年2个月15天”),适用于计算日期间隔。

import java.time.Period;
import java.time.LocalDate;LocalDate startDate = LocalDate.of(2020, 1, 1);
LocalDate endDate = LocalDate.of(2023, 3, 16);
Period period = Period.between(startDate, endDate); // P3Y2M15D(3年2个月15天)int years = period.getYears(); // 3
int months = period.getMonths(); // 2
int days = period.getDays(); // 15

8. ZoneId 和 ZoneOffset

  • ZoneId:表示时区标识符(如 Asia/ShanghaiAmerica/New_York)。
  • ZoneOffset:表示与 UTC 的固定偏移量(如 +08:00-05:00)。
import java.time.ZoneId;
import java.time.ZoneOffset;// 获取系统默认时区
ZoneId systemZone = ZoneId.systemDefault(); // Asia/Shanghai(取决于系统设置)// 获取所有可用时区
Set<String> allZones = ZoneId.getAvailableZoneIds();// 创建固定偏移量
ZoneOffset offset = ZoneOffset.of("+08:00");

9. DateTimeFormatter

用于日期时间的解析和格式化,替代旧的 SimpleDateFormat,线程安全。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;LocalDateTime now = LocalDateTime.now();// 格式化为字符串
String formatted = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); // 2023-10-15T14:30:45
String customFormat = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2023-10-15 14:30:45// 从字符串解析
LocalDateTime parsed = LocalDateTime.parse("2023-10-15 14:30:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

10. TemporalAdjusters

用于获取特殊日期(如当月第一天、下一个周一等)。

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;LocalDate today = LocalDate.now();LocalDate firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 当月第一天
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 下一个周一
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear()); // 当年最后一天

总结

Java 8 的日期时间 API 设计更加合理,避免了旧 API 的线程安全问题和设计缺陷。常用场景建议:

  • 日期:用 LocalDate
  • 时间:用 LocalTime
  • 日期+时间:用 LocalDateTime
  • 带时区的完整时间:用 ZonedDateTime
  • 时间戳:用 Instant
  • 计算间隔:用 Duration(时间)或 Period(日期)
  • 格式化/解析:用 DateTimeFormatter

以下是对 Java 8 日期时间 API 的补充内容,涵盖更多实用技巧、最佳实践及注意事项:

11. YearYearMonthMonthDay

处理特定时间单位的类:

import java.time.Year;
import java.time.YearMonth;
import java.time.MonthDay;// Year:表示年份
Year currentYear = Year.now(); // 2025
Year specificYear = Year.of(2024);
boolean isLeapYear = specificYear.isLeap(); // 是否闰年// YearMonth:表示年月
YearMonth currentMonth = YearMonth.now(); // 2025-05
YearMonth futureMonth = currentMonth.plusMonths(3); // 2025-08
String formatted = currentMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"));// MonthDay:表示月日(如生日)
MonthDay birthday = MonthDay.of(5, 12); // 05-12
boolean isBirthday = birthday.equals(MonthDay.from(LocalDate.now()));

12. DayOfWeekMonth 枚举

增强代码可读性:

import java.time.DayOfWeek;
import java.time.Month;LocalDate date = LocalDate.of(2025, 5, 12);
DayOfWeek dayOfWeek = date.getDayOfWeek(); // MONDAY
Month month = date.getMonth(); // MAY// 枚举值可直接用于比较或循环
if (dayOfWeek == DayOfWeek.MONDAY) {System.out.println("It's Monday!");
}// 获取月份天数
int daysInMonth = month.length(Year.of(2024).isLeap()); // 2024年2月有29天

13. 时间调整器(TemporalAdjusters)进阶

获取复杂日期:

import java.time.temporal.TemporalAdjusters;LocalDate today = LocalDate.now();// 当月最后一个工作日(周五)
LocalDate lastFriday = today.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));// 下一个工作日(跳过周六、周日)
LocalDate nextWorkingDay = today.with(temporal -> {DayOfWeek dow = DayOfWeek.from(temporal);int daysToAdd = switch (dow) {case FRIDAY -> 3;case SATURDAY -> 2;default -> 1;};return temporal.plus(daysToAdd, ChronoUnit.DAYS);
});

14. 日期时间计算与比较

更灵活的计算方式:

import java.time.temporal.ChronoUnit;LocalDateTime start = LocalDateTime.of(2025, 5, 1, 10, 0);
LocalDateTime end = LocalDateTime.of(2025, 5, 2, 12, 30);// 计算精确差值
long hoursBetween = ChronoUnit.HOURS.between(start, end); // 26
long minutesBetween = ChronoUnit.MINUTES.between(start, end); // 1590// 检查是否在范围内
LocalDateTime now = LocalDateTime.now();
boolean isBetween = now.isAfter(start) && now.isBefore(end);// 日期时间截断
LocalDateTime truncated = now.truncatedTo(ChronoUnit.HOURS); // 截断到小时

15. 与旧 API 的互操作性

兼容 java.util.Datejava.util.Calendar

import java.util.Date;
import java.util.Calendar;
import java.time.ZoneId;
import java.time.Instant;
import java.time.LocalDateTime;// Date → Instant → LocalDateTime
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
LocalDateTime dateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();// Calendar → ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zonedDateTime = calendar.toInstant().atZone(ZoneId.systemDefault());// LocalDateTime → Date(需指定时区)
LocalDateTime localDateTime = LocalDateTime.now();
Instant instantFromLocal = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Date legacyDateFromLocal = Date.from(instantFromLocal);

16. 时区转换最佳实践

避免夏令时陷阱:

// 上海时间 → 纽约时间(保持同一时刻)
ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));// 上海时间 → 纽约时间(保持相同钟点,日期可能变化)
ZonedDateTime newYorkSameTime = shanghaiTime.withZoneSameLocal(ZoneId.of("America/New_York"));// 处理夏令时转换(自动调整时间)
ZonedDateTime beforeDst = ZonedDateTime.of(2025, 3, 8, 1, 59, 0, 0, ZoneId.of("America/New_York"));
ZonedDateTime afterDst = beforeDst.plusMinutes(1); // 结果为 3:00(跳过2:00)

17. 解析非标准格式的日期时间

自定义格式器:

// 解析带中文的日期
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDate date = LocalDate.parse("2025年05月12日", formatter);// 解析带时区缩写的时间(需自定义解析器)
DateTimeFormatter customFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss").optionalStart().appendPattern(" z").optionalEnd().toFormatter();
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2025-05-12 14:30:00 CST", customFormatter);

18. 处理不可变对象

所有 java.time 类都是不可变的,修改操作返回新对象:

LocalDate date = LocalDate.of(2025, 5, 12);
// 错误:不会修改原对象
date.plusDays(1); // 无效操作// 正确:必须接收返回值
LocalDate newDate = date.plusDays(1); // 2025-05-13

19. 线程安全性

java.time API 是线程安全的,可在多线程环境共享:

// 线程安全的格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");// 多线程共享使用
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {executor.submit(() -> {String formatted = LocalDate.now().format(formatter);System.out.println(formatted);});
}

20. 常见陷阱与注意事项

  1. 时区缺失问题

    // 错误:LocalDateTime 没有时区信息
    LocalDateTime localDateTime = LocalDateTime.now();
    // 需显式关联时区
    ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
    
  2. 夏令时转换

    // 美国2025年夏令时开始于3月9日
    ZonedDateTime invalidTime = ZonedDateTime.of(2025, 3, 9, 2, 30, 0, 0, ZoneId.of("America/New_York"));
    // 抛出 DateTimeException:2:30 在当天不存在(跳过2:00)
    
  3. 日期时间比较

    // 错误:直接比较 LocalDateTime 可能忽略时区差异
    LocalDateTime ldtShanghai = LocalDateTime.of(2025, 5, 12, 12, 0);
    LocalDateTime ldtNewYork = LocalDateTime.of(2025, 5, 12, 12, 0);
    // ldtShanghai.equals(ldtNewYork) 返回 true,但实际时刻不同// 正确:转换为 Instant 比较
    Instant instantShanghai = ldtShanghai.atZone(ZoneId.of("Asia/Shanghai")).toInstant();
    Instant instantNewYork = ldtNewYork.atZone(ZoneId.of("America/New_York")).toInstant();
    // instantShanghai.equals(instantNewYork) 返回 false
    

总结

Java 8+ 的日期时间 API 设计更符合现代编程需求,建议优先使用:

  • 不可变性:所有类都是不可变的,线程安全。
  • 清晰的命名:类名直观(如 LocalDateZonedDateTime)。
  • 丰富的 API:内置大量工具方法,减少手动计算。
  • 时区支持:彻底解决旧 API 的时区问题。

避免混用新旧 API,必要时通过 InstantZonedDateTime 进行转换。

在 Java 8 中,除了 LocalDate,还有以下常用的日期时间类,它们均位于 java.time 包中,具有不可变性和线程安全的特性,设计更符合现代编程需求:


1. LocalTime
• 功能:仅表示时间(时、分、秒、纳秒),不含日期信息。

• 示例:14:30:45.123

• 常用方法:

now() 获取当前时间。

of(时, 分, 秒) 创建特定时间。

plusHours(小时)minusMinutes(分钟) 进行时间运算。


2. LocalDateTime
• 功能:组合日期和时间,但不包含时区信息。

• 示例:2025-03-30T14:30:45

• 特点:

• 适用于不需要时区操作的场景,如日志记录、本地事件。

• 支持通过 LocalDateLocalTime 组合创建。


3. ZonedDateTime
• 功能:包含时区的完整日期时间,如 2025-03-30T14:30:45+08:00[Asia/Shanghai]

• 用途:处理跨时区应用(如航班时间、全球会议)。

• 相关类:

ZoneId:表示时区标识(如 Asia/Shanghai)。

ZoneOffset:表示与 UTC 的固定偏移量(如 +08:00)。


4. Instant
• 功能:表示时间戳(从 1970-01-01T00:00:00Z 开始的纳秒数),用于机器时间处理。

• 示例:2025-03-30T03:01:37Z(UTC 时间)。

• 转换:可与 Date 互转(Date.toInstant()Date.from(Instant))。


5. Period 与 Duration
• Period:表示以年、月、日为单位的日期差(如计算两个 LocalDate 之间相差的天数)。

Period period = Period.between(startDate, endDate);

• Duration:表示以秒、纳秒为单位的时间差(如计算两个 LocalTime 之间相差的分钟数)。

Duration duration = Duration.between(startTime, endTime);

6. DateTimeFormatter
• 功能:用于日期时间的格式化和解析,替代旧版 SimpleDateFormat

• 特点:线程安全,支持自定义格式(如 yyyy-MM-dd HH:mm:ss)。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse("2025-05-12", formatter);

如何选择合适类?
• 本地操作:优先使用 LocalDateLocalTimeLocalDateTime

• 时区敏感:使用 ZonedDateTimeZoneId

• 时间戳处理:用 Instant 与机器时间交互。

• 差值计算:Period(日期差)和 Duration(时间差)。

这些类共同构成了 Java 8 强大的日期时间处理体系,解决了旧版 DateCalendar 的缺陷(如可变性、线程不安全)。

http://www.xdnf.cn/news/5737.html

相关文章:

  • 【Linux系统】自动化构建-make/Makefile的使用
  • AI Agent开发第64课-DIFY和企业现有系统结合实现高可配置的智能零售AI Agent(上)
  • #S4U2SELF#S4U2Proxy#CVE-2021-42278/42287
  • 按指定位置或关键字批量删除工作表-Excel易用宝
  • 关系实验课--笛卡尔积
  • cURL:通过URL传输数据的命令行工具库介绍
  • 请求参数:Header 参数,Body 参数,Path 参数,Query 参数分别是什么意思,什么样的,分别通过哪个注解获取其中的信息
  • 每日算法刷题Day4 5.12:leetcode数组4道题,用时1h
  • zabbix6.4监控主机并触发邮件告警
  • Egg.js知识框架
  • Linux驱动:驱动编译流程了解
  • 向量组的维度是单个向量中元素的个数
  • Vue3的命名规范
  • 从ES5到ES6+:JavaScript语法演进与实现解析
  • 《汽车软件升级通用技术要求》 GB 44496-2024——解读
  • 仿函数和函数对象
  • Java中堆栈
  • vue实现进度条带指针
  • Elasticsearch 字段映射与数据类型
  • 面试专栏-03-Git的常用命令
  • 异构计算时代:混合编程的崛起与未来
  • 大型视频学习平台项目问题解决笔记
  • Megatron系列——流水线并行
  • KUKA机器人安装包选项KUKA.PLC mxAutomation软件
  • 产品功能更新迭代后需要重做算法备案吗?
  • Linux系统管理与编程20:Apache
  • 关于mac配置hdc(鸿蒙)
  • Nginx部署前端项目深度解析
  • 使用 Syncthing 在两台电脑之间同步文件:简单教程
  • 用drawdb.app可视化创建mysql关系表