Java 8——日期时间工具库(java.time)

时间:2022-09-24 14:15:28

一.前言

在介绍Java SE 8中新的日期时间库前,先了解下Java 8之前的日期时间工具的诟病。

在Java SE 8前,日期时间工具库在java.util包中,包括:

  • java.util.Date:表示日期和时间
  • java.util.Calendar以及其实现子类:表示各种日历系统,常用的是格林威治日历java.util.GregorianCalendar
  • java.util.TimeZone以及其实现子类:表示时区偏移量和夏令时

以及辅助其进行格式化和解析的工具库在java.text包中,包括:

  • java.text.DateFormat:格式化日期时间和解析日期时间的工具抽象类
  • java.text.SimpleDateFormat:DateDateFormat的实现

从以上的简述中,对java 8之前的日期时间库,有所宏观视觉。下面简要总结下其设计上的瑕疵和被开发者无限吐槽的诟病:

  • 从以上的api上看,java 8之前的日期时间工具库缺乏年、月、日、时间、星期的单独抽象;
  • Dater日期时间类既描述日期又描述时间,耦合,且Date不仅在java.util包中存在,在java.sql中也存在,重复名称,容易导致bug发生;
  • api的设计上晦涩,难用,不够生动,难以以自然人类的思维理解日期时间。年月日需要从Calendar中获取。q;
  • 最被开发者抱怨的是类型不安全,Calendar类中全局属性是可变的,在多线程访问时,会存在线程安全问题。SimpleDateFormat格式化和解析日期,需要使用年月日时分秒,所以持有了Calendar属性,导致其也是非线程安全;
// 以下都是Calendar中持有的全局属性
// 这些全局属性都是可变的,提供了set
protected int fields[];
transient private int stamp[];
protected long time;
protected boolean isTimeSet; // 在其子类GregorianCalendar中
private transient int[] zoneOffsets;
// setTime方法会调用此方法
// 该方法中修改了上述的很多全局属性
public void setTimeInMillis(long millis) {
// If we don't need to recalculate the calendar field values,
// do nothing.
if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
&& (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
return;
}
time = millis;
isTimeSet = true;
areFieldsSet = false;
computeFields();
areAllFieldsSet = areFieldsSet = true;
}

所以在多线程环境中使用Calendar是非线程安全,多个线程修改其属性域会发生数据一致性和可见性问题。

在DateFormat中持有了Calendar属性,用于解析和格式化日期:

// 从注释上看,Calendar用于计算日期时间域
/**
* The {@link Calendar} instance used for calculating the date-time fields
* and the instant of time. This field is used for both formatting and
* parsing.
*
* <p>Subclasses should initialize this field to a {@link Calendar}
* appropriate for the {@link Locale} associated with this
* <code>DateFormat</code>.
* @serial
*/
protected Calendar calendar;
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date); boolean useDateFormatSymbols = useDateFormatSymbols(); for (int i = 0; i < compiledPattern.length; ) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
} switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break; case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break; default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
}

format方法中设置了全局成员Calendar的time,多线程访问时每次都会改变Calendar类,导致format格式化时会出现线程安全问题。所以DateFormat和其子类SimpleDateFormat都是非类型安全。

这个可以说是被开发者极度抱怨的。所以每次在使用日期格式工具时大多数都会重新new或者使用ThreadLocal。

基于此诸多问题,java设计者终于在Java SE 8中引入了新的日期时间库。新的日期时间库的易用程度会让你振服!下面开始进入主题,Java SE 8中的日期时间库java.time。

二.概览

先认识下joda项目,joda项目包括:

  • Joda-Time - Basic types for Date and Time
  • Joda-Money - Basic types for Money
  • Joda-Beans - Next generation JavaBeans
  • Joda-Convert - String to Object conversion
  • Joda-Collect - Additional collection data structures

其中joda time是日期时间三方库,但是在java 8之前joda time其实是标准的日期时间库,其出色的语义表达,易用易于理解的api,类型安全的特性,大受开发者的追捧。而且其日历系统遵循的是IOS_8601国际标准,同时还包括其他的非标准的日历系统。支持时区、持续时间、格式化和解析功能。

在java 8之前可以依赖joda time三方库,使用其日期时间库。

