tulip notes
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Star-Lord

希望一天成为大师的学徒
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础与面向对象

  • 高级进阶

    • Java中的集合
    • Java泛型
    • Java中的日志
    • Java8-行为参数化与Lambda
    • Java8-函数式数据处理
    • Java8-代码优化与设计模式
    • Java8-新的日期和时间API
      • LocalDate-简单日期类
        • 代码示例
      • LocalTime-简单时间类
      • LocalDateTime
        • LocalDateTime跟String的互转
      • Duration和Period
        • Duration-时间
        • Period-日期
      • 解析格式化以及输出时间
      • 不同的时区
      • 时间戳-Instant
        • Instant在实际应用中的作用
      • 1.8前后的区别
        • 区别总结
      • 一个问题
        • MySQL 时间类型与对应场景
        • Java 类型映射
        • 关于MySql的TimeStamp
        • 推荐使用
    • Java中的引用
  • 并发合集

  • JVM合集

  • 实战与细节

  • 代码之丑与提升

  • 《Java》学习笔记
  • 高级进阶
EffectTang
2024-10-18
目录

Java8-新的日期和时间API

# Java8-新的日期和时间API

# LocalDate-简单日期类

LocalDate类只包含日期信息,即年、月和日,而不包含任何时间(时、分、秒)或时区信息。LocalDate的设计目的是为了表示纯日期的概念,不涉及具体的时间点或地理位置的影响。

LocalDate的主要特点

  1. 无时间信息:LocalDate仅包含年、月、日的信息,没有时间部分。
  2. 无时区信息:LocalDate不涉及任何时区的概念,因此它是一个纯粹的日历日期对象。
  3. 不可变性:LocalDate对象是不可变的,这意味着一旦创建后,其值不能被更改。
  4. 线程安全:由于不可变性,LocalDate对象是线程安全的。

# 代码示例

// 获取系统日期
LocalDate now = LocalDate.now();
System.out.println(now);
//  输出 2024-10-18

// 指定日期创建
LocalDate specificDate = LocalDate.of(2024, 1, 1);

//字符串解析创建
LocalDate dateFromString = LocalDate.parse("2024-01-01");

LocalDate date = LocalDate.of(2014, 3, 18);     // 2014-03-18
int year = date.getYear();   
// 2014
Month month = date.getMonth();    
// MARCH
int day = date.getDayOfMonth();   
// 18
DayOfWeek dow = date.getDayOfWeek();    
 // TUESDAY
int len = date.lengthOfMonth();    
 // 31 (days in March)
boolean leap = date.isLeapYear();    // false (not a leap year)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# LocalTime-简单时间类

它是专门用于表示时间(即时、分、秒),而不包含日期信息或时区信息。跟localDate类似,它——LocalTime对象也是不可变的,这意味着一旦创建后,其值不能被修改,从而保证了线程安全性。

你可以使用of重载的两个工厂方法创建LocalTime的实例。第一个重载函数接收小时和分钟,第二个重载函数同时还接收秒。

LocalTime time = LocalTime.of(17, 20, 20);     // 17:20:20
int hour = time.getHour();             
// 17 
int minute = time.getMinute();    // 20
int second = time.getSecond();     // 20

// 
LocalTime time = LocalTime.parse("17:45:20");

1
2
3
4
5
6
7
8
9

# LocalDateTime

localDateTime这个复合类从名称上看,就能明白其作用,它是LocalDate和LocalTime的合体,同时表示了日期和时间,但请注意,它也不带有时区信息,你可以直接创建,也可以通过合并日期和时间对象构造。

// 2014-03-18T13:45:20
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(13, 45, 20);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
1
2
3
4
5
6

当然,它也可以直接转换成localDate或者localTime。

LocalDate date1 = dt1.toLocalDate();     // 2014-03-18
LocalTime time1 = dt1.toLocalTime();    // 13:45:20
1
2

# LocalDateTime跟String的互转

	LocalDateTime now = LocalDateTime.now();
  DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  String format = now.format(dateTimeFormatter);
  System.out.println("当前时间为:"+format);

  String temp = "2023-04-07 09:09:09";
  LocalDateTime parse = LocalDateTime.parse(temp, dateTimeFormatter);
  System.out.println("parse:"+parse);
