Java 时间处理:轻松掌握 java.time 包

前言

在 Java 开发中,日期和时间处理一直是令人头疼的问题。传统的 Date​ 和 Calendar​ 类不仅复杂,还充满了线程安全和时区处理的坑。Java 8 引入的 java.time​ 包彻底改变了这一局面,带来了现代化、直观且功能强大的日期时间 API。

本文将带你深入了解 java.time​ 包的核心功能,从基础的 LocalDate​ 和 LocalTime​ 到强大的 Duration​ 和 Period​,再到灵活的 DateTimeFormatter​,让你轻松掌握日期时间处理的最佳实践。

‍

一、关于java.time包

1.1 简介

java.time​ 包是 Java 8 中引入的,用于处理日期和时间的新 API。它旨在克服旧版 java.util.Date​ 和 java.util.Calendar​ 类中的一些缺点,提供更现代、更简洁、线程安全和不可变的日期和时间处理方式。该 API 基于 ISO-8601 标准,并支持更多的时区、日期/时间格式以及各种常见的时间计算需求。

​

‍

1.2 核心类和接口

  • LocalDate:表示没有时区的日期(年、月、日)。例如,2025年3月28日。
  • LocalTime:表示没有时区的时间(时、分、秒)。例如,14:30:00。
  • LocalDateTime:表示没有时区的日期和时间(年、月、日、时、分、秒)。例如,2025年3月28日 14:30:00。
  • ZonedDateTime:表示带时区的日期和时间。它不仅包含日期和时间信息,还包含时区信息(例如 UTC+08:00)。
  • Instant:表示时间戳(从1970年1月1日0时0分0秒到某一时刻的秒数或毫秒数),通常用于与 Unix 时间戳和其他基于秒的时间戳的交互。
  • Duration:表示时间段,用于表示两个时间点之间的差异,精度为秒和纳秒。
  • Period:表示日期段,用于表示两个日期之间的差异,精度为年、月和日。
  • Month:表示一个月(如 JANUARY、FEBRUARY 等),是枚举类型。
  • DayOfWeek:表示一周中的某一天(如 MONDAY、TUESDAY 等),也是枚举类型。

‍

java.time包目录结构

​

‍

‍

二、核心类

2.1 LocalDate类

简介

LocalDate​ 类是 Java 8 引入的日期和时间 API(java.time 包)的一部分。它表示一个没有时间部分的日期(例如:2025-03-31),适用于处理年、月、日等信息,而不涉及时间、时区等。

LocalDate​ 代表不带时区的日期。

  • 表示不带时区的日期(年-月-日)。
  • 示例:2023-10-01。

​

‍

LocalDate​ 使用 ISO-8601 标准来处理日期,并将日期表示为 YYYY-MM-DD​ 的格式。ISO-8601日历系统是当今世界大部分地区使用的现代民用日历系统。相当于现在的公历日历体系,其中表示闰年规则永远适用。

‍

方法

常用方法

  • now():获取当前日期。
  • of(int year, int month, int dayOfMonth):创建指定日期。
  • plusDays(long days):增加天数。
  • minusMonths(long months):减少月数。
  • getYear(), getMonth(), getDayOfMonth():获取日期的各个部分。
  • isLeapYear():判断是否为闰年。
  • isBefore(LocalDate other):判断当前日期是否在指定日期之前。
  • isAfter(LocalDate other):判断当前日期是否在指定日期之后。

‍

示例

ini 复制代码
import java.time.LocalDate;
​
public class LocalDateExample {
    public static void main(String[] args) {
        // 获取当前日期
        LocalDate today = LocalDate.now();
        System.out.println("今天: " + today);
​
        // 创建指定日期
        LocalDate specificDate = LocalDate.of(2025, 3, 31);
        System.out.println("指定日期: " + specificDate);
​
        // 日期操作
        LocalDate tomorrow = today.plusDays(1);
        System.out.println("明天: " + tomorrow);
​
        LocalDate yesterday = today.minusDays(1);
        System.out.println("昨天: " + yesterday);
​
        // 判断是否为闰年
        boolean isLeapYear = today.isLeapYear();
        System.out.println("是否为闰年: " + isLeapYear);
​
        // 日期比较
        boolean isBefore = today.isBefore(specificDate);
        System.out.println("今天是否在指定日期之前? " + isBefore);
    }
}
​