但在java 8中提出了JSR 310: Date and Time API规范,该规范即新版的日期时间库java.time规约。可以说JSR-310的设计上汲取了大量的joda time的特性。新版本的日期时间库基于JSR 310: Date and Time API被开发,java.time是基于国际化标准日历系统(International Organization for Standardization)ISO_8601,同时java.time.chrono支持对全球日历系统的扩展。

JSR-310中设计的java.time包括年、月、星期、日期时间、持续时间段、瞬时、时钟、时区的抽象及处理。且api的设计上使用易读易于理解的名称和设计模式,让使用者欣然接受。而且提供旧版和新版api之间的互通以处理兼容性问题。

下面看张概览图,从宏观角度了解下java.time

  • 第一层是对年、月、月中日、星期的抽象;
  • 第二层是对日期、日期时间、时区的抽象,其中时区分为时区Id(Europe/Paris)和时区偏移量(Z/+hh:mm/-hh:mm);
  • 第三层是对区域时间和便宜时间的抽象;
  • 第四层是对瞬时和时钟的抽象;
  • 第五层是对时序时段和持续周期的抽象
  • 右侧层是辅助工具类,如:日期时间格式、日期时间调整器、其他的日历系统;

java 8中日期时间库共分为五个package:

  • java.time:基于ISO_8601日历系统实现的日期时间库
  • java.time.chrono:全球日历系统扩展库,可以自行扩展
  • java.time.format:日期时间格式,包含大量预定义的格式,可以自行扩展
  • java.time.zone:时区信息库
  • java.time.temporal:日期时间调整辅助库

关于日期时间库的使用详细过程,推荐查看oracle提供的java教程The Java™ Tutorials——Trail: Date Time

也可以查看我的github中java 8新特性代码:lixyou/java

三.java.time优点

1.设计

java.time中使用了大量的设计模式

  • 工厂模式:now()工厂方法直接生成当前日期时间或者瞬时;of()工厂方法根据年月日时分秒生成日期或者日期时间;
  • 装饰模式:时区时间ZoneDateTime/便宜时间OffsetDateTime,都是在LocalDateTime的基础上加上时区/偏移量的修饰成为时区时间,然后可以进行时区转换;
  • 建造者模式:Calendar中加入建造者类,用于生成新的Calendar对象;

2.命名

  • java 8中的日期时间库类名、方法名命名上都是极其形象生动,易于理解,让开发者极易于使用——语义清晰精确!如:LocalDate中提供的now表示现在的日期,of用于年月日组成的日期(这里和英文中的of意义非常贴切),plus/minus加减等等;

3.合理的接口设计

  • LocalDate表示日期,由年月日组成,提供了获取所在年,所在月,所在日的api,提供所在一年的第几天api,用于比较日期前后api,替换年份、月份、日的api,这些api使得日期或者日期时间的处理上得到的功能上的极大提升;
  • 抽象出年、月、日、星期、日期、日期时间、瞬时、周期诸多接口,对事物本质有了细腻的抽象,并提供了相互转换的能力——提供极强的处理能力和语言表达能力;
  • 对于遗留的日期时间库Calendar/Date/Timezone和新的日期时间库的互通性;
  • 将全球的非标准日历系统单独抽象并支持扩展,从标准日历系统中隔离(符合设计原则:对修改关闭,对扩展开放)

