余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

Java8 新特性:新日期与时间

xiyangw 2023-10-08 14:00 20 浏览 0 评论

扩展1:以固定格式输出日期的字符串表示

DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(accessTime)

1

如今,一些应用程序仍在使用java.util.Date和java.util.Calendar API,包括使我们的生活更轻松地使用这些类型的库,例如JodaTime。 但是,Java 8引入了新的API来处理日期和时间,这使我们可以对日期和时间表示进行更精细的控制,为我们提供不可变的datetime对象,更流畅的API以及在大多数情况下提高性能,而无需使用其他库。 让我们看一下基础知识。

1. LocalDate / LocalTime / LocalDateTime

让我们开始与最相关的新API的java.util.Date : LocalDate ,日期的API,表示没有时间的日期; LocalTime ,不带日期的时间表示; 和LocalDateTime ,这是前两个的组合。 所有这些类型都代表一个区域的本地日期和/或时间,但是,就像java.util.Date一样,它们包含零表示该区域的信息,仅表示当前日期和时间。时区。

首先,这些API支持简单的实例化:

LocalDate date = LocalDate.of(2018,2,13);

// Uses DateTimeformatter.ISO_LOCAL_DATE for which the format is: yyyy-MM-dd

LocalDate date = LocalDate.parse("2018-02-13");

LocalTime time = LocalTime.of(6,30);

// Uses DateTimeFormatter.ISO_LOCAL_TIME for which the format is: HH:mm[:ss[.SSSSSSSSS]]

// this means that both seconds and nanoseconds may optionally be present.

LocalTime time = LocalTime.parse("06:30");

LocalDateTime dateTime = LocalDateTime.of(2018,2,13,6,30);

// Uses DateTimeFormatter.ISO_LOCAL_DATE_TIME for which the format is the

// combination of the ISO date and time format, joined by 'T': yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]

LocalDateTime dateTime = LocalDateTime.parse("2018-02-13T06:30");

在它们之间进行转换很容易:

// LocalDate to LocalDateTime

LocalDateTime dateTime = LocalDate.parse("2018-02-13").atTime(LocalTime.parse("06:30"));

// LocalTime to LocalDateTime

LocalDateTime dateTime = LocalTime.parse("06:30").atDate(LocalDate.parse("2018-02-13"));

// LocalDateTime to LocalDate/LocalTime

LocalDate date = LocalDateTime.parse("2018-02-13T06:30").toLocalDate();

LocalTime time = LocalDateTime.parse("2018-02-13T06:30").toLocalTime();

除此之外,使用plus和minus方法以及一些实用程序功能,对我们的日期和时间表示进行操作非常容易:

LocalDate date = LocalDate.parse("2018-02-13").plusDays(5);

LocalDate date = LocalDate.parse("2018-02-13").plus(ChronoUnit.MONTHS);

LocalTime time = LocalTime.parse("06:30").minusMinutes(30);

LocalTime time = LocalTime.parse("06:30").minus(5ChronoUnit.MILLIS);

LocalDateTime dateTime = LocalDateTime.parse("2018-02-13T06:30").plus(Duration.ofHours(2));

// using TemporalAdjusters, which implements a few useful cases:

LocalDate date = LocalDate.parse("2018-02-13").with(TemporalAdjusters.lastDayOfMonth());

现在我们如何从java.util.Date移到LocalDateTime及其变体? 好吧,这很简单:我们可以将Date类型转换为Instant类型,该类型表示从1970年1月1日开始的时间,然后我们可以使用Instant和当前区域实例化LocalDateTime 。

LocalDateTime dateTime = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());

1

要转换回日期,我们可以简单地使用Java 8时间类型表示的Instant。 但是要注意的一件事是,尽管LocalDate , LocalTime和LocalDateTime不包含任何Zone或Offset信息,但它们确实表示特定区域中的本地日期和/或时间,因此它们确实保留了当前的偏移量。在那个地区。 因此,我们需要提供一个偏移量以将特定类型正确转换为Instant。

