Java 8 系列文章 持续更新中

日期时间API 也是Java 8重要的更新之一,Java从一开始就缺少一致的日期和时间方法,Java 8 Date Time API是Java核心API的一个非常好的补充。

为什么需要新的日期时间API

Java中现有的与日期和时间相关的类存在一些问题:

  • 日期时间类的定义不一致,在java.utiljava.sql包中都有Date类。同样,格式化和解析类是在java.text包中定义的。
  • java.util.Date同时包含日期和时间值,而java.sql.Date只包含日期值,把它放在java.sql包中是没有意义的。而且这两个类的名称相同,这本身就是一个非常糟糕的设计。
  • 没有为时间、时间戳、格式化和解析明确定义的类。我们有java.text.DateFormat抽象类用于解析和格式化,通常使用SimpleDateFormat类解析和格式化。
  • 所有Date类都是可变的,所以它们不是线程安全的,这也是JavaDateCalendar类最大的问题之一。
  • Date类不提供国际化,不支持时区。虽然引入了java.util.Calendarjava.util.TimeZone,但是它们也存在上面的问题。

DateCalendar类中定义的方法还有一些其他的问题,但是上面的问题清楚地表明,Java中需要一个健壮的日期时间API。这就是为什么 Joda Time 可以成为Java 日期时间高质量的替代品。

Java 8日期时间设计原则

Java 8日期时间API是基于 JSR-310 规范实现的。目的是为了解决遗留日期时间实现中的所有缺陷。新的日期时间API的一些设计原则如下:

  • 不变性:新的日期时间API中的所有类都是不可变的,适用于多线程环境。

  • 关注点分离:新的日期时间API明确区分了人类可读的日期、时间和机器时间(Unix时间戳),它为DateTimeDateTimeTimestampTimezone 等定义单独的类。

  • 清晰性:所有的类中都清晰地定义了方法,并执行相同的操作。例如,要获取当前时间实例可以用now()方法,在所有这些类中都定义了format()parse()方法,而不是为它们单独定义一个类。

    所有类都使用工厂模式策略模式来更好地操作。一旦您使用了其中一个类中的方法,使用其他类并不困难。

  • 实用的操作:所有新的日期时间API类都常见的方法,比如加、减、格式化、解析、在日期/时间中获取单独的部分等等。

  • 可扩展:新的日期时间API可以在ISO-8601日历系统上工作,但是我们也可以在其他非ISO日历上使用它。

Java 8日期时间API的包

Java8日期时间API由以下包组成。

  • java.time:这是Java 8日期时间API的基本包。所有主要的基类都是这个包的一部分,例如LocalDateLocalTimeLocalDateTimeInstantPeriodDuration等。所有这些类都是不可变的和线程安全的。大多数情况下,这些类足以处理常见的需求。
  • java.time.chrono:这个包为非ISO日历系统定义了通用API。我们可以扩展AbstractChronology类来创建我们自己的日历系统。
  • java.time.format:这个包包含用于格式化和解析日期时间对象的类。大多数时候我们不会直接使用它们,因为java.time包中的日期时间类已经提供了格式化和解析方法。
  • java.time.temporal:这个包包含temporal对象,我们可以使用它来找出与日期/时间对象相关的特定日期或时间。例如,我们可以使用它们来查找一个月的第一天或最后一天。您可以很容易地识别这些方法,因为它们的格式总是withXXX
  • java.time.zone:这个包包含用于支持不同时区及其规则的类。

Java 8日期时间API类的示例

下面通过一些日期时间API类的示例,来更好的了解Java 8日期时间API

1.LocalDate

LocalDate是一个不可变的日期类,它以yyyy-MM-dd的默认格式表示日期。可以使用now()方法来获取当前日期,还可以提供年、月和日期的输入参数来创建LocalDate实例。

这个类为now()提供了一个重载方法,在这里可以传递ZoneId来获取特定时区中的日期。这个类提供了与java.sql.Date相同的功能。

