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)LocalDateLocalDateTimeZonedDateTime 等对象转换为字符串。
  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​ 类(处理基于日期的量,如年、月、日)互补,适用于需要精确时间计算的场景。

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

核心特性
  • 时间单位 :支持秒、纳秒,自动转换为标准单位(如 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​,让时间处理从"痛点"变为"亮点",在复杂业务中真正实现时间自由

相关推荐
雷渊7 分钟前
深入分析mybatis中#{}和${}的区别
java·后端·面试
我是福福大王11 分钟前
前后端SM2加密交互问题解析与解决方案
前端·后端
亦是远方14 分钟前
2025华为软件精英挑战赛2600w思路分享
android·java·华为
花月C27 分钟前
Spring IOC:容器管理与依赖注入秘籍
java·开发语言·rpc
ylfhpy34 分钟前
Java面试黄金宝典22
java·开发语言·算法·面试·职场和发展
老友@1 小时前
Kafka 全面解析
服务器·分布式·后端·kafka
Java中文社群1 小时前
超实用!Prompt程序员使用指南,大模型各角色代码实战案例分享
后端·aigc
风象南1 小时前
Spring Boot 实现文件秒传功能
java·spring boot·后端
橘猫云计算机设计1 小时前
基于django优秀少儿图书推荐网(源码+lw+部署文档+讲解),源码可白嫖!
java·spring boot·后端·python·小程序·django·毕业设计
黑猫Teng1 小时前
Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现与实战指南
java·spring boot·后端