1
2
3
4
5
6
7
8

最后再补一个时间戳转LocalDateTime

				// 创建 Timestamp 对象
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());

        // 转换为 LocalDateTime
        LocalDateTime localDateTime = timestamp.toLocalDateTime();

        // 格式化为字符串
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDate = localDateTime.format(formatter);

        // 输出结果
        System.out.println("时间戳转字符串: " + formattedDate);
1
2
3
4
5
6
7
8
9
10
11
12

# Duration和Period

前面介绍的都是某个时间点,但时间还有一种表示,就是一段。

# Duration-时间

Duration类是java.time包的一部分,用于表示两个时间点之间的时间间隔。Duration类可以用来衡量两个时刻之间的时间差,而不涉及具体的日期或时间点。它主要关注的是时间跨度,如秒、毫秒、分钟、小时等。

LocalTime startTime = LocalTime.of(12, 0);
LocalTime endTime = LocalTime.of(13, 30);
Duration duration = Duration.between(startTime, endTime);
System.out.println("Duration between start and end times: " + duration.getSeconds() + " seconds");
1
2
3
4

或者直接设置时长。创建一个指定小时数、分钟数、纳秒数等的Duration对象:

Duration twoHours = Duration.ofHours(2);
Duration fiveMinutes = Duration.ofMinutes(5);
Duration tenNanos = Duration.ofNanos(10);
1
2
3

转换成长整型:

long seconds = duration.getSeconds();
long minutes = Math.abs(duration.toMinutes());
long hours = Math.abs(duration.toHours());
1
2
3

# Period-日期

Period类同样是java.time包的一部分,用于表示两个日期之间的间隔,即表示一段时间的长度,但它专注于日期而非时间点。Period类主要用于表示日期间隔,例如年、月和日的数量差异。

LocalDate startDate = LocalDate.of(2024, 1, 1);
LocalDate endDate = LocalDate.of(2024, 12, 31);
Period period = Period.between(startDate, endDate);
System.out.println("Period between start and end dates: " + period.getYears() + " years, " + period.getMonths() + " months, " + period.getDays() + " days");
1
2
3
4

获取日期的各个部分:

int years = period.getYears();
int months = period.getMonths();
int days = period.getDays();
1
2
3

注意:以上的5种时间类,它们的对象是不可变的,这意味着一旦创建后,其值不会改变。性质类似String。

# 解析格式化以及输出时间

DateTimeFormatter是java.time包中的一个类,用于格式化和解析日期时间字符串。它是现代日期时间API的一部分,和DateFormat(java.util包下的传统日期时间API)相比, 它更加符合现代编程的需求。

同时它的对象也是不可变的,因此是线程安全的。此外,它还支持更多的格式化选项。

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter);
System.out.println("Formatted date and time: " + formattedDateTime);

LocalDateTime parsedDateTime = LocalDateTime.parse("2024-01-01 13:45:30", formatter);
System.out.println("Parsed date and time: " + parsedDateTime);
1
2
3
4
5
6
7

一些其它格式:

LocalDate date = LocalDate.of(2020, 3, 18);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);    //  20200318
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);    //  2020-03-18
1
2
3

# 不同的时区

时间除了年月日,时分秒,这些数字部分,它还有一个部分————时区。

Java 8中,ZoneId是java.time包中的一个类,用于表示时区。ZoneId类提供了管理和获取时区的功能,可以用来处理不同地区的日期时间差异,特别是在涉及跨时区的应用程序中尤为重要。新的java.time.ZoneId类是老版java.util.TimeZone的替代品。它的设计目标就是要让你无需为时区处理的复杂和繁琐而操心。

ZoneId表示一个时区,可以用来描述地理位置的时区,如"Asia/Shanghai"、"America/New_York"等。

每个特定的ZoneId对象都由一个地区ID标识,使用ZoneId.of方法可以创建一个指定时区的ZoneId对象,比如:

ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
// 获取系统默认时区
ZoneId defaultZone = ZoneId.systemDefault();
1
2
3

