简而言之
LocalDate
.now(
ZoneId.of( "Asia/Tokyo" )
)
.atStartOfDay(
ZoneId.of( "Asia/Tokyo" )
)
开始一天
获取今天在特定时区中的完整长度。
使用半开放方法,其中开始时间是包容性的,而结束时间是排除性的。这种方法解决了代码中无法考虑到一天中最后一秒的缺陷。
ZoneId zoneId = ZoneId.of( "Africa/Tunis" ) ;
LocalDate today = LocalDate.now( zoneId ) ;
ZonedDateTime zdtStart = today.atStartOfDay( zoneId ) ;
ZonedDateTime zdtStop = today.plusDays( 1 ).atStartOfDay( zoneId ) ;
zdtStart.toString() = 2020年1月30日00:00+01:00[非洲/突尼斯]
zdtStop.toString() = 2020年1月31日00:00+01:00[非洲/突尼斯]
在UTC中查看相同的时刻。
Instant start = zdtStart.toInstant() ;
Instant stop = zdtStop.toInstant() ;
start.toString() = 2020-01-29T23:00:00Z
stop.toString() = 2020-01-30T23:00:00Z
如果您想要以UTC格式获得日期的整天而不是时区,请使用OffsetDateTime
。
LocalDate today = LocalDate.now( ZoneOffset.UTC ) ;
OffsetDateTime odtStart = today.atTime( OffsetTime.MIN ) ;
OffsetDateTime odtStop = today.plusDays( 1 ).atTime( OffsetTime.MIN ) ;
odtStart.toString() = 2020-01-30T00:00+18:00
odtStop.toString() = 2020-01-31T00:00+18:00
这些 OffsetDateTime
对象已经是UTC时间,但是如果您需要始终处于UTC时间的对象,可以调用 toInstant
方法。
Instant start = odtStart.toInstant() ;
Instant stop = odtStop.toInstant() ;
start.toString() = 2020-01-29T06:00:00Z
stop.toString() = 2020-01-30T06:00:00Z
提示:您可能需要将ThreeTen-Extra库添加到您的项目中,以使用其Interval
类来表示这对Instant
对象。该类提供了有用的比较方法,如abuts
、overlaps
、contains
等等。
Interval interval = Interval.of( start , stop ) ;
interval.toString() = 2020-01-29T06:00:00Z/2020-01-30T06:00:00Z
半开放
mprivat的答案是正确的。他的观点是不要试图获取一天的结束时间,而是比较“下一天开始之前”。他的想法被称为“半开放”方法,其中一段时间有一个包含开始时间而不包括结束时间的部分。
- Java的当前日期时间框架(java.util.Date/Calendar和Joda-Time)都使用自epoch以来的毫秒。但是在Java 8中,新的JSR 310 java.time.*类使用纳秒分辨率。如果切换到新类并基于强制一天中最后一刻的毫秒计数编写的任何代码都将不正确。
- 如果其他来源的数据采用其他分辨率,则比较数据会出现故障。例如,Unix库通常使用整秒,并且像Postgres这样的数据库将日期时间解析为微秒。
- 有些夏令时更改发生在午夜之后,这可能会进一步混淆事情。
![enter image description here](https://istack.dev59.com/kE3r8.webp)
Joda-Time 2.3提供了一个方法来获取一天中的第一个时刻:withTimeAtStartOfDay()
。在java.time中,类似的方法是LocalDate::atStartOfDay
。
搜索StackOverflow以获取“joda半开放”以查看更多讨论和示例。
请参阅Bill Schneider的文章时间间隔和其他范围应该是半开放的。
避免使用旧版日期时间类
java.util.Date和.Calendar类非常麻烦。请避免使用它们。
使用java.time类。 java.time框架是高度成功的Joda-Time库的官方继承者。
java.time
java.time框架内置于Java 8及更高版本中。在ThreeTen-Backport项目中向Java 6和7进行了后移,进一步适配了ThreeTenABP项目中的Android。
Instant
是一个时间线上以UTC为基准的瞬间,分辨率为nanoseconds。
Instant instant = Instant.now();
应用时区以获取某个地区的
挂钟时间。
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
要获取一天的第一个时刻,请使用LocalDate
类及其atStartOfDay
方法。
ZonedDateTime zdtStart = zdt.toLocalDate().atStartOfDay( zoneId );
使用半开区间方法,获取以下一天的第一个时刻。
ZonedDateTime zdtTomorrowStart = zdtStart.plusDays( 1 );
![Table of all date-time types in Java, both modern and legacy](https://istack.dev59.com/WyAl2.webp)
目前,java.time框架缺少像Joda-Time中描述的Interval
类。然而,ThreeTen-Extra项目通过添加额外的类扩展了java.time。该项目是可能未来添加到java.time的试验场。其中一个类是Interval
。通过传递一对Instant
对象来构造Interval
。我们可以从我们的ZonedDateTime
对象中提取一个Instant
。
Interval today = Interval.of( zdtStart.toInstant() , zdtTomorrowStart.toInstant() );
关于java.time
java.time框架是Java 8及更高版本内置的。这些类取代了老旧的legacy日期时间类,如java.util.Date
, Calendar
, 和 SimpleDateFormat
。
要了解更多,请参阅Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
你可以直接在数据库中使用
java.time 对象进行交换。使用符合
JDBC 4.2 或更高版本的
JDBC driver。无需字符串,也不需要
java.sql.*
类。Hibernate 5 和 JPA 2.2 支持
java.time。
在哪里获取
java.time 类?
Joda-Time
更新:Joda-Time项目现在处于维护模式,并建议迁移到java.time类。我保留这个部分作为历史记录。
Joda-Time有三个类来以各种方式表示时间跨度:Interval
,Period
和Duration
。一个Interval
在宇宙时间线上具有特定的开始和结束。 这符合我们表示“一天”的需求。
我们调用withTimeAtStartOfDay
方法而不是将时间设置为零。由于夏令时和其他异常情况,一天的第一刻钟可能不是00:00:00
。
使用Joda-Time 2.3的示例代码。
DateTimeZone timeZone = DateTimeZone.forID( "America/Montreal" );
DateTime now = DateTime.now( timeZone );
DateTime todayStart = now.withTimeAtStartOfDay();
DateTime tomorrowStart = now.plusDays( 1 ).withTimeAtStartOfDay();
Interval today = new Interval( todayStart, tomorrowStart );
如果必须的话,你可以转换为java.util.Date。
java.util.Date date = todayStart.toDate()