我在这里正确地使用了java.time吗?看起来旧的API会更容易



我很少使用java.timeAPI,因为我的工作主要涉及捕获、存储和显示日期和时间,而不以任何方式操纵它们。但偶尔,我不得不做一些类似的事情:

  • 获取过去30天的日期

我还决定,基于其他不相关的经历,人类倾向于考虑";过去的30天";到实际上的意思是";过去30天的一天的开始";不仅仅是";现在减去过去的CCD_ 2毫秒";这只需使用System.currentTimeMillis和算术就非常容易实现。

所以我的要求是:

  • 在我的时区中获取过去30天的日期

如果我用旧的API来做这件事,我会这样做:

TimeZone zone = ...; // However I get my time zone
Locale locale = ...; // However I get my locale
Calendar now = Calendar.getInstance(zone, locale);
// Reset to midnight
now.set(Calendar.HOUR, 0);
now.set(Calendar.MINUTE, 0);
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
// Now go back 30 days
now.add(Calendar.DAY, -30);
// done

除了反复调用以清空Calendar的时间部分之外,它非常简单。它只使用了3个类,其中两个是TimeZoneLocale类,其中只有一个是真正必要的(TimeZone)。

这一次,我决定使用java.time,因为它是";更好"我真的不知道该怎么办,所以我在网上搜索了很多,想知道如何做各种事情,最后我想出了下面的代码。这不应该是";更容易">

TimeZone zone = ...; // However I get my time zone
ZoneId zid = zone.toZoneId(); // Convert to new kind of time zone
ZonedDateTime date = ZonedDateTime.now(zid);
date = date.minus(Period.ofDays(30)); // Rewind 30 days
date = date.truncatedTo(ChronoUnit.DAYS);
Instant instant = date.toInstant(); // Convert to Instant for comparisons

现在我使用6不同的类。好吧,其中2个是常量类型的东西,所以我们可以忽略它们。我必须使用Instant,因为我必须用于比较的所有日期都是java.util.Date,而Instant是弥合所有差距的类。

问题是这是一个";简单问题;而CCD_ 11旨在解决更复杂的问题?

获取过去中30天的日期

听起来像

java.time.LocalDate.now().minusDays(30)

java.time中多个不同类的目的正是为您在问题中提到的许多事情建模。

人类倾向于考虑";过去的30天";实际上是指";过去30天的一天的开始";

好吧,你不在乎时间,所以你不需要DateTime,只需要LocalDate

LocalDate prev = LocalDate.now().minusDays(30);

如果必须转换为Instant,则需要指定一个时区。

Instant asInstant = prev.atStartOfDay​(zone).toInstant();

然而,如果你不在乎时间,那么你可能不应该转换为Instant,只需使用LocalDate即可。

我必须使用Instant,因为我必须使用的所有比较日期都是java.util.Date

Date包括一个时间部分。你是不是也会把它们缩短到一天开始?这样做并取回LocalDate的一种方法是:

date.toInstant().atZone(zone).toLocalDate();

tl;dr

ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;        // Date varies around the globe by time zone.
LocalDate todayTokyo = LocalDate.now( zTokyo ) ;   // Capture the current date as seen in a particular time zone.
ZonedDateTime firstMomentOfDateInTokyo = todayTokyo.atStartOfDay( zTokyo ) ;  // Determine the first moment of that date in that zone.
Instant firstMomentOfDateInTokyoAsSeenInUtc = firstMomentOfDateInTokyo.toInstant() ;

请在Ideone.com.上实时查看此代码

2022-05-24

2022-05-24T00:00+09:00[亚洲/东京]

2022-05-23T15:00:00Z

详细信息

仅限日期

如Thilo的正确答案所示,如果您希望当前日期没有一天中的时间,没有时区或与UTC的偏移,请使用30*24*60*60*10000。

LocalDate today = LocalDate.now() ;

now方法隐式地使用JVM的当前默认时区来确定日期。请注意,在任何特定时刻,全球各地的日期都会因时区而异。现在是";明天";在日本东京;昨天";位于美国俄亥俄州托莱多。

最好指定您想要/期望的时区。

ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;
LocalDate todayTokyo = LocalDate.now( zTokyo ) ;

带时间的日期&区域

如果您希望在该时区中看到当天的第一个时刻,请使用ZonedDateTime类。不要以为一天从00:00开始。某些时区的某些日期可能在其他时间开始,例如01:00。让java.time确定第一个时刻。

ZonedDateTime firstMomentOfDateInTokyo = todayTokyo.atStartOfDay( zTokyo ) ;

如果您想查看在距离UTC偏移0小时分秒的情况下,该时刻同时是什么样子,请提取一个Instant

Instant firstMomentOfDateInTokyoAsSeenInUtc = firstMomentOfDateInTokyo.toInstant() ;

避免遗留类

你说:

Instant是一个弥合所有差距的类。

您不应该将传统的日期-时间类与现代的java.time类混合。旧的类是可怕的,非常可怕,是如何而不是进行面向对象编程的大师级。

如果必须与尚未为java.time更新的旧代码进行互操作,请使用添加到旧类中的新to…/from…/valueOf转换方法进行来回转换。但不要混合传统&在你的逻辑中是现代的。

这是一个";简单问题;而java.time旨在解决更复杂的问题?

否,java.time适用于所有面向业务和个人的日期时间工作(可能不是所有科学或学术工作)。遗留类是一场灾难,应该完全避免。

java.time类实际上在它们所代表的实体中非常简单。你只需要真正清楚你的需求是什么,你试图实现什么目标。

  • 表示偏移为零小时分秒的时刻:Instant
  • 表示日期,只是一个日期,没有时间,没有区域,因此本质上是模糊的:LocalDate
  • 表示在特定时区中看到的时刻:ZonedDateTime
  • 表示从UTC的特定偏移中看到的力矩:OffsetDateTime。这个类映射到标准SQL中的TIMESTAMP WITH TIME ZONE
  • 用一天中的时间表示日期,但缺少时区或偏移量的上下文,因此在大约26-27小时的范围内不明确:LocalDateTime。这个类映射到标准SQL中的TIMESTAMP WITHOUT TIME ZONE

遗留类仅对其中两个类具有等价物:(a)java.util.DateInstant替换,(b)java.util.Calendar(实际上是java.util.GregorianCalendar)被ZonedDateTime替换。被忽略的遗留类仅表示日期、带偏移量的日期和不带区域/偏移量的时间。java.sql.Date假装仅表示日期,但不表示日期。而java.sql.Timestamp,嗯,我从来都不知道那只野兽想做什么

仅供参考,偏移量仅为UTC之前或之后的小时-分-秒数。时区远不止这些。时区是特定地区人民使用的偏移量的过去、现在和未来变化的命名历史,由他们的政治家决定。

相关内容

最新更新