Java LocalDateTime 详解:基础、格式化、解析与实用用法

目次

1. 目标受众及学习内容

您是否曾在使用 Java 处理日期和时间时,对 LocalDateTime 类的使用感到困惑?本文面向 从 Java 初学者到积极开发企业系统的工程师,细致讲解 LocalDateTime 的基础概念到实际项目中的使用方法。

本文您将收获什么

  • 了解 LocalDateTime 的基本结构和特性
  • 通过具体示例学习如何创建、转换、格式化以及对日期时间进行算术运算
  • 理解 LocalDateTime 与传统 API(如 Date、Calendar)的区别,以及何时使用各自的 API
  • 学会处理常见场景,如数据库集成和常见错误的应对
  • 避免开发中的常见陷阱,高效安全地处理日期时间逻辑

推荐阅读对象

  • 想要在 Java 中安全、简洁地处理日期时间的开发者
  • 希望系统掌握 LocalDateTime 的全部用法的读者
  • 寻求日期时间管理最佳实践,以用于系统设计与开发的工程师
  • 使用 MySQL、PostgreSQL 等数据库的开发者
  • 正在从传统 API(Date / Calendar)迁移的任何人

阅读本文后,您将拥有足够的知识和信心,摆脱对 Java 中日期时间处理的担忧。让我们先从解释 LocalDateTime 的基础以及它与其他常见类的区别开始。

2. 什么是 LocalDateTime?基础及与其他类的区别

LocalDateTime 基本概述

LocalDateTime 是 Java 8 在 java.time 包中引入的现代日期时间 API 的一部分。它的核心特性是 同时处理日期和时间,能够存储年、月、日、时、分、秒以及纳秒等信息。

与传统的 java.util.DateCalendar 等 API 不同,LocalDateTime 不包含时区信息。因此,它非常适合表示诸如 “2025 年 7 月 10 日 15:30:00” 这类不涉及时区的本地日期时间,例如计划的事件或记录。

另一个重要特性是 LocalDateTime 不可变且线程安全。对实例的任何修改都会返回一个新对象,使其在多线程环境下使用安全可靠。

与传统 API 及其他日期时间类的区别

Java 提供了多种日期时间类,各自承担不同的职责。下表概括了它们的区别及典型使用场景。

ClassTime ZoneManaged DataMain Use Case
LocalDateTimeNoDate and timeRepresenting local date-time values
LocalDateNoDate onlyWhen only the date is needed
LocalTimeNoTime onlyWhen only the time is needed
ZonedDateTimeYesDate, time, and time zoneWhen explicit time zone handling is required
OffsetDateTimeYes (e.g., +09:00)Date, time, and offsetAPIs or systems sensitive to time differences
Date / CalendarVariesDate and timeLegacy APIs (not recommended today)

关键要点

  • 当涉及时区时,请使用 ZonedDateTimeOffsetDateTime
  • 只需要日期或时间时,可分别使用 LocalDateLocalTime
  • 当需要在本地(无时区)管理日期和时间时,使用 LocalDateTime

LocalDateTime 的典型使用场景

  • 调度系统和任务截止时间
  • 本地时间的日志与审计记录
  • 与数据库 DATETIME 列的集成

在跨服务器或跨地区用户的场景下,时区处理变得尤为关键。此时,请考虑使用 ZonedDateTime

3. 如何创建 LocalDateTime 实例(附代码示例)

在学习 LocalDateTime 时,首先要掌握的就是如何创建实例。本节将介绍最常用的创建方式,并配以实用示例。

3-1. 获取当前日期和时间(now)

最简单的用法是获取 当前本地日期和时间。虽然不包含时区信息,但数值基于系统默认时区。

import java.time.LocalDateTime;

LocalDateTime now = LocalDateTime.now();
System.out.println(now); // Example: 2025-07-10T15:30:45.123

3-2. 创建指定的日期和时间(of)

要创建特定的日期和时间,请使用 of() 方法。您可以指定到秒和纳秒的值(纳秒是可选的)。

LocalDateTime dateTime = LocalDateTime.of(2025, 7, 10, 15, 30, 0);
System.out.println(dateTime); // 2025-07-10T15:30

3-3. 从字符串创建(parse)

LocalDateTime 也可以从 ISO-8601 格式的字符串(例如 "2025-07-10T15:30:00")或自定义格式的字符串创建。

使用标准 ISO 格式:

LocalDateTime parsed = LocalDateTime.parse("2025-07-10T15:30:00");
System.out.println(parsed); // 2025-07-10T15:30