四.补充

  • 时区:时区是地球上的区域使用同一个时间定义。以前,人们通过观察太阳的位置(时角)决定时间,这就使得不同经度的地方的时间有所不同(地方时)。1863年,首次使用时区的概念。时区通过设立一个区域的标准时间部分地解决了这个问题。

    世界各个国家位于地球不同位置上,因此不同国家,特别是东西跨度大的国家日出、日落时间必定有所偏差。这些偏差就是所谓的时差。

    理论时区以被15整除的子午线为中心,向东西两侧延伸7.5度,即每15°划分一个时区,这是理论时区。理论时区的时间采用其*经线(或标准经线)的地方时。所以每差一个时区,区时相差一个小时,相差多少个时区,就相差多少个小时。东边的时区时间比西边的时区时间早。为了避免日期的紊乱,提出国际日期变更线的概念

    但是,为了避开国界线,有的时区的形状并不规则,而且比较大的国家以国家内部行政分界线为时区界线,这是实际时区,即法定时区。请参见时区列表。

  • 子午线:经线也称子午线,和纬线一样是人类为度量而假设出来的辅助线,定义为地球表面连接南北两极的大圆线上的半圆弧。任两根经线的长度相等,相交于南北两极点。每一根经线都有其相对应的数值,称为经度。经线指示南北方向。

  • 本初子午线:即0度经线,亦称格林尼治子午线或本初经线,是经过英国格林尼治天文台的一条经线(亦称子午线)。本初子午线的东西两边分别定为东经和西经,于180度相遇。

  • 国际标准ISO 8601:是国际标准化组织的日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是2004年12月1日发行的第三版“ISO8601:2004”以替代1998年的第一版“ISO8601:1988”与2000年的第二版“ISO8601:2000”。

    年由4位数字组成YYYY,或者带正负号的四或五位数字表示±YYYYY。以公历公元1年为0001年,以公元前1年为0000年,公元前2年为-0001年,其他以此类推。应用其他纪年法要换算成公历,但如果发送和接受信息的双方有共同一致同意的其他纪年法,可以自行应用。

    月、日用两位数字表示:MM、DD。

    只使用数字为基本格式。使用短横线"-"间隔开年、月、日为扩展格式。

    ISO 8601:2004不再允许缺省(默认)世纪仅用两位数字表示年,这会与小时数的表示相混淆。而遵循ISO 8601:2000的GB/T 7408-2005,尚还存在这一问题。

  • 协调世界时英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称UTC):是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间。中华民国采用CNS 7648的《资料元及交换格式–资讯交换–日期及时间的表示法》(与ISO 8601类似)称之为世界协调时间。*采用ISO 8601:2000的国家标准GB/T 7408-2005《数据元和交换格式 信息交换 日期和时间表示法》中亦称之为协调世界时。

    协调世界时是世界上调节时钟和时间的主要时间标准,它与0度经线的平太阳时相差不超过1秒[4],并不遵守夏令时。协调世界时是最接近格林威治标准时间(GMT)的几个替代时间系统之一。对于大多数用途来说,UTC时间被认为能与GMT时间互换,但GMT时间已不再被科学界所确定。

    如果时间是以协调世界时(UTC)表示,则在时间后面直接加上一个“Z”(不加空格)。“Z”是协调世界时中0时区的标志。因此,“09:30 UTC”就写作“09:30Z”或是“0930Z”。“14:45:15 UTC”则为“14:45:15Z”或“144515Z”。

  • UTC偏移量:UTC偏移量用以下形式表示:±[hh]:[mm]、±[hh][mm]、或者±[hh]。如果所在区时比协调世界时早1个小时(例如柏林冬季时间),那么时区标识应为“+01:00”、“+0100”或者直接写作“+01”。这也同上面的“Z”一样直接加在时间后面。

    "UTC+8"表示当协调世界时(UTC)时间为凌晨2点的时候,当地的时间为2+8点,即早上10点。

  • 格林尼治平时(英语:Greenwich Mean Time,GMT):是指位于英国伦敦郊区的皇家格林尼治天文台当地的平太阳时,因为本初子午线被定义为通过那里的经线。

    自1924年2月5日开始,格林尼治天文台负责每隔一小时向全世界发放调时信息。

    格林尼治平时的正午是指当平太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时基于天文观测本身的缺陷,已经被原子钟报时的协调世界时(UTC)所取代。

参考

Trail: Date Time

Class DateTimeFormatter

Java 8新特性终极指南

Is java.util.Calendar thread safe or not?Ask

协调世界时

时区

ISO 8601

经线