// represents Wed Feb 23:24:CET 2018

Date now = new Date();

// represents 2018-02-28T23:24:43.106

LocalDateTime dateTime = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());

// represent Wed Feb 23:24:CET 2018

Date date = Date.from(dateTime.toInstant(ZoneOffset.ofHours(1)));

Date date = Date.from(dateTime.toInstant(ZoneId.systemDefault().getRules().getOffset(dateTime)));

2. 时间差异–持续时间和期间

您已经注意到,在以上示例之一中,我们使用了Duration对象。 Duration和Period是两个日期之间时间的两种表示形式,前者表示以秒和纳秒为单位的时间差,后者以天,月和年表示。

什么时候应该使用这些? Period ,当你需要知道两者之间的时间差LocalDate陈述:

Period period = Period.between(LocalDate.parse("2018-01-18"), LocalDate.parse("2018-02-14"));

1

您在寻找具有时间信息的表示形式之间的差异时的Duration时间:

Duration duration = Duration.between(LocalDateTime.parse("2018-01-18T06:30"), LocalDateTime.parse("2018-02-14T22:58"));

1

使用toString()输出Period或Duration ,将基于ISO-8601标准使用特殊格式。 周期使用的模式是PnYnMnD,其中n定义周期内存在的年,月或日的数量。 这意味着P1Y2M3D定义为1年2个月3天。 。 模式中的“ P”是时段指示符,它告诉我们以下格式表示一个时段。 使用该模式,我们还可以使用parse()方法基于字符串创建句点。

// represents a period of 27 days

Period period = Period.parse("P27D");

使用Durations ,由于Java 8不使用相同的模式,因此我们稍微偏离了ISO-8601标准。 ISO-8601定义的模式是PnYnMnDTnHnMn.nS。 这基本上是“ Period模式,带有时间表示形式。 在模式中,T是时间标记,因此后面的部分定义了以小时,分钟和秒为单位的持续时间。

Java的8个使用两种特定模式的Duration ,解析字符串,一当就是PnDTnHnMn.nS Duration ,以及调用时PTnHnMn.nS toString()上的一个方法Duration实例。

最后但并非最不重要的一点是,我们还可以通过使用类型上的相应方法来检索时间段或持续时间的各个部分。 但是,重要的是要知道各种日期时间类型也通过使用ChronoUnit枚举类型来支持此功能。 让我们看一些例子:

// represents PT664H28M

Duration duration = Duration.between(LocalDateTime.parse("2018-01-18T06:30"), LocalDateTime.parse("2018-02-14T22:58"));

// returns 664

long hours = duration.toHours();

// returns 664

long hours = LocalDateTime.parse("2018-01-18T06:30").until(LocalDateTime.parse("2018-02-14T22:58"), ChronoUnit.HOURS);

3. 使用区域和偏移量– ZonedDateTime和OffsetDateTime

到目前为止,我们已经展示了新的日期API如何使某些事情变得容易一些。 但是,真正与众不同的是在时区上下文中轻松使用日期和时间的能力。 Java 8为我们提供了ZonedDateTime和OffsetDateTime ,第一个是LocalDateTime其中包含特定区域(例如,欧洲/巴黎)的信息,第二个是带有偏移量的LocalDateTime 。 有什么不同? OffsetDateTime使用UTC /格林威治标准时间与指定日期之间的固定时差,而ZonedDateTime指定表示时间的区域,并将考虑夏时制。

转换为以下两种类型都很容易:

OffsetDateTime offsetDateTime = LocalDateTime.parse("2018-02-14T06:30").atOffset(ZoneOffset.ofHours(2));

// Uses DateTimeFormatter.ISO_OFFSET_DATE_TIME for which the default format is

// ISO_LOCAL_DATE_TIME followed by the offset ("+HH:mm:ss").

OffsetDateTime offsetDateTime = OffsetDateTime.parse("2018-02-14T06:30+06:00");

ZonedDateTime zonedDateTime = LocalDateTime.parse("2018-02-14T06:30").atZone(ZoneId.of("Europe/Paris"));