地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构(IANA)的时区数据库提供。获取后,我们就可以使用ZoneId与其他日期时间类一起使用,如ZonedDateTime、LocalDateTime等,以便进行更复杂的日期时间表达以及计算,还有时区转换。

// 当前本地日期时间
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("Local Date Time: " + localDateTime);

// 将本地日期时间转换为特定时区的日期时间
ZoneId newYorkZone = ZoneId.of("America/New_York");
ZonedDateTime newYorkZonedDateTime = ZonedDateTime.of(localDateTime, newYorkZone);
System.out.println("New York Date Time: " + newYorkZonedDateTime);

// 将ZonedDateTime转换为另一个时区
ZoneId londonZone = ZoneId.of("Europe/London");
ZonedDateTime londonZonedDateTime = newYorkZonedDateTime.withZoneSameInstant(londonZone);
System.out.println("London Date Time: " + londonZonedDateTime);
1
2
3
4
5
6
7
8
9
10
11
12
13

再来一个简单例子:

// 创建指定时区的ZoneId对象
ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
// 获取时区信息
String id = shanghaiZone.getId();
String displayName = shanghaiZone.getDisplayName(TextStyle.FULL_STANDALONE, Locale.CHINA);
System.out.println("Shanghai Zone ID: " + id + ", Display Name: " + displayName);

// 创建一个指定时区的ZonedDateTime对象
ZonedDateTime zonedDateTime = ZonedDateTime.of(2024, 1, 1, 10, 0, 0, 0, shanghaiZone);
System.out.println("Shanghai Time: " + zonedDateTime);

1
2
3
4
5
6
7
8
9
10
11

当然,zoneID结合localDate 或者LocalDateTime的方法(API)还有,这里就不一一展示了。

提示:为了兼容以前的旧方法,开发者也做了努力。你可以通过Java 8的新方法toZoneId将一个老的时区对象转换为ZoneId:

ZoneId zoneId = TimeZone.getDefault().toZoneId();
1

# 时间戳-Instant

我们习惯于以星期几、几号、几点、几分这样的方式理解日期和时间。毫无疑问,这种方式对于计算机而言并不容易理解。从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是新的java.time.Instant类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。

更简单的讲,它就是时间戳。它————Instant类是java.time包的一部分,用于表示时间轴上的一个瞬间点,通常指的是Unix纪元以来的秒数加上纳秒数的组合。Instant类非常适合用来表示与特定时区无关的时间戳,常用于网络传输或存储时间戳。

// 使用Instant.now()方法获取当前时间的Instant对象:
Instant now = Instant.now();

//  获取时间戳的各个部分
long epochSecond = now.getEpochSecond(); // 获取秒数
int nanoOfSecond = now.getNano(); // 获取纳秒数

// 转换为其他时区的时间
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime zonedDateTime = now.atZone(zoneId);
System.out.println("In Shanghai: " + zonedDateTime);
1
2
3
4
5
6
7
8
9
10
11

# Instant在实际应用中的作用

  1. 网络传输:在网络传输中,通常使用Instant来表示服务器端的时间戳,这样可以保证时间戳的一致性和准确性。
  2. 数据库存储:在存储时间戳时,可以使用Instant来表示一个精确的时间点,然后在读取时根据需要转换为特定时区的时间。
  3. 定时任务:在定时任务中,可以使用Instant来表示未来某一时刻的任务执行时间,然后根据当前时间计算剩余时间。
  4. 日志记录:在日志记录中,使用Instant可以记录事件发生的确切时间点,方便后续分析。

# 1.8前后的区别

以下是关于1.8之前和之后用于日期时间处理的主要包及其区别。

1.8之前:

  • 对应的包:java.util.Date 和 java.util.Calendar

1.8及、之后

  • 对应的包:java.time包

# 区别总结

特性 Java 1.8之前 (java.util.Date, java.util.Calendar) Java 1.8及之后 (java.time.*)
不可变性 可变对象,非线程安全 不可变对象,线程安全
时区处理 处理复杂,容易出错 提供了专门的ZonedDateTime类,易于理解
API友好性 API设计不够直观 更加现代化和直观的设计
性能与准确性 存在精度丢失的风险 高精度,支持纳秒级别
扩展性 功能有限 强大的扩展能力,如Duration, Period