[时区列表](https://zh.wikipedia.org/wiki/ %E6%97%B6%E5%8C%BA%E5%88%97%E8%A1%A8#UTC%EF%BC%88WET_-%E6%AD%90%E6%B4%B2%E8%A5%BF%E9%83%A8%E6%99%82%E5%8D%80%EF%BC%8CGMT-_%E6%A0%BC%E6%9E%97%E5%A8%81%E6%B2%BB%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4%EF%BC%89)

Java 8——日期时间工具库(java.time)的更多相关文章

  1. 【转】JAVA 8 日期&sol;时间(Date Time)API指南

    前言 本来想写下Java 8的日期/时间API,发现已经有篇不错的文章了,那就直接转载吧~ PS:主要内容没变,做了部分修改. 原文链接: journaldev 翻译: ImportNew.com - ...

  2. Java 8 日期时间 API

    转自:https://www.runoob.com/java/java8-datetime-api.html Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与 ...

  3. Java 8 新特性-菜鸟教程 &lpar;8&rpar; -Java 8 日期时间 API

    Java 8 日期时间 API Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理. 在旧版的 Java 中,日期时间 API 存在诸多问题,其中有: ...

  4. Java 8 日期时间API

    Java 8一个新增的重要特性就是引入了新的时间和日期API,它们被包含在java.time包中.借助新的时间和日期API可以以更简洁的方法处理时间和日期; 在介绍本篇文章内容之前,我们先来讨论Jav ...

  5. 【Hutool】Hutool工具类之日期时间工具——DateUtil

    一.用于取代Date对象的DateTime对象 再也不用Date SimpleDateFormat Calendar之间倒腾来倒腾去了!日期创建-获取-操作一步到位! 如果JDK版本更新到了8及以上, ...

  6. 【Hutool】工具类之日期时间工具-DateUtil

    日期时间工具类 一.依赖 <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-al ...

  7. Apache Commons Lang之日期时间工具类

    码农不识Apache,码尽一生也枉然. FastDateFormat FastDateFormat是一个快速且线程安全的时间操作类,它完全可以替代SimpleDateFromat.因为是线程安全的,所 ...

  8. Java中日期时间API小结

    Java中为处理日期和时间提供了大量的API,确实有把一件简单的事情搞复杂的嫌疑,各种类:Date Time Timestamp Calendar...,但是如果能够看到时间处理的本质就可以轻松hol ...

  9. Java 8 日期时间API使用介绍

    如何正确处理时间 现实生活的世界里,时间是不断向前的,如果向前追溯时间的起点,可能是宇宙出生时,又或是是宇宙出现之前, 但肯定是我们目前无法找到的,我们不知道现在距离时间原点的精确距离.所以我们要表示 ...

随机推荐

  1. 避免jsp传参返回乱码问题

    $("#searchForm input").each(function(i){ var obj=$(this); var va=obj.val(); obj.val(decode ...

  2. D3 学习资源

    发现这个网站还是挺不错的:http://www.ourd3js.com/wordpress/

  3. Linux TTY框架【转】

    本文转载自:http://ju.outofmemory.cn/entry/281168 1. 前言 由于串口的缘故,TTY是Linux系统中最普遍的一类设备,稍微了解Linux系统的同学,对它都不陌生 ...

  4. 修改mysql的root密码

    use msyql; update user set password=password('新密码') where user='root'; flush privileges; quit net st ...

  5. 【UVA10537】The Toll&excl; Revisited (逆推最短路)

    题目: Sample Input1a Z19 a Z5A DD XA bb cc X39 A X-1Sample OutputCase 1:20a-ZCase 2:44A-b-c-X 题意: 有两种节 ...

  6. 不能执行已释放script的代码

    ”从Dom中删除IFrame后,IE9+会回收内存.影响范围:适用于 Internet Explorer 9 以及更高版本.“ 1. 应用场景(相当隐蔽!!!) 在主页面定义一个全局变量,然后让子页面 ...

  7. 一个测ip和端口是否联通的工具类

    public class TestIp { public static void main(String[] args) { Socket connect = new Socket(); try { ...

  8. mongodb&colon; Remote server has closed the connection

    <?php function getMongoClient($seeds = "", $options = array(), $retry = 3) { try { retu ...

  9. java面试题之分析(二)

    QUESTION NO:2 package com.cdu.test;  public class Test { static boolean foo(char c) { System.out.pri ...

  10. 关于mysql中unique的插入Duplicate key

    MySQL数据库中 如果在后台中不做判断是否unique的column是否存在的话,直接把数据操作给dao层再传给DB的话,就会报重复的唯一值.如果确实是不希望先取出判断unique的column是否 ...