我很少使用java.time
API,因为我的工作主要涉及捕获、存储和显示日期和时间,而不以任何方式操纵它们。但偶尔,我不得不做一些类似的事情:
- 获取过去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个类,其中两个是TimeZone
和Locale
类,其中只有一个是真正必要的(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*1000
0。
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.Date
被Instant
替换,(b)java.util.Calendar
(实际上是java.util.GregorianCalendar
)被ZonedDateTime
替换。被忽略的遗留类仅表示日期、带偏移量的日期和不带区域/偏移量的时间。java.sql.Date
类假装仅表示日期,但不表示日期。而java.sql.Timestamp
,嗯,我从来都不知道那只野兽想做什么
仅供参考,偏移量仅为UTC之前或之后的小时-分-秒数。时区远不止这些。时区是特定地区人民使用的偏移量的过去、现在和未来变化的命名历史,由他们的政治家决定。