// 当前日期
LocalDate today = LocalDate.now();
System.out.println("当前日期=" + today); // 通过提供年月日参数创建日期
LocalDate nowYear_1024 = LocalDate.of(2022, Month.OCTOBER, 24);
System.out.println("参数日期=" + nowYear_1024); // 通过时区获取当前日期
LocalDate todayShanghai = LocalDate.now(ZoneId.of("Asia/Shanghai"));
System.out.println("当前日期(CTT)=" + todayShanghai); // 从纪元日(1970-01-01)开始的第多少天
LocalDate dateFromBase = LocalDate.ofEpochDay(365);
System.out.println("1970-01-01的第365天= " + dateFromBase); // 某年的第多少天
LocalDate hundredDay2022 = LocalDate.ofYearDay(2022, 100);
System.out.println("2022年的第100天=" + hundredDay2022);

运行之后结果如下:

当前日期=2022-10-26
特殊日期=2022-10-24
当前日期(CTT)=2022-10-26
1970-01-01的第365天= 1971-01-01
2022年的第100天=2022-04-10

2.LocalTime

LocalTime是一个不可变的时间类,它以HH:mm:ss.SSS的默认格式表示时间。与LocalDate一样,这个类提供了时区支持,并可以通过传递小时、分钟和秒作为输入参数来创建实例。

// 当前时间
LocalTime time = LocalTime.now();
System.out.println("当前时间=" + time); // 通过提供时分秒参数创建日期
LocalTime specificTime = LocalTime.of(12, 20, 25, 40);
System.out.println("参数时间=" + specificTime); // 通过时区获取当前时间
LocalTime timeShanghai = LocalTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("当前时间(CTT)=" + timeShanghai); // 从纪元日开始的第多少秒
LocalTime specificSecondTime = LocalTime.ofSecondOfDay(100);
System.out.println("从纪元日开始的第100秒=" + specificSecondTime);

运行之后结果如下:

当前时间=15:39:18.948
参数时间=12:20:25.000000040
当前时间(CTT)=15:39:18.949
从0开始的第100秒=00:01:40

3.LocalDateTime

LocalDateTime是一个不可变的日期时间类,它以yyyy-MM-ddTHH:mm:ss.SSS的默认格式表示时间日期。它提供了一个工厂方法,该方法使用LocalDateLocalTime作为参数创建LocalDateTime实例。

// 当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期时间=" + now); // 通过提供LocalDate和LocalTime参数创建日期时间
now = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("当前日期时间=" + now); // 通过提供年月日时分秒参数创建日期时间
LocalDateTime specificTime = LocalDateTime.of(2022, Month.OCTOBER, 24, 10, 24, 24);
System.out.println("参数日期时间=" + specificTime); // 通过时区获取当前日期时间
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("当前日期时间(CTT)=" + todayKolkata); // 从纪元日开始的第多少秒
LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(100, 0, ZoneOffset.UTC);
System.out.println("从纪元日开始的第100秒= " + dateFromBase);

运行之后结果如下:

当前日期时间=2022-10-26T15:51:59.070
当前日期时间=2022-10-26T15:51:59.071
参数日期时间=2022-10-24T10:24:24
当前日期时间(CTT)=2022-10-26T15:51:59.071
从纪元日开始的第100秒=1970-01-01T00:01:40

注意:以上例子通过输入参数创建实例时,如果输入了无效的参数name将会抛出java.time.DateTimeException

4.Instant

instant类用于处理机器可读的时间格式。instant类将日期时间存储在unix时间戳中。

// 当期时间戳
Instant timestamp = Instant.now();
System.out.println("当期时间戳= "+timestamp); // 从纪元日开始的第多少毫秒
Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
System.out.println("从纪元日开始="+specificTime);

运行之后结果如下:

当期时间戳=2022-10-26T08:08:40.429Z
从纪元日开始=2022-10-26T08:08:40.429Z

Java8日期时间API类的实用方法

大多数日期时间类都会提供各种实用方法,例如加/减天数、周数、月数等。还有一些其他实用方法可以使用时间调整器TemporalAdjuster调整日期,并计算两个日期之间的时间段。

LocalDate today = LocalDate.now();

//获取年份,判断年份是否是闰年
System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear()); //比较两个时间
System.out.println("Today is before 01/01/2023? "+today.isBefore(LocalDate.of(2023,1,1))); //通过LocalDate创建LocalDateTime
System.out.println("Current Time="+today.atTime(LocalTime.now())); //加减操作
System.out.println("10 days after today will be "+today.plusDays(10));
System.out.println("3 weeks after today will be "+today.plusWeeks(3));
System.out.println("20 months after today will be "+today.plusMonths(20)); System.out.println("10 days before today will be "+today.minusDays(10));
System.out.println("3 weeks before today will be "+today.minusWeeks(3));
System.out.println("20 months before today will be "+today.minusMonths(20)); //时间调整器调整时间
System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("Last date of this year= "+lastDayOfYear); Period period = today.until(lastDayOfYear);
System.out.println("Period Format= "+period);
System.out.println("Months remaining in the year= "+period.getMonths());