​

‍

2.2 LocalTime类

简介

  • 表示不带时区的时间(小时:分钟:秒.纳秒)。
  • 示例:15:30:45.123。

‍

方法

  • 常用方法:

    • now():获取当前时间。
    • of(int hour, int minute, int second):创建指定时间。
    • plusHours(long hours):增加小时数。
    • minusMinutes(long minutes):减少分钟数。
    • getHour(), getMinute(), getSecond():获取时间的各个部分。

‍

示例

java 复制代码
import java.time.LocalTime;
​
public class LocalTimeExample {
    public static void main(String[] args) {
        LocalTime time = LocalTime.now();  // 获取当前时间
        System.out.println(time);  // 输出当前时间,格式如 14:30:00
  
        LocalTime specificTime = LocalTime.of(14, 30);  // 创建特定时间
        System.out.println(specificTime);  // 输出:14:30
    }
}

‍

​

‍

2.3 LocalDateTime类

关于

简介

LocalDateTime​ 是 java.time​ 包中的一个类,用于表示没有时区信息的日期和时间。它结合了 LocalDate​ 和 LocalTime​,可以表示年份、月份、日期、小时、分钟、秒以及纳秒的信息,但不包含时区。LocalDateTime​ 是不可变的,意味着它的实例一旦创建后就不能被修改,因此它是线程安全的。

  • 表示不带时区的日期和时间(年-月-日T小时:分钟:秒.纳秒)。
  • 示例:2023-10-01T15:30:45.123。

‍

主要特点
  • 无时区 :LocalDateTime 不包含时区信息,仅表示本地日期和时间。
  • 精确到纳秒:提供纳秒级别的精度。
  • 不可变和线程安全 :一旦创建,LocalDateTime 的值不能改变,因此是线程安全的。

‍

关联类对比
类名 描述 示例值
LocalDate​ 仅日期(无时间) 2023-10-01​
LocalTime​ 仅时间(无日期) 15:30:45​
LocalDateTime​ 日期 + 时间(无时区) 2023-10-01T15:30:45​
ZonedDateTime​ 日期时间 + 时区 2023-10-01T15:30:45+08:00[Asia/Shanghai]​
Instant​ 时间戳(基于 UTC) 2023-10-01T07:30:45Z​

‍

时间带T

在 LocalDateTime​ 类中,时间表示格式中的 T 是 ISO 8601 标准规定的日期和时间之间的分隔符。

解释:

在 ISO 8601 格式中,日期和时间通常由字母 "T" 分隔。例如:

makefile 复制代码
2025-03-28T14:30:00

在这个示例中:

  • 2025-03-28 是日期部分(年-月-日)
  • 14:30:00 是时间部分(小时:分钟:秒)

字母 T 用来清楚地标识日期部分和时间部分的分隔,避免歧义。因此,它并不代表任何特殊的含义,而仅仅是作为分隔符来表示日期和时间的结合。

‍

构造方法

LocalDateTime​ 类提供了多种构造方法来创建日期时间实例:

  • LocalDateTime.now():获取当前系统的日期和时间(根据默认时区)。
  • LocalDateTime.of(int year, int month, int dayOfMonth, int hour, int minute):创建指定日期和时间的 LocalDateTime。
  • LocalDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second):创建指定日期、时间和秒的 LocalDateTime。
  • LocalDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond):创建指定日期、时间、秒和纳秒的 LocalDateTime。
  • LocalDateTime.parse(CharSequence text):通过字符串解析创建 LocalDateTime 对象。

‍

解析字符串(ISO 格式)

ini 复制代码
LocalDateTime dt = LocalDateTime.parse("2023-10-01T15:30:45");

