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. vue自定义switch开关,使用less支持换肤

    实际项目用到了,记录一下,也方便以后使用,这样也可以避免为了使用一个switch,引入整个外部web框架: 也可以方便更好的理解是和使用less. 基础代码使用的是网上的,然后自己添加了less换肤, ...

  2. MySQL 数据查询语句

    一般查询 字段取别名 别名不用加单引号,as 可省略. select t.id ID, t.name 名称 from grade t; 拼接字符串 concat(a, b) select concat ...

  3. 面试常问:HTTP 1.0 和 HTTP 1.1 有什么区别?

    这篇文章会从下面几个维度来对比 HTTP 1.0 和 HTTP 1.1: 响应状态码 缓存处理 连接方式 Host头处理 带宽优化 响应状态码 HTTP/1.0仅定义了16种状态码.HTTP/1.1中 ...

  4. mysql存储过程的创建和调用

    描述:存储过程就是具有名字的一段代码,用来完成一个特定的功能.创建的存储过程保存在数据库的数据词典中. --创建一个名为GreetWorld的存储过程,拼接两个值 CREATE PROCEDURE G ...

  5. Jira使用浅谈篇一

    本篇参考: https://www.jianshu.com/u/9dd427d9ad94 Salesforce 生命周期管理(二)Agile & Scrum 浅谈 我们都知道 salesfor ...

  6. linux 3个快捷方式

    Ctrl+c组合键:当同时按下键盘上的Ctrl和字母c的时候,意味着终止当前进程的运行.假如执行了一个错误命令,或者是执行某个命令后迟迟无法结束,这时就可以冷静地按下Ctrl+c组合键,命令行终端的控 ...

  7. 1.2_Selenium的三生三世

  8. 【Java】学习路径53-InetAdress获取服务器ip

    InetAdress如何使用? import java.net.*; public class InetAdress { public static void main(String[] args) ...

  9. iOS 组件化及二进制化的探索

    组件化的优缺点 组件化的拆分 组件与组件之间如何进行通讯(路由) 从Cocopods拉取代码的过程 远程索引库里很多的.spec文件,该文件记录了很多内容,如用户名,框架名称,描述,框架的地址 Pod ...

  10. [机器学习]-分类问题常用评价指标、混淆矩阵及ROC曲线绘制方法

    分类问题 分类问题是人工智能领域中最常见的一类问题之一,掌握合适的评价指标,对模型进行恰当的评价,是至关重要的. 同样地,分割问题是像素级别的分类,除了mAcc.mIoU之外,也可以采用分类问题的一些 ...