// Uses DateTimeFormatter.ISO_ZONED_DATE_TIME for which the default format is

// ISO_OFFSET_DATE_TIME followed by the the ZoneId in square brackets.

ZonedDateTime zonedDateTime = ZonedDateTime.parse("2018-02-14T06:30+08:00[Asia/Macau]");

// note that the offset does not matter in this case.

// The following example will also return an offset of +08:00

ZonedDateTime zonedDateTime = ZonedDateTime.parse("2018-02-14T06:30+06:00[Asia/Macau]");

在它们之间进行切换时,必须记住,从ZonedDateTime转换为OffsetDateTime将考虑夏时制,而在另一个方向上从OffsetDateTime至ZonedDateTime意味着您将没有有关区域区域的信息,也不会对夏时制应用任何规则。 这是因为偏移量未定义任何时区规则,也未绑定到特定区域。

ZonedDateTime winter = LocalDateTime.parse("2018-01-14T06:30").atZone(ZoneId.of("Europe/Paris"));

ZonedDateTime summer = LocalDateTime.parse("2018-08-14T06:30").atZone(ZoneId.of("Europe/Paris"));

// offset will be +01:00

OffsetDateTime offsetDateTime = winter.toOffsetDateTime();

// offset will be +02:00

OffsetDateTime offsetDateTime = summer.toOffsetDateTime();

OffsetDateTime offsetDateTime = zonedDateTime.toOffsetDateTime();

OffsetDateTime offsetDateTime = LocalDateTime.parse("2018-02-14T06:30").atOffset(ZoneOffset.ofHours(5));

ZonedDateTime zonedDateTime = offsetDateTime.toZonedDateTime();

现在,如果我们想知道特定时区或偏移时间在我们自己的时区中怎么办? 嗯,为此还定义了一些方便的功能!

// timeInMacau represents 2018-02-14T13:30+08:00[Asia/Macau]