组合 LocalDate + LocalTime

ini 复制代码
LocalDateTime dt = LocalDate.now().atTime(LocalTime.of(12, 0));

‍

ini 复制代码
LocalDate date1 = LocalDate.of(2023, 6, 7);
LocalTime time1 = LocalTime.of(11, 30, 0);
LocalDateTime dateTime1 = LocalDateTime.of(date1, time1);
System.out.println(dateTime1);
//2023-06-07T01:00

‍

old

ini 复制代码
LocalDateTime startTime, endTime;

在您的代码片段中,声明了两个 LocalDateTime 类型的变量 startTime 和 endTime,但是并没有进行初始化赋值操作,因此它们的值默认为 null。

‍

‍

方法

以下是 LocalDateTime​ 类的常用方法概览:

一、获取当前日期时间
方法名 描述
now()​ 获取当前的日期和时间。
now(ZoneId zone)​ 根据指定的时区获取当前日期和时间。
now(Clock clock)​ 根据指定的时钟获取当前日期和时间。
二、创建特定日期时间
方法名 描述
of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nano)​ 创建指定日期和时间的 LocalDateTime​ 对象。
of(int year, int month, int dayOfMonth, int hour, int minute, int second)​ 创建指定日期和时间的 LocalDateTime​ 对象(毫秒和纳秒为 0)。
of(int year, int month, int dayOfMonth, int hour, int minute)​ 创建指定日期和时间的 LocalDateTime​ 对象(秒、毫秒和纳秒为 0)。
of(int year, Month month, int dayOfMonth, int hour, int minute, int second, int nano)​ 使用 Month​ 枚举创建指定日期和时间的 LocalDateTime​ 对象。
三、解析和格式化
方法名 描述
parse(CharSequence text, DateTimeFormatter formatter)​ 使用指定的格式化程序解析字符串,创建 LocalDateTime​ 对象。
format(DateTimeFormatter formatter)​ 使用指定的格式化程序将 LocalDateTime​ 格式化为字符串。
四、获取日期和时间部分
方法名 描述
toLocalDate()​ 获取 LocalDateTime​ 对象的日期部分,返回一个 LocalDate​ 对象。
toLocalTime()​ 获取 LocalDateTime​ 对象的时间部分,返回一个 LocalTime​ 对象。
getYear()​ 获取年份。
getMonthValue()​ 获取月份(1-12)。
getDayOfMonth()​ 获取当月的日期(1-31)。
getHour()​ 获取小时(0-23)。
getMinute()​ 获取分钟(0-59)。
getSecond()​ 获取秒(0-59)。
getNano()​ 获取纳秒(0-999,999,999)。
五、日期时间的加减
方法名 描述
plusYears(long years)​ 增加指定的年数。
plusMonths(long months)​ 增加指定的月数。
plusWeeks(long weeks)​ 增加指定的周数。
plusDays(long days)​ 增加指定的天数。
plusHours(long hours)​ 增加指定的小时数。
plusMinutes(long minutes)​ 增加指定的分钟数。
plusSeconds(long seconds)​ 增加指定的秒数。
plusNanos(long nanos)​ 增加指定的纳秒数。
minusYears(long years)​ 减少指定的年数。
minusMonths(long months)​ 减少指定的月数。
minusWeeks(long weeks)​ 减少指定的周数。
minusDays(long days)​ 减少指定的天数。
minusHours(long hours)​ 减少指定的小时数。
minusMinutes(long minutes)​ 减少指定的分钟数。
minusSeconds(long seconds)​ 减少指定的秒数。
minusNanos(long nanos)​ 减少指定的纳秒数。
六、比较日期时间
方法名 描述
isBefore(LocalDateTime other)​ 判断此日期时间是否在指定的日期时间之前。
isAfter(LocalDateTime other)​ 判断此日期时间是否在指定的日期时间之后。
isEqual(LocalDateTime other)​ 判断此日期时间是否等于指定的日期时间。
compareTo(LocalDateTime other)​ 比较两个日期时间的大小,返回负数、零或正数。
七、时间戳转换
方法名 描述
toEpochSecond(ZoneOffset offset)​ 将此日期时间转换为从 1970-01-01T00:00:00Z 开始的秒数。
ofEpochSecond(long epochSecond, int nano, ZoneOffset offset)​ 从秒数和纳秒数创建 LocalDateTime​ 对象。
八、其他方法
方法名 描述
with(TemporalAdjuster adjuster)​ 使用指定的调整器调整日期时间。
withYear(int year)​ 设置年份。
withMonth(int month)​ 设置月份。
withDayOfMonth(int dayOfMonth)​ 设置当月的日期。
withHour(int hour)​ 设置小时。
withMinute(int minute)​ 设置分钟。
withSecond(int second)​ 设置秒。
withNano(int nano)​ 设置纳秒。
atZone(ZoneId zone)​ 将此日期时间转换为指定时区的 ZonedDateTime​ 对象。
toLocalDate()​ 获取日期部分。
toLocalTime()​ 获取时间部分。