使用自定义格式(配合 DateTimeFormatter):

import java.time.format.DateTimeFormatter;

String input = "2025/07/10 15:30:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime parsedCustom = LocalDateTime.parse(input, formatter);
System.out.println(parsedCustom); // 2025-07-10T15:30

3-4. 常见错误:DateTimeParseException

在使用 parse() 时经常遇到的错误是 DateTimeParseException。主要原因是输入字符串的格式与格式化器不匹配。

示例:

LocalDateTime.parse("2025/07/10 15:30:00");
// Error: not in ISO-8601 format

解决方案:

  • 如果格式不是 ISO-8601,始终指定 DateTimeFormatter
  • 尽可能提前验证输入字符串。

小结

  • 使用 LocalDateTime.now() 获取当前日期和时间
  • 使用 of() 创建特定的日期时间
  • 使用 parse() 配合 DateTimeFormatter 解析字符串
  • 确保格式一致以避免解析错误

4. 格式化 LocalDateTime 并转换为字符串

在 Java 中处理日期和时间数据时,通常需要关注 显示格式输入/输出格式。虽然 LocalDateTime 默认输出 ISO-8601 格式(例如 2025-07-10T15:30:00),但实际应用常常需要自定义格式。本节将说明如何格式化 LocalDateTime 值以及需要注意的事项。

4-1. 默认输出和 ISO-8601 格式

当您直接使用 System.out.println() 输出 LocalDateTime 实例时,它会以 ISO-8601 格式 YYYY-MM-DDTHH:MM:SS 显示。T 字符是 ISO 标准定义的日期与时间之间的分隔符。

LocalDateTime now = LocalDateTime.now();
System.out.println(now); // Example: 2025-07-10T15:30:45.123

4-2. 转换为自定义格式(使用 DateTimeFormatter)

在业务应用和数据库集成中,通常需要 自定义或地区特定的格式。此时请使用 DateTimeFormatter 类。

示例:日本常用的模式

import java.time.format.DateTimeFormatter;

LocalDateTime dateTime = LocalDateTime.of(2025, 7, 10, 15, 30, 0);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String formatted = dateTime.format(formatter);

System.out.println(formatted); // 2025/07/10 15:30:00

您可以自由定义其他格式,例如:

  • "yyyy年MM月dd日 HH時mm分ss秒"
  • "yyyyMMdd_HHmmss"

4-3. 输出中是否包含 “T” 的情况

  • 出现 “T”:使用 toString()DateTimeFormatter.ISO_LOCAL_DATE_TIME 时。
  • 去除 “T”:通过指定自定义格式模式即可。

示例:不含 “T” 的输出

DateTimeFormatter noT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(dateTime.format(noT)); // 2025-07-10 15:30:00

4-4. 将字符串转换回 LocalDateTime

如第 3 节所述,将自定义格式的字符串转换回 LocalDateTime 需要使用 DateTimeFormatter 配合 parse()

String input = "2025/07/10 15:30:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime parsed = LocalDateTime.parse(input, formatter);
System.out.println(parsed); // 2025-07-10T15:30

摘要

  • 默认输出遵循 ISO-8601(带 “T”)
  • 使用 DateTimeFormatter 进行 自定义输出格式
  • 使用格式化器安全地 将字符串解析为 LocalDateTime
  • 灵活定制格式以满足业务和集成需求

5. 添加、减法和比较日期时间(实践中常见)

在实际应用中,常常需要执行诸如“计算几天后的日期”或“比较两个日期时间值”等操作。LocalDateTime 提供了直观的 API 来完成这些操作。

5-1. 添加和减去日期时间值(plus / minus)

LocalDateTime 提供了丰富的方法用于添加和减去时间单位。以下是一些常用示例。

添加示例:

LocalDateTime base = LocalDateTime.of(2025, 7, 10, 15, 30, 0);
LocalDateTime plusDays = base.plusDays(5);        // 5 days later
LocalDateTime plusHours = base.plusHours(3);      // 3 hours later
LocalDateTime plusMonths = base.plusMonths(1);    // 1 month later

System.out.println(plusDays);   // 2025-07-15T15:30
System.out.println(plusHours);  // 2025-07-10T18:30
System.out.println(plusMonths); // 2025-08-10T15:30

减法示例:

LocalDateTime minusDays = base.minusDays(2);        // 2 days earlier
LocalDateTime minusMinutes = base.minusMinutes(45); // 45 minutes earlier