ZonedDateTime timeInMacau = LocalDateTime.parse( "2018-02-14T13:).atZone( ZoneId.of( "Asia/Macau" ) );

// timeInParis represents 2018-02-14T06:30+01:00[Europe/Paris]

ZonedDateTime timeInParis = timeInMacau.withZoneSameInstant( ZoneId.of( "Europe/Paris" ) );

OffsetDateTime offsetInMacau = LocalDateTime.parse( "2018-02-14T13:).atOffset( ZoneOffset.ofHours( 8 ) );

OffsetDateTime offsetInParis = offsetInMacau.withOffsetSameInstant( ZoneOffset.ofHours( 1 ) );

如果我们不得不一直在这些类型之间手动进行转换以获取我们需要的类型,那将是一件麻烦事。 这就是Spring框架为我们提供帮助的地方。 Spring为我们提供了许多现成的日期时间转换器,这些日期时间转换器已在ConversionRegistry中注册,可以在org.springframework.format.datetime.standard.DateTimeConverters类中找到。

使用这些转换器时,重要的是要知道它不会转换区域或偏移量之间的时间。 该ZonedDateTimeToLocalDateTimeConverter ,例如,将返回LocalDateTime因为它是在,而不是指定的区域LocalDateTime ,它会在你的应用程序的区域代表。

ZonedDateTime zonedDateTime = LocalDateTime.parse("2018-01-14T06:30").atZone(ZoneId.of("Asia/Macau"));

// will represent 2018-01-14T06:regardless of the region your application has specified

LocalDateTime localDateTime = conversionService.convert(zonedDateTime, LocalDateTime.class);

最后但并非最不重要的一点是,您可以查询ZoneId.getAvailableZoneIds()来查找所有可用时区,或使用地图ZoneId.SHORT_IDS ,其中包含一些时区的缩写版本,例如EST,CST等。

4. 格式化–使用

当然,世界各地都使用不同的格式来指定时间。 一个应用程序可能使用MM-dd-yyyy,而另一个应用程序使用dd / MM / yyyy。 一些应用程序希望消除所有混淆,并用yyyy-MM-dd表示其日期。 使用java.util.Date ,我们将快速转向使用多个格式化程序。 但是, DateTimeFormatter类为我们提供了可选的模式,因此我们可以将单个格式化程序用于多种格式! 让我们来看一些例子。

// Let’s say we want to convert all of patterns mentioned above.

// 09-23-2023/09/2and 2018-09-should all convert to the same LocalDate.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("[yyyy-MM-dd][dd/MM/yyyy][MM-dd-yyyy]");

LocalDate.parse("09-23-2018", formatter);

LocalDate.parse("23/09/2018", formatter);

LocalDate.parse("2018-09-23", formatter);

模式中的方括号定义了模式中的可选部分。 通过使我们的各种格式成为可选,与字符串匹配的第一个模式将用于转换我们的日期表示形式。 当您使用多种模式时,这可能很难理解,因此让我们看一下使用构建器模式创建DateTimeFormatter方法。

DateTimeFormatter formatter = new DateTimeFormatterBuilder()

.appendOptional( DateTimeFormatter.ofPattern( "yyyy-MM-dd" ) )

.optionalStart().appendPattern( "dd/MM/yyyy" ).optionalEnd()

.optionalStart().appendPattern( "MM-dd-yyyy" ).optionalEnd()

.toFormatter();

这些是包含多个模式的基础,但是如果我们的模式仅稍有不同,该怎么办? 让我们看一下yyyy-MM-dd和yyyy-MMM-dd。

// 2018-09-and 2018-Sep-should convert to the same LocalDate.

// Using the ofPattern example we’ve used above will work:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("[yyyy-MM-dd][yyyy-MMM-dd]" );

LocalDate.parse( "2018-09-23", formatter );

LocalDate.parse( "2018-Sep-23", formatter );

// Using the ofPattern example where we reuse the common part of the pattern

DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyy-[MM-dd][MMM-dd]" );

LocalDate.parse( "2018-09-23", formatter );

LocalDate.parse( "2018-Sep-23", formatter );

但是,在转换为字符串时,不应使用支持多种格式的格式化程序,因为当我们使用格式化程序将日期格式化为字符串表示形式时,它还将使用可选模式。

LocalDate date = LocalDate.parse("2018-09-23");

// will result in 2018-09-232018-Sep-23

date.format(DateTimeFormatter.ofPattern("[yyyy-MM-dd][yyyy-MMM-dd]" ));

// will result in 2018-09-23Sep-23

date.format(DateTimeFormatter.ofPattern( "yyyy-[MM-dd][MMM-dd]" ));

由于我们处于21世纪,因此显然我们必须考虑全球化,并且我们希望为用户提供本地化日期。 为确保您的DateTimeFormatter返回特定的语言环境,您只需执行以下操作:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "EEEE, MMM dd, yyyy" ).withLocale(Locale.UK);

DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MMM-dd" ).toFormatter(Locale.UK);

要查找可用的语言环境,可以使用Locale.getAvailableLocales() 。

现在可能是您收到的日期格式比您使用的类型包含更多的信息。 一旦提供的日期表示形式与该模式不一致,则DateTimeFormatter将引发异常。 让我们仔细研究这个问题以及如何解决它。

// The issue: this will throw an exception.

LocalDate date = LocalDate.parse("2018-02-15T13:45");

// We provide a DateTimeFormatter that can parse the given date representation.

// The result will be a LocalDate holding 2018-02-15.

LocalDate date = LocalDate.parse("2018-02-15T13:45", DateTimeFormatter.ISO_LOCAL_DATE_TIME);

让我们创建一个可以处理ISO日期,时间和日期时间模式的格式化程序。

DateTimeFormatter formatter = new DateTimeFormatterBuilder()

.appendOptional( DateTimeFormatter.ISO_LOCAL_DATE )

.optionalStart().appendLiteral( "T" ).optionalEnd()

.appendOptional( DateTimeFormatter.ISO_LOCAL_TIME )

.toFormatter();

现在,我们可以完美地执行以下所有操作:

// results in 2018-03-16

LocalDate date = LocalDate.parse( "2018-03-16T06:30", formatter );

LocalDate date = LocalDate.parse( "2018-03-16", formatter );

// results in 06:30

LocalTime time = LocalTime.parse( "2018-03-16T06:30", formatter );

LocalTime time = LocalTime.parse( "06:30", formatter );

LocalDateTime localDateTime = LocalDateTime.parse( "2018-03-16T06:30", formatter );

现在,下一期是哪里来的? 如果您尝试解析LocalDateTime的日期模式怎么办? 如果您希望使用LocalTime并得到日期表示,反之亦然怎么办?

// will throw an exception

LocalDateTime localDateTime = LocalDateTime.parse("2018-03-16", formatter);

LocalDate localDate = LocalDate.parse("06:30", formatter);

对于这后两种情况,没有一个正确的解决方案,但这取决于您的要求,或者这些日期和时间代表或可能代表什么。 使用TemporalQuery可以找到魔术,您可以使用它为模式的一部分创建默认值。

如果我们以LocalDateTime开头,而您只需要LocalDate或LocalTime ,则将收到LocalDateTime的相应部分。 要创建LocalDateTime ,我们需要其保存的日期和时间的默认值。 假设如果您不提供有关日期的信息,我们将返回今天的日期,如果您不提供时间,则将假设您的意思是一天的开始。

由于我们将返回LocalDateTime ,因此不会将其解析为LocalDate或LocalTime ,因此让我们使用ConversionService来获取正确的类型。

TemporalQuery<TemporalAccessor> myCustomQuery = new MyCustomTemporalQuery();

// results in 2018-03-16

LocalDateTime localDateTime = conversionService.convert( formatter.parse( "2018-03-16", myCustomQuery ), LocalDateTime.class );

// results in 00:00

LocalTime localTime = conversionService.convert( formatter.parse( "2018-03-16", myCustomQuery ), LocalTime.class );

class MyCustomTemporalQuery implements TemporalQuery<TemporalAccessor>

{

@Override

public TemporalAccessor queryFrom( TemporalAccessor temporal ) {

LocalDate date = temporal.isSupported( ChronoField.EPOCH_DAY )

? LocalDate.ofEpochDay( temporal.getLong( ChronoField.EPOCH_DAY ) ) : LocalDate.now();

LocalTime time = temporal.isSupported( ChronoField.NANO_OF_DAY )

? LocalTime.ofNanoOfDay( temporal.getLong( ChronoField.NANO_OF_DAY ) ) : LocalTime.MIN;

return LocalDateTime.of( date, time );

}

}

使用TemporalQuery ,我们可以检查存在的信息,并为缺少的任何信息提供默认值,从而使我们能够使用应用程序中有意义的逻辑轻松地转换为所需的类型。

要了解如何编写有效的时间模式,请查看[DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns)文档 。

5. 结论

大多数新功能需要一些时间来理解和习惯,Java 8 Date / Time API也不例外。 新的API使我们能够更好地访问必要的正确格式,以及使用日期时间操作的更加标准化和易读的方式。 使用这些提示和技巧,我们几乎可以涵盖所有用例。

相关推荐

华为交换机配置命令总结

1、配置文件相关命令[Quidway]displaycurrent-configuration显示当前生效的配置[Quidway]displaysaved-configuration显示fla...

解决账户无法登录的故障
解决账户无法登录的故障

在优化系统时错误地根据网上的提示,将唯一的Administrator账户设置为禁用,导致重启后无法进入系统。类似的故障还有使用组策略限制本地账户登录,导致重启后...

2023-10-11 17:16 xiyangw

S5720交换机登录提示初始密码存在安全风险
S5720交换机登录提示初始密码存在安全风险

问题描述客户每次登录输密码时,提示初始密码不安全,现在客户嫌麻烦想要去掉:Username:huaweiPassword:Warning:Theinitia...

2023-10-11 17:15 xiyangw

Springboot,Mybatis修改登录用户的密码
Springboot,Mybatis修改登录用户的密码

一、Mybatis.xml<updateid="changePassword"parameterType="string...

2023-10-11 17:15 xiyangw

PHP理论知识之沐浴更衣重看PHP基础(二)
PHP理论知识之沐浴更衣重看PHP基础(二)

接上篇,咱们继续讲解PHP基础八、标准PHP组件和框架的数量很多,随之产生的问题就是:单独开发的框架没有考虑到与其他框架的通信。这样对开发者和框架本身都是不利的...

2023-10-11 17:15 xiyangw

新鲜出炉UCloud云主机“数据方舟”评测报告(5)— — 关其城
新鲜出炉UCloud云主机“数据方舟”评测报告(5)— — 关其城

2015年10月29日,UCloud云主机黑科技——“数据方舟”功能正式上线,首轮内测随即开放。截止至2015年12月6日,我们共收到了534位用户的评测申...

2023-10-11 17:14 xiyangw

业余无线电Q简语及英文缩语
业余无线电Q简语及英文缩语

Q简语:语音通信及CW通信通用(加粗为常用)QRA电台何台QRB电台间之距离QRG告之正确频率QRH频率是否变动QRI发送音调QRJ能否收到QRK信号之可...

2023-10-11 17:14 xiyangw

非常详细!如何理解表格存储的多版本、生命周期和有效版本偏差
非常详细!如何理解表格存储的多版本、生命周期和有效版本偏差

表格存储在8月份推出了容量型实例,直接支持了表级别最大版本号和生命周期,高性能实例也将会在9月中旬支持这两个特性。那么,最大版本号和生命周期以及特有的...

2023-10-11 17:14 xiyangw

H3C交换机恢复出厂和各种基本配置,这20个要点你知道吗?
H3C交换机恢复出厂和各种基本配置,这20个要点你知道吗?

私信“干货”二字,即可领取138G伺服与机器人专属及电控资料!H3C交换机不知道密码如何恢复出厂设置1、开机启动,Ctrl+B进入bootrom菜单,选择恢复出...

2023-10-11 17:13 xiyangw

在使用移动支付系统的时候如何保护信息安全?

移动支付的方式近年来不断被更新,使得Venmo(据嘉丰瑞德理财师了解,此为美国的“支付宝”)之类的支付方式已经可以某种意义上代替随身携带现金了。但是你必须防范那些第三方应用程序轻松地获取你的银行卡以及...

界面控件DevExpress WinForms MVVM入门指南——登录表单(下)

从本文档中,您将了解如何向应用程序添加登录表单。在本节教程中着重讨论了如何实现此任务,这基本上是附加应用程序功能的一部分。DevExpressUniversalSubscription官方最新版免...

linux基础命令(一)
linux基础命令(一)

为啥要学linux?您可能熟悉WindowsXP、Windows7、Windows10和MacOSX等操作系统。Linux就是这样一种强大的操...

2023-10-11 17:13 xiyangw

MySQL数据库密码忘记了,怎么办?

#头条创作挑战赛#MySQL数据库密码忘记了且没有其他可以修改账号密码的账户时怎么办呢?登录MySQL,密码输入错误/*密码错误,报如下错误*/[root@TESTDB~]#mysql-u...

MobaXterm忘记Session密码,如何查看已保存的密码
MobaXterm忘记Session密码,如何查看已保存的密码

MobaXterm工具登录过SSH终端后,如果存储了Session(存储后再连接ssh的时候只需要输入账号不需要输入密码就可以直接连接上ssh),则可以...

2023-10-11 17:12 xiyangw

华为交换机密码丢失修改方法
华为交换机密码丢失修改方法

华为S2300交换机找回密码设置一、目的交换机的console和telnet密码丢失,无法登录设备。交换机已进行过数据配置,要把密码恢复而数据配置不能丢失。二、...

2023-10-11 17:12 xiyangw

取消回复欢迎 发表评论: