在 Java 中,想处理日期和时间时,通常都会选用 java.util.Date 这个类进行处理。不过不知道是设计者在当时没想好还是其它原因,在 Java 1.0 中引入的这个类,大部分的 API 在 Java 1.1 中就被标记为了 Deprecated(已过时),而这些标记为已过时的接口大部分都是一些 getter 和 setter,它们被移到了 java.util.Calendarjava.text.DateFormat 这些类里面。这样就出现了我想操作日期和时间,结果需要同时操作好几个类,给编程带来了麻烦。除此之外,java.util.Date 本身还有设计上的缺陷。诸如月份从 0 开始啦、年份从 1900 年开始推算(JavaScript 也是这个尿性),外部可以随意更改等。为了解决这些痛点,也出现了一些第三方的工具包帮助开发者,在 Java 8 中,一组新的日期与时间 API (JSR-310)被引入进来,解决了上面的种种问题。接下来,将简要介绍这一组 API,并给出我自己的一些使用建议。

JSR-310(日期与时间 API)简介

Java 8 中引入的这一套新的 API 位于 java.base 模块,java.time 包下。官方文档对这一组 API 的描述如下:

The main API for dates, times, instants, and durations.

The classes defined here represent the principle date-time concepts, including instants, durations, dates, times, time-zones and periods. They are based on the ISO calendar system, which is the de facto world calendar following the proleptic Gregorian rules. All the classes are immutable and thread-safe.

简单地说,就是把我们能想到的所有对时间的相关操作都包含进来了。这些日期/时间/时刻/时段类的实体都是不可改变(immutable)的,对它们的任何修改都将产生一个新的对象。因此也是线程安全的。

在这个包下常用的一些类/枚举列举如下

class/enum 描述
Instant 表示时间轴上的一个时刻。与 java.util.Date 可以通过 Date.toInstant()Date.from(Instant) 进行互相转化。
LocalDateTime/LocalDate/LocalTime 表示(不带时区的)日期/时间
DayOfWeek 定义了一组表示星期几的常量(e.g. TUESDAY
Month 定义了一组表示月份的常量(e.g. JULY

在 API 的命名上,该包下的 API 命名遵循如下规则:

  • of - static factory method
  • parse - static factory method focussed on parsing
  • get - gets the value of something
  • is - checks if something is true
  • with - the immutable equivalent of a setter
  • plus - adds an amount to an object
  • minus - subtracts an amount from an object
  • to - converts this object to another type
  • at - combines this object with another, such as date.atTime(time)

另外,根据 MyBatis 文档的说明,从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API),所以用上述的日期与时间 API 也是可以用来操作数据库中的时间数据的。其它的 ORM 框架(Hibernate 等)应该也提供了对这一套 API 的支持。

对于 Android 开发者来说,印象中是直到 API Level 28 才能够使用 JSR-310 这一套 API,所以对于 Android 开发者来说,使用第三方的日期时间库可能是更妥当的选择(出于应用程序的向下兼容)

使用例子

这里举一个我在开发考试系统过程中的例子。读者也可以通过阅读这篇文章获取更加完整的 API 使用的示例。

在考生登录考试系统中,需要设定他的考试开始时间和结束时间,其中开始时间默认为现在,结束时间由开始时间加上试卷上的考试时间(以分钟为单位)得到。

考试记录的实体类的部分代码如下:

@TableName("t_exam_record")
public class ExamRecordEntity implements Serializable {
private static final long serialVersionUID = 1L; /**
* 考试记录ID
*/
@TableId("id")
private Long examRecordId; /**
* 计划开始时间
*/
@TableField("plan_start_time")
private Instant planStartTime; /**
* 计划结束时间
*/
@TableField("plan_end_time")
private Instant planEndTime; /**
* 实际开始时间
*/
@TableField("actual_start_time")
private Instant actualStartTime; /**
* 实际结束时间
*/
@TableField("actual_end_time")
private Instant actualEndTime; // 省略 getter、setter
}

其中当考生开始考试后,需要设置 planStartTimeplanEndTimeactualStartTime 为相应的值。如果这里使用 java.util.Date,那么除了直接用 new 创建一个实体会比较方便以外,其它的操作就变得相当麻烦了。而如果用上新式 API,只要以下几行代码即可完成:

/**
* 考试开始,保存考试记录
* @param limitTime 考试时长(分钟为单位)
* @return 考试记录的 DTO,用于后续处理
*/
private MobilePaperDTO saveNewRecord(int limitTime) {
ExamRecordEntity entity = new ExamRecordEntity(); Instant currentTime = Instant.now();
entity.setPlanStartTime(currentTime);
entity.setActualStartTime(currentTime);
entity.setPlanEndTime(currentTime.add(limitTime, ChronoUnit.MINUTES)); // 省略其它操作
}

其中 ChronoUnit 是预先定义好的一组时间单位。由于 Instant 代表的是时间轴上的一个点,只能加减上一个“时间段”(在 java.time.temporal 包下有相关定义)。如果这里选择使用 LocalDateTime 保存日期和时间,则可直接使用 LocalDateTime.plusMinutes() 方法。Java 并不支持运算符重载,不然在某些支持运算符重载的语言(例如 Kotlin)上,这套 API 可以表现的更优雅一些。

开发建议

就我个人的使用见解来说,这部分新的 API 肯定是越早引入越好。如果是老旧系统或者和别的不熟悉这套 API 的开发者协同开发,建议直接使用 Instant,因为这个就是官方用来取代 Date 的类,并且与 Date 间可以相互转化,之后再慢慢引入其它 API。

Java8 日期与时间 API的更多相关文章

  1. Java8 日期和时间API

    LocalDate.LocalTime.Instant.Duration.Period 1.1使用LocalDate和LocalTime 1.1.1LocalDate的创建方式和相关方法使用示例 @T ...

  2. Java8系列 (六) 新的日期和时间API

    概述 在Java8之前, 我们一般都是使用 SimpleDateFormat 来解析和格式化日期时间, 但它是线程不安全的. @Test public void test() { SimpleDate ...

  3. 详解Java8的日期和时间API

    详解Java8的日期和时间API 在JDK1.0的时候,Java引入了java.util.Date来处理日期和时间:在JDK1.1的时候又引入了功能更强大的java.util.Calendar,但是C ...

  4. 计算机程序的思维逻辑 (95) - Java 8的日期和时间API

    ​本节继续探讨Java 8的新特性,主要是介绍Java 8对日期和时间API的增强,关于日期和时间,我们在之前已经介绍过两节了,32节介绍了Java 1.8以前的日期和时间API,主要的类是Date和 ...

  5. Java编程的逻辑 (95) - Java 8的日期和时间API

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  6. 《Java 8 in Action》Chapter 12:新的日期和时间API

    在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类.同时这个类还有两个很大的缺点:年份的起始选择是1900年,月份的起始从0开始. 在Java 1.1中,Date类中的很多 ...

  7. Java 8 (11) 新的日期和时间API

    在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类.这个类只能以毫秒的精度表示时间.这个类还有很多糟糕的问题,比如年份的起始选择是1900年,月份的起始从0开始.这意味着你 ...

  8. java 8中新的日期和时间API

    java 8中新的日期和时间API 使用LocalDate和LocalTime LocalDate的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息.另外,它也不附带任何与时区相关的信 ...

  9. Java8 日期和时间类

    新的日期和时间API 新的日期和时间类解决了Date和Calendar类出现的问题 浅尝 LocalDate 日期类 LocalDate of = LocalDate.of(2018, 7, 13); ...

随机推荐

  1. (原创)使用C#开发高性能PLC上位机监控系统服务器应用程序

    PLC服务器监控系统的特点: 1·使用微软C#面向对象开发语言开发应用程序.2·使用了健壮性与性能良好的SUPER SOCKET服务器通信框架,实现自定义应用层通信协议,支持多台PC客户端访问服务器, ...

  2. C#安全类型转换基于convert

    using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Globalization; n ...

  3. Python中的函数(高级使用)

    一.将函数作为变量去使用 在Python中函数也是一种对象,是一种名为function的对象,所以可以将函数赋值给变量,类似整数.浮点数.列表.元组.字典赋值给变量. 我们编写如下程序: #codin ...

  4. Java线程安全面试题,你真的了解吗?

    多个线程不管以何种方式访问某个类,并且在主调代码中不需要进行同步,都能表现正确的行为. 线程安全有以下几种实现方式: 不可变 不可变(Immutable)的对象一定是线程安全的,不需要再采取任何的线程 ...

  5. 并发编程-线程,JMM,JVM,volatile

    1.线程 相信大家对线程这个名词已经很不陌生了,从刚开始学习java就接触到线程,先说说进程吧,进程就是系统分配资源的基本单位,线程是调度cpu的基本单位,进程由线程组成,一个进程至少又一个线程组成, ...

  6. 6 Linux用户和用户组管理

    Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户都必须首相像系统管理员申请账号,然后以这个账号身份进入系统 每个用户账号都拥有一个唯一的用户名和各自的口令 用户在登陆时键入 ...

  7. Kubernetes基础服务架构图

    最近看了一些kubernetes的相关资料, 简单的画了一个原理图 欢迎大家批阅

  8. hdu1677 贪心

    题意: 对于给出的n个俄罗斯套娃,要求将这n个套起来(满足w1 < w2 && h1 < h2才能套进去),最后输出最少剩下的套娃个数(尽可能去套起来) 题目分析: 朴素的 ...

  9. JDK1.8 LocalDate 使用方式;LocalDate 封装Util,LocalDate工具类(四)

    未完待续 ........ 前言:       加班了好几天,终于结束上一个坑的项目了,项目交接人员全部离职,代码一行注释没有,无人问津的情况下,完成了项目,所以好的规范真的很重要. 继续日期改写 一 ...

  10. 09-cmake语法-add_dependencies()

    在编译器的命令行上,为当前路径以及下层路径的源文件加入一些define flag.这个命令可以用来引入任何flag,但是它的原意是用来引入预处理器的定义.那些以-D或/D开头的.看起来像预处理器定义的 ...