运行之后结果如下:

Year 2022 is Leap Year? false
Today is before 01/01/2023? true
Current Time=2022-10-26T16:25:04.740
10 days after today will be 2022-11-05
3 weeks after today will be 2022-11-16
20 months after today will be 2024-06-26
10 days before today will be 2022-10-16
3 weeks before today will be 2022-10-05
20 months before today will be 2021-02-26
First date of this month= 2022-10-01
Last date of this year= 2022-12-31
Period Format= P2M5D
Months remaining in the year= 2

Java8日期时间的解析和格式化

经常用到的操作有:将日期时间格式化为不同格式String,解析String以获得日期时间对象。

// 格式化
LocalDate date = LocalDate.now();
// 默认格式
System.out.println("Default format of LocalDate=" + date);
// 自定义格式
System.out.println(date.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE)); LocalDateTime dateTime = LocalDateTime.now();
// 默认格式
System.out.println("Default format of LocalDateTime=" + dateTime);
// 自定义格式
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE)); Instant timestamp = Instant.now();
// 默认格式
System.out.println("Default format of Instant=" + timestamp); // 解析
LocalDateTime dt = LocalDateTime.parse("2022年10月24日10时24分24秒",
DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒"));
System.out.println("Default format after parsing = " + dt);

运行之后结果如下:

Default format of LocalDate=2022-10-26
2022年10月26日
20221026
Default format of LocalDateTime=2022-10-26T16:37:51.300
2022年10月26日16时37分51秒
20221026
Default format of Instant=2022-10-26T08:37:51.301Z
Default format after parsing = 2022-10-24T10:24:24

对遗留日期时间的支持

遗留日期/时间类几乎在所有应用程序中都使用,因此必须有向下兼容。这就是为什么我们可以通过一些实用方法将遗留类转换为新类,反之亦然。

//Date转Instant
Instant timestamp = new Date().toInstant();
//Instant转LocalDateTime
LocalDateTime date = LocalDateTime.ofInstant(timestamp,
ZoneId.of(ZoneId.SHORT_IDS.get("CTT")));
System.out.println("Date = " + date); //Calendar转Instant
Instant time = Calendar.getInstance().toInstant();
System.out.println(time);
//TimeZone转ZoneId
ZoneId defaultZone = TimeZone.getDefault().toZoneId();
System.out.println(defaultZone); //ZonedDateTime from specific Calendar
ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
System.out.println(gregorianCalendarDateTime); //Date API to Legacy classes
Date dt = Date.from(Instant.now());
System.out.println(dt); TimeZone tz = TimeZone.getTimeZone(defaultZone);
System.out.println(tz); GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
System.out.println(gc);

运行之后结果如下:

Date = 2022-10-26T16:47:38.329
2022-10-26T08:47:38.429Z
Asia/Shanghai
2022-10-26T16:47:38.455+08:00[Asia/Shanghai]
Wed Oct 26 16:47:38 CST 2022
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null]
java.util.GregorianCalendar[time=1666774058455,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2022,MONTH=9,WEEK_OF_YEAR=43,WEEK_OF_MONTH=4,DAY_OF_MONTH=26,DAY_OF_YEAR=299,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=47,SECOND=38,MILLISECOND=455,ZONE_OFFSET=28800000,DST_OFFSET=0]

可以看到,遗留的TimeZoneGregorianCalendartoString()方法过于冗长,对用户不友好。