System.out.println(minusDays);    // 2025-07-08T15:30
System.out.println(minusMinutes); // 2025-07-10T14:45

5-2. 比较日期时间值(isBefore、isAfter、equals)

要判断一个日期时间是早于、晚于还是等于另一个日期时间,请使用以下方法。

LocalDateTime a = LocalDateTime.of(2025, 7, 10, 10, 0, 0);
LocalDateTime b = LocalDateTime.of(2025, 7, 10, 15, 0, 0);

System.out.println(a.isBefore(b)); // true
System.out.println(a.isAfter(b));  // false
System.out.println(a.equals(b));   // false

5-3. 计算差异(Duration 与 Period)

当需要计算两个日期时间值之间的差异时,根据要衡量的内容在 DurationPeriod 之间进行选择。

  • Duration:用于基于时间的差异(秒、分钟、小时)
  • Period:用于基于日期的差异(年、月、日)

示例:Duration(时间差)

import java.time.Duration;

LocalDateTime start = LocalDateTime.of(2025, 7, 10, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 7, 10, 15, 0, 0);

Duration duration = Duration.between(start, end);
System.out.println(duration.toHours());   // 5
System.out.println(duration.toMinutes()); // 300

示例:Period(日期差)

import java.time.Period;

LocalDateTime dateTime1 = LocalDateTime.of(2025, 7, 10, 0, 0, 0);
LocalDateTime dateTime2 = LocalDateTime.of(2025, 8, 5, 0, 0, 0);

// Convert to LocalDate before calculating the difference
Period period = Period.between(dateTime1.toLocalDate(), dateTime2.toLocalDate());
System.out.println(period.getMonths()); // 0
System.out.println(period.getDays());   // 26

小结

  • 使用 plusminus 进行 简便的算术运算
  • 使用 isBeforeisAfter 比较日期时间值
  • 使用 Duration 处理基于时间的差异,使用 Period 处理基于日期的差异
  • 将这些 API 组合使用,使业务逻辑保持简洁易读

6. 将 LocalDateTime 与其他类和数据库类型转换

在与业务系统或已有应用集成时,将 LocalDateTime 转换为其他日期时间类或数据库类型是非常常见的。本节总结了常用的转换模式及需要注意的要点。

6-1. 在 LocalDate 与 LocalTime 之间转换

虽然 LocalDateTime 同时表示日期和时间,但在许多情况下只需要处理日期或时间之一。

LocalDateTime → LocalDate / LocalTime

LocalDateTime dateTime = LocalDateTime.of(2025, 7, 10, 15, 30, 0);

LocalDate date = dateTime.toLocalDate();
LocalTime time = dateTime.toLocalTime();

System.out.println(date); // 2025-07-10
System.out.println(time); // 15:30

LocalDate / LocalTime → LocalDateTime

LocalDate date = LocalDate.of(2025, 7, 10);
LocalTime time = LocalTime.of(15, 30);

LocalDateTime dateTime = LocalDateTime.of(date, time);
System.out.println(dateTime); // 2025-07-10T15:30

6-2. Converting with java.util.Date, Calendar, and java.sql.Timestamp

When working with legacy APIs or JDBC, you may need to convert between LocalDateTime and older date-time types such as Date or Timestamp.

LocalDateTime → java.sql.Timestamp

import java.sql.Timestamp;
import java.time.LocalDateTime;

LocalDateTime dateTime = LocalDateTime.now();
Timestamp timestamp = Timestamp.valueOf(dateTime);
System.out.println(timestamp); // Example: 2025-07-10 15:30:00.123

java.sql.Timestamp → LocalDateTime

Timestamp timestamp = Timestamp.valueOf("2025-07-10 15:30:00");
LocalDateTime dateTime = timestamp.toLocalDateTime();
System.out.println(dateTime); // 2025-07-10T15:30

Converting java.util.Date or Calendar requires an intermediate Instant

Date date = new Date();
LocalDateTime dateTime =
    date.toInstant()
        .atZone(ZoneId.systemDefault())
        .toLocalDateTime();

6-3. Mapping to Database DATETIME Types (MySQL / PostgreSQL)

LocalDateTime works very well with DATETIME columns in MySQL and PostgreSQL. Using JDBC drivers, you can convert smoothly via setTimestamp and getTimestamp.

  • MySQL / PostgreSQL DATETIME ↔ Java LocalDateTime or java.sql.Timestamp
  • When reading: use getTimestamp()toLocalDateTime()
  • When writing: convert with Timestamp.valueOf(LocalDateTime) and use setTimestamp()

Important: Be careful with time zone management

  • DATETIME columns in MySQL and PostgreSQL do not store time zone information.
  • It is critical to keep a consistent time zone policy within the application.
  • If strict time zone control is required, consider TIMESTAMP WITH TIME ZONE or using ZonedDateTime .

6-4. Converting with ZonedDateTime and OffsetDateTime

When time zone information is required, conversions between LocalDateTime and ZonedDateTime are commonly used.

LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zone = ZoneId.of("Asia/Tokyo");

ZonedDateTime zonedDateTime = localDateTime.atZone(zone);
System.out.println(zonedDateTime); // 2025-07-10T15:30+09:00[Asia/Tokyo]

LocalDateTime backToLocal = zonedDateTime.toLocalDateTime();
System.out.println(backToLocal); // 2025-07-10T15:30

Summary

  • LocalDateTime can be easily converted to and from other date-time classes and database types
  • JDBC integration works smoothly via Timestamp
  • Use ZonedDateTime or OffsetDateTime when time zone handling is required
  • Ensure time zone consistency when integrating with databases

7. Practical Use Cases and Quick Reference by Scenario

This section organizes real-world use cases for LocalDateTime and helps you choose the appropriate class depending on the situation.

7-1. Common Practical Use Cases

(1) Task and Schedule Management Systems

LocalDateTime is ideal when managing schedules and deadlines that require both date and time. It allows intuitive handling of task start and end times.

LocalDateTime deadline =
    LocalDateTime.of(2025, 7, 31, 23, 59, 59);

(2) Attendance and Time Tracking

Clock-in and clock-out records require both date and time. Integration with database DATETIME columns is straightforward.

LocalDateTime clockIn = LocalDateTime.now();

(3) Logging and Audit Trails

System logs and error histories often record event timestamps using LocalDateTime. It is suitable when time zone adjustments are unnecessary or logs are internal to the application.

7-2. 按使用场景快速参考表

Use CaseRecommended ClassReason
Store local date and timeLocalDateTimeBest choice when time zones are not required
Date onlyLocalDateCalendars, birthdays, etc.
Time onlyLocalTimeAlarms, business hours
Explicit time zone managementZonedDateTimeMulti-region systems
Use UTC or offsetsOffsetDateTimeAPIs and external integrations

7-3. 何时需要时区,何时不需要

不需要时区的典型情况

  • 仅在应用内部使用的日期时间值
  • 单一地点系统(例如,仅面向国内的服务)

需要时区的典型情况

  • 涉及多个地区或国际用户的系统
  • 在不同时区运行的服务器
  • 根据用户所在位置以不同方式显示时间的应用程序

决策指南

问自己:“这个日期时间是否表示一个绝对的时间点?”如果是,请使用 ZonedDateTimeOffsetDateTime

7-4. 简单类选择流程

  1. 日期时间是否需要时区感知?
  • 是 → ZonedDateTimeOffsetDateTime
  • 否 → 前往步骤 2
  1. 你需要仅日期、仅时间,还是两者都要?
  • 仅日期 → LocalDate
  • 仅时间 → LocalTime
  • 日期和时间 → LocalDateTime

小结

  • LocalDateTime 适用于在不涉及时区的情况下管理本地日期和时间
  • 选择合适的类可以简化系统设计和维护
  • 清晰地了解需求有助于避免未来的错误和不一致

8. 常见错误、故障排除与解决方案

在使用 LocalDateTime 时,开发者常会遇到重复出现的错误或困惑来源。本节以问答形式总结常见问题及其解决方案,帮助你在出现问题时快速响应。

Q1. 出现 DateTimeParseException

原因

  • 当传入 LocalDateTime.parse() 的字符串与预期格式不匹配时,会抛出此异常。
  • 非 ISO-8601 格式的字符串(例如 "2025-07-10T15:30:00")需要使用 DateTimeFormatter

解决方案

  • 始终确认字符串格式匹配,必要时使用 DateTimeFormatter
    String input = "2025/07/10 15:30:00";
    DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
    LocalDateTime.parse(input, formatter); // OK
    

Q2. 注意 NullPointerException

原因

  • 对为 null 的 LocalDateTime 引用调用方法。

解决方案

  • 在使用前检查是否为 null。
  • 将值包装在 Optional 中也很有效。

Q3. 时区处理不当

原因

  • LocalDateTime 不保存时区信息,系统或数据库时区的变化可能导致意外结果。