总的来说,Java 8引入的新日期时间API极大地改善了开发者处理日期和时间的方式,使得代码更加简洁、易读且不易出错。截至2025年,对于任何新的Java项目,推荐优先考虑使用java.time包中的类来进行日期和时间的操作。

# 一个问题

那么后续我们在进行数据库设计时,遇到对sql表设计时间字段时,该如何选择呢?它每一种类型又对应着Java中的哪种类型?

以下是2个可供参考的分析表:

# MySQL 时间类型与对应场景

MySQL 类型 范围/特性 适用场景
DATE 仅日期,格式 YYYY-MM-DD 仅需存储日期(如生日、事件日期)
TIME 仅时间,格式 HH:MM:SS[.微秒] 仅需存储时间(如会议开始时间)
DATETIME 日期+时间,范围 1000-01-01 00:00:00 到 9999-12-31 23:59:59,无时区 需要存储与时区无关的日期时间(如用户注册时间、计划任务时间)
TIMESTAMP 日期+时间,范围 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC,自动转换时区 需要自动时区转换或记录行更新时间(如日志时间、订单更新时间)

# Java 类型映射

MySQL 类型 推荐 Java 类型 说明
DATE java.time.LocalDate 直接映射日期,无需时区信息。
TIME java.time.LocalTime 直接映射时间,支持微秒精度。
DATETIME java.time.LocalDateTime 适合存储固定时间(如用户设定的时间),与时区无关。
TIMESTAMP java.time.Instant 或 java.time.OffsetDateTime 适合存储时间点(如日志记录),需处理时区。

# 关于MySql的TimeStamp

如果serverTimezone=Asia/Shanghai 设置正确,Java代码层面是否可以用LocalDateTime,而数据库使用TimeStamp?

答案是:是的。

如果在 JDBC 连接中正确设置了 serverTimezone=Asia/Shanghai,完全可以在 Java 代码中使用 LocalDateTime 类型,而数据库使用 TIMESTAMP 字段。但需要注意以下关键点:

MySQL 的 TIMESTAMP 类型会自动将存储的时间转换为 UTC,并在读取时根据连接的时区转换回本地时间。 当 JDBC 连接配置了 serverTimezone=Asia/Shanghai 时,驱动会完成以下行为:

  1. 写入时:将 Java 的 LocalDateTime(如 2023-10-01 08:00:00,视为上海时间)转换为 UTC 时间(2023-10-01 00:00:00),存储到数据库。
  2. 读取时:将数据库的 UTC 时间(2023-10-01 00:00:00)转换为上海时间(2023-10-01 08:00:00),再映射到 Java 的 LocalDateTime。

只要时区配置一致,LocalDateTime 和 TIMESTAMP 的转换是透明且准确的。

  • 配置--在连接字符串中明确指定时区:
# application.properties(Spring Boot 示例)
spring.datasource.url=jdbc:mysql://localhost:3306/db_name?serverTimezone=Asia/Shanghai
1
2

# 推荐使用

Instant(推荐)

  • 特点: 直接表示 UTC 时间点,与数据库 TIMESTAMP 的存储机制(UTC 时间戳)完全一致。
  • 映射示例:
@Entity
public class Event {
    @Column(name = "event_time")
    private Instant eventTime; // 自动映射到 TIMESTAMP
}
1
2
3
4
5
  • 行为:
    • 写入时:Instant(UTC 时间)直接存储为数据库的 UTC 时间戳。
    • 读取时:数据库的 UTC 时间戳直接转为 Instant。
上次更新: 2025/05/21, 15:29:11
Java8-代码优化与设计模式
Java中的引用

← Java8-代码优化与设计模式 Java中的引用→

最近更新
01
springboot中发送http
06-17
02
集成Redis及注意事项
06-08
03
Vue项目发布
06-04
更多文章>
Theme by Vdoing | Copyright © 2023-2025 EffectTang
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式