Java 8 Time API的更多相关文章

  1. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...

  2. Java 8 Stream API Example Tutorial

    Stream API Overview Before we look into Java 8 Stream API Examples, let’s see why it was required. S ...

  3. java微信开发API解析(二)-获取消息和回复消息

    java微信开发API解析(二)-获取消息和回复消息 说明 * 本演示样例依据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/20 ...

  4. 文件IO 相关的包:java.io文件——API

    文件IO 相关的包:java.io文件——API 1.Java.io.File类的使用(1)两种路径绝对路径:相对于当前路径:当前为 “工程名”(2)File类创建,对象为一个文件/目录,可能存在或不 ...

  5. Elasticsearch Java Rest Client API 整理总结 (一)——Document API

    目录 引言 概述 High REST Client 起步 兼容性 Java Doc 地址 Maven 配置 依赖 初始化 文档 API Index API GET API Exists API Del ...

  6. Elasticsearch Java Rest Client API 整理总结 (二) —— SearchAPI

    目录 引言 Search APIs Search API Search Request 可选参数 使用 SearchSourceBuilder 构建查询条件 指定排序 高亮请求 聚合请求 建议请求 R ...

  7. Elasticsearch Java Rest Client API 整理总结 (三)——Building Queries

    目录 上篇回顾 Building Queries 匹配所有的查询 全文查询 Full Text Queries 什么是全文查询? Match 全文查询 API 列表 基于词项的查询 Term Term ...

  8. Java 8 Stream API

    Java 8 Stream API JDK8 中有两大最为重要的改变.第一个是 Lambda 式:另外 Stream API(java.util.stream.*) Stream 是 JDK8 中处理 ...

  9. Java学习(API及Object类、String类、StringBuffer字符串缓冲区)

    一.JAVA的API及Object类 1.API 概念: Java 的API(API: Application(应用) Programming(程序) Interface(接口)) Java API就 ...

  10. java.io.BufferedWriter API 以及源码解读

    下面是java se 7 API 对于java.io.BufferedWriter 继承关系的描述. BufferedWriter可以将文本写入字符流.它会将字符缓存,目的是提高写入字符的效率. bu ...

随机推荐

  1. DS二叉树——二叉树之数组存储

    题目描述 二叉树可以采用数组的方法进行存储,把数组中的数据依次自上而下,自左至右存储到二叉树结点中,一般二叉树与完全二叉树对比,比完全二叉树缺少的结点就在数组中用0来表示.,如下图所示 从上图可以看出 ...

  2. 深入分析FragmentPagerAdapter和FragmentStatePagerAdapter

    最近遇到比较奇怪的bug,TableLayout+ViewPager实现点击顶部tab切换viewpager视图.但是在Viewpager设置dapter时,最开始设置的是FragmentPagerA ...

  3. silk-GUI图形界面开发一个词典

    了解使用的库 Silk内置了一些GUI类库供使用者开发MacOS上的图形界面程序,只需引用gui.si即可 准备 首先要知道app需要什么功能,这里我要的是查询单词,可以听语音,还可以存储生词! 那么 ...

  4. 最大连续子序列(简单DP实现)

    最大连续子序列 最大连续子数列和一道很经典的算法问题,给定一个数列,其中可能有正数也可能有负数,我们的任务是找出其中连续的一个子数列(不允许空序列),使它们的和尽可能大.我们一起用多种方式,逐步优化解 ...

  5. UOJ#XX A+B Problem (罔烙硫)

    题面 背景 题目描述 从前有个 n n n 个方格排成一行,从左至右依此编号为 1 , 2 , ⋯ , n 1,2,⋯,n 1,2,⋯,n. 有一天思考熊想给这 n n n 个方格染上黑白两色. 第 ...

  6. 【JDBC】学习路径7-转账-commit事务操作

    现在我们要做一个转账系统. 第一章:创建一张新的表格(用户银行账户表格) 在之前的JDBC_01数据库中新增一个表格,名字为:Account   其中,有id.银行卡号.银行密码.账户余额. 创建表格 ...

  7. C/C++内存泄漏检测方法

    1. 内存泄漏 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果. 2. 检测代码 使用链 ...

  8. MySQL集群搭建(1)-主备搭建

    数据库在任何业务中都是最重要的环节之一,这就对数据库架构提出的较高的要求.单点数据库永远不应该出现在生产环境,我们已经目睹过太多由于单点.备份缺失造成的损失,所以,搭建高可用 MySQL 集群是非常有 ...

  9. PVC-U排水管的断管与接管

    1. PVC-U管的常用切割工具 2. PVC-U管的胶粘剂 3. 用胶粘剂粘接PVC-U管与管件

  10. 创建Grafana Dashboard

    输入表达式,获取数据 但是考虑到多台主机,需要添加变量来选择不同主机,从而出现相应的图表 点击右上角齿轮一样的图标,这个是设置 通过在prometheus界面查询可知,可以设置的变量有两个 考虑到这俩 ...