解决方案

  • 统一服务器和数据库的时区设置。
  • 在需要时区精确性的情况下使用 ZonedDateTimeOffsetDateTime

Q4. 与数据库集成时日期时间值偏移

原因

  • 数据库列类型或时区设置与 Java 应用设置不匹配。

解决方案

  • 在使用 DATETIMELocalDateTime 时,明确时区基准。
  • 在需要严格精确性时,考虑使用 TIMESTAMP WITH TIME ZONEZonedDateTime

Q5. 精度损失(毫秒/纳秒)

原因

  • 某些 JDBC 驱动或数据库仅支持毫秒精度,导致纳秒被截断。

解决方案

  • 确认系统需求中是否可以接受此精度损失。
  • 若必须保留纳秒精度,请采用其他处理方式。

Q6. 从旧版 API(Date、Calendar)转换时的错误

原因

  • 直接尝试将 DateCalendar 转换为 LocalDateTime

解决方案

  • 始终通过 InstantZoneId 进行转换。
    Date date = new Date();
    LocalDateTime dateTime =
        date.toInstant()
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime();
    

实用开发技巧

  • 注意 格式、时区和空值检查,以防止大多数问题
  • 在与数据库或其他系统集成时,始终验证 类型和配置的一致性
  • 当出现错误时,仔细阅读异常信息,并检查输入值、转换逻辑和环境设置

9. 常见问题解答 (FAQ)

本节回答了关于 LocalDateTime 在实际开发场景中常见的疑问。可将其作为排查或设计系统时的快速参考。

Q1. LocalDateTime 能处理时区吗?

不行。LocalDateTime 不存储时区信息。如果需要管理绝对时间点,请使用 ZonedDateTimeOffsetDateTime

Q2. 将 Date / Calendar 迁移到 LocalDateTime 的最安全方式是什么?

不能直接转换 DateCalendar。请始终通过 InstantZoneId 进行转换。

Date date = new Date();
LocalDateTime dateTime =
    date.toInstant()
        .atZone(ZoneId.systemDefault())
        .toLocalDateTime();

Q3. 为什么输出有时会出现 “T”?

“T”字符是 ISO‑8601 标准的分隔符。当使用 toString()DateTimeFormatter.ISO_LOCAL_DATE_TIME 时会出现。若要去除它,请指定自定义的格式模式。

Q4. 将值存入数据库时需要注意什么?

数据库的 DATETIME 列不存储时区信息。确保应用程序始终使用统一的时区。如果需要严格的准确性,考虑使用 TIMESTAMP WITH TIME ZONEZonedDateTime

Q5. LocalDateTime 支持多少精度?

LocalDateTime 支持纳秒精度。然而,许多数据库和 JDBC 驱动仅支持毫秒,可能会截断更细的精度。

Q6. LocalDateTime 会受到夏令时 (DST) 的影响吗?

不会。LocalDateTime 本身不进行夏令时调整。如需处理 DST,请使用 ZonedDateTime

Q7. 如果只需要日期或时间该使用什么?

仅需要日期时使用 LocalDate,仅需要时间时使用 LocalTime。当日期和时间都需要时,使用 LocalDateTime

Q8. 应该如何处理异常?

仔细阅读异常信息并检查:

  • 字符串格式是否正确
  • 是否存在空值或无效值
  • 转换步骤是否实现正确

10. 总结与参考链接

本文涵盖了 LocalDateTime 的基础、实用用法、常见陷阱以及常见问题。以下是简要总结和进一步学习的有用参考。

10-1. 正确使用 LocalDateTime 的关键要点

  • LocalDateTime 是一个安全且直观的类,用于管理不含时区的本地日期和时间。 它支持算术运算、格式化、比较和解析,使用便捷。
  • 根据系统需求选择合适的日期时间类。
  • 仅需要日期时使用 LocalDate
  • 仅需要时间时使用 LocalTime
  • 当涉及时区时使用 ZonedDateTimeOffsetDateTime
  • 在与数据库或外部系统集成时,务必关注时区和格式。
  • 提前了解常见错误有助于预防问题。 请参考 FAQ 和故障排查章节快速解决。

10-2. 参考链接与文档

10-3. 开发者的最终提示

通过本文的知识,你应该不再为 LocalDateTime 的使用而苦恼。当出现新需求时,始终参考官方文档和可信的技术资源,以保持最新。

通过应用正确的概念和最佳实践,你可以让 Java 日期时间处理在实际系统中更安全、更简洁、更易维护。