这些方法提供了丰富的功能来处理日期和时间,包括获取当前日期时间、创建特定日期时间、解析和格式化、日期时间的加减、比较日期时间以及与时间戳的转换等。通过使用这些方法,可以更方便、更准确地处理日期时间相关的操作。

‍

示例

java 复制代码
import java.time.LocalDateTime;
​
public class LocalDateTimeExample {
    public static void main(String[] args) {
        LocalDateTime dateTime = LocalDateTime.now();  // 获取当前日期和时间
        System.out.println(dateTime);  // 输出:2025-03-28T14:30:00.123
  
        LocalDateTime specificDateTime = LocalDateTime.of(2025, 3, 28, 14, 30);  // 创建特定日期和时间
        System.out.println(specificDateTime);  // 输出:2025-03-28T14:30
    }
}
​

‍

​

‍

2.4 DateTimeFormatter

关于

简介

Java 的 DateTimeFormatter​ 类是 java.time.format​ 包中用于格式化和解析日期时间对象的核心工具,专为 java.time​(Java 8+ 引入的日期时间 API)设计。它替代了旧的 SimpleDateFormat​,具备线程安全 和更清晰的API设计,支持 ISO-8601 标准及自定义模式。

​

‍

功能
  1. 格式化(Formatting) 将 LocalDate、LocalDateTime、ZonedDateTime 等对象转换为字符串。
  2. 解析(Parsing) 将字符串解析为日期时间对象。
  3. ISO-8601 兼容 直接支持 ISO 格式的日期时间表示。
  4. 自定义模式 通过模式字符串定义灵活的日期时间格式。

‍

方法

方法 说明 示例
ofPattern(String pattern)​ 创建自定义格式 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")​
format(TemporalAccessor temporal)​ 格式化日期时间对象 localDateTime.format(formatter)​
parse(CharSequence text)​ 解析字符串为日期时间对象 LocalDate.parse("2023-10-05", formatter)​
withZone(ZoneId zone)​ 指定时区 formatter.withZone(ZoneId.of("Asia/Shanghai"))​

‍

DateTimeFormatter​ 预定义了 ISO 标准的格式化常量,例如:

  • ISO_LOCAL_DATE: yyyy-MM-dd(如 2023-10-05)
  • ISO_LOCAL_TIME: HH:mm:ss.SSS(如 15:30:45.123)
  • ISO_DATE_TIME: yyyy-MM-dd'T'HH:mm:ss.SSSZ(如 2023-10-05T15:30:45.123+08:00)

‍

常见模式符号:

通过模式字符串定义格式:

符号 含义 示例
yyyy​ 4位年份 2023​
MM​ 2位月份 10​
dd​ 2位日期 05​
HH​ 24小时制小时 15​
mm​ 分钟 30​
ss​ 秒 45​
SSS​ 毫秒 123​
Z​ 时区偏移 +0800​

‍

示例

ini 复制代码
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
​
public class Main {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
  
        // 定义格式模板
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
​
        // 格式化 LocalDateTime → String
        String formattedDate = now.format(formatter);
        System.out.println(formattedDate);
​
        // 解析 String → LocalDateTime
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        LocalDateTime parsedDate = LocalDateTime.parse("2024-10-01 15:30", formatter1);
        System.out.println(parsedDate);
    }
}

‍

​

‍

2.5 Duration类(时间间隔)

关于

简介

Java 的 ​Duration​ ​ 类是 java.time​ 包中用于表示基于时间的量 (如小时、分钟、秒、纳秒)的不可变类,专为处理时间间隔设计。它与 Period​ 类(处理基于日期的量,如年、月、日)互补,适用于需要精确时间计算的场景。

  • 表示两个时间点之间的时间间隔,单位为秒和纳秒。
  • 示例:计算两个 LocalTime 或 LocalDateTime 之间的时间差。

​

‍

核心特性
  • 时间单位 :支持秒、纳秒,自动转换为标准单位(如 1 小时 = 3600 秒)。
  • 不可变与线程安全:所有操作返回新对象,适合多线程环境。
  • ISO-8601 兼容 :默认遵循 PnYnMnDTnHnMnS 格式(如 PT8H30M 表示 8 小时 30 分钟)。
  • 精确计算:处理纳秒级精度的时间间隔。

‍

方法

方法 说明 示例
ofDays(long)​ / ofHours(long)​ 创建指定天、小时的 Duration Duration.ofHours(2)​ → PT2H​
ofMinutes(long)​ / ofSeconds(long)​ 创建分钟、秒的 Duration Duration.ofSeconds(90)​ → PT1M30S​
between(Temporal start, Temporal end)​ 计算两个时间点之间的间隔 Duration.between(startTime, endTime)​
plus(Duration)​ / minus(Duration)​ 时间间隔的加减 duration.plus(Duration.ofMinutes(10))​
toHours()​ / toMinutes()​ / toMillis()​ 转换为其他时间单位 duration.toMinutes()​ → 总分钟数
parse(CharSequence)​ 解析 ISO-8601 格式字符串 Duration.parse("PT2H30M")​ → 2小时30分钟
isNegative()​ 判断是否为负时间间隔 duration.isNegative()​

‍

示例

java 复制代码
import java.time.Duration;
import java.time.LocalDateTime;
​
public class DurationExample {
    public static void main(String[] args) {
        // 创建两个时间点
        LocalDateTime start = LocalDateTime.of(2025, 3, 31, 10, 0, 0);
        LocalDateTime end = LocalDateTime.of(2025, 3, 31, 12, 30, 0);
​
        // 计算两个时间点之间的持续时间
        Duration duration = Duration.between(start, end);
​
        // 输出持续时间
        System.out.println("Duration (in hours): " + duration.toHours());        // 2小时
        System.out.println("Duration (in minutes): " + duration.toMinutes());    // 150分钟
        System.out.println("Duration (in seconds): " + duration.getSeconds());   // 5400秒
    }
}
​

‍

​

‍

2.6 Period类(日期间隔)

关于

简介

Java 的 ​Period​ ​ 类是 java.time​ 包中用于表示基于日历的日期间隔(如年、月、日)的不可变类,专为处理与日历相关的周期设计(如"2年3个月10天")。

  • 表示两个日期之间的间隔,单位为年、月和日。
  • 示例:计算两个 LocalDate 之间的日期差。

‍

核心特性
  • 日历单位:支持年、月、日,自动处理不同月份和闰年的差异。
  • 不可变与线程安全:所有操作返回新对象,适合多线程环境。
  • ISO-8601 兼容 :默认遵循 PnYnMnD 格式(如 P1Y2M3D 表示1年2个月3天)。
  • 自然日期计算 :加减周期时考虑月份和年的实际天数(如 2024-02-28 + P1M = 2024-03-28)。

‍

方法

常用方法

方法 说明 示例
of(int years, int months, int days)​ 创建指定年、月、日的 Period Period.of(1, 2, 3)​ → P1Y2M3D​
between(LocalDate start, LocalDate end)​ 计算两个日期之间的间隔 Period.between(startDate, endDate)​
plus(Period)​ / minus(Period)​ 周期的加减 period.plus(Period.ofMonths(1))​
getYears()​ / getMonths()​ / getDays()​ 获取年、月、日的数值 period.getMonths()​ → 2
parse(CharSequence)​ 解析 ISO-8601 格式字符串 Period.parse("P3Y6M")​ → 3年6个月
isNegative()​ 判断是否为负周期 period.isNegative()​

‍

示例

示例1---创建 Period​ 对象:
java 复制代码
import java.time.Period;
​
public class PeriodExample {
    public static void main(String[] args) {
        // 使用静态方法创建 Period 对象
        Period period1 = Period.of(1, 2, 10);  // 1年2个月10天
        Period period2 = Period.ofYears(3);    // 3年
        Period period3 = Period.ofMonths(5);   // 5个月
        Period period4 = Period.ofDays(20);    // 20天
​
        // 输出 Period 对象
        System.out.println("Period 1: " + period1);  // P1Y2M10D
        System.out.println("Period 2: " + period2);  // P3Y
        System.out.println("Period 3: " + period3);  // P5M
        System.out.println("Period 4: " + period4);  // P20D
    }
}

​

‍

示例2---计算两个日期之间的差异
java 复制代码
import java.time.LocalDate;
import java.time.Period;
​
public class PeriodExample {
    public static void main(String[] args) {
        // 创建两个日期
        LocalDate startDate = LocalDate.of(2020, 1, 1);
        LocalDate endDate = LocalDate.of(2023, 3, 15);
​
        // 计算两个日期之间的差异
        Period period = Period.between(startDate, endDate);
​
        // 输出差异
        System.out.println("Years: " + period.getYears());   // 3
        System.out.println("Months: " + period.getMonths()); // 2
        System.out.println("Days: " + period.getDays());     // 14
    }
}
​

‍

​

‍

三、总结

java.time​的诞生不仅是Java对开发者痛点的回应,更是面向未来时间处理的标杆。其不可变性、线程安全与ISO标准兼容性,使其成为高可靠系统的基石。无论是替换陈旧的Date​,还是应对全球化业务的时区挑战,java.time​都以优雅的设计降低认知成本。作为开发者,掌握这一工具不仅提升代码质量,更能在分布式系统、微服务架构中规避潜在风险。拥抱java.time​,让时间处理从"痛点"变为"亮点",在复杂业务中真正实现时间自由。

​

‍

相关推荐
wuminyu几秒前
stackChunkOop内部结构与GC屏障的配合工作原理剖析
java·linux·c语言·jvm·c++
xuxigifxfh5 分钟前
牛客:HJ21 简单密码
java·开发语言·算法
小π军6 分钟前
C 和 C++ 是否都支持函数的重载?
java·c语言·c++
sp42a11 分钟前
简单的 Spring 文件上传与下载组件
java·后端·spring
步行cgn14 分钟前
为什么 Spring 建议使用构造器注入?
java·后端·spring
Nefu_lyh15 分钟前
【工作杂谈_20261012】数据平台与工程治理: Spark OOM 排查:Java Heap、Executor 与代码内存模型
java·大数据·spark
小蒜学长17 分钟前
基于Vue.js + Spring Boot的酒店客房预订系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·酒店客房预订系统
打工仔折腾 AI18 分钟前
鸿蒙平板远控电脑实测:ToDesk蓝牙键鼠、双模式鼠标与跨端剪贴板落地记录
人工智能·后端·python·性能优化·计算机外设·电脑·harmonyos
真恋寄语枫秋22 分钟前
【Java零基础入门28】Java包机制详解:package包定义、import导入、权限访问、项目分层管理
java
豆角焖肉22 分钟前
SpringBoot + Redis 缓存实战
java·spring boot·redis·后端·缓存·菜单缓存