tl; dr
LocalDate // Represents an entire day, without time-of-day and without time zone.
.now( // Capture the current date.
ZoneId.of( "Asia/Tokyo" ) // Returns a `ZoneId` object.
) // Returns a `LocalDate` object.
.atStartOfDay( // Determines the first moment of the day as seen on that date in that time zone. Not all days start at 00:00!
ZoneId.of( "Asia/Tokyo" )
) // Returns a `ZonedDateTime` object.
一天的开始
获取在时区中看到的今天的完整长度。
使用Half-Open方法,其中开始是包含的,而结尾是排他的。这种方法解决了代码中无法解决一天中最后一秒的缺陷。
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-01-30T00:00 + 01:00 [非洲/突尼斯]
zdtStop.toString()= 2020-01-31T00: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中,但是toInstant
如果您需要这样的对象(根据定义始终位于UTC中),则可以调用。
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)都使用从纪元开始的毫秒数。但是在Java 8中,新的JSR 310 java.time。*类使用纳秒分辨率。如果切换到新类,则您基于强制一天中最后时刻的毫秒数编写的任何代码都是不正确的。
- 如果其他来源使用其他分辨率,则比较它们的数据将出错。例如,Unix库通常使用整秒,而诸如Postgres之类的数据库将日期时间解析为微秒。
- 某些夏令时更改会在午夜发生,这可能会使事情进一步混乱。
Joda-Time 2.3为此提供了一种方法,以获取一天的第一时间:withTimeAtStartOfDay()
。同样在java.time中LocalDate::atStartOfDay
。
在StackOverflow中搜索“ joda半开”以查看更多讨论和示例。
请参阅这篇文章,时间间隔和其他范围应该是半开放的,由Bill Schneider发表。
避免使用旧的日期时间类
众所周知,java.util.Date和.Calendar类很麻烦。避免他们。
使用java.time类。该java.time框架是非常成功的正式继任者乔达时库。
java.time
java.time框架内置于Java 8及更高版本中。在ThreeTen- Backport项目中向后移植到Java 6和7,在ThreeTenABP项目中进一步适应了Android 。
一个Instant
是在时间轴上的一个时刻UTC,分辨率为纳秒。
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 );
当前,java.time框架缺少Interval
如下面有关Joda-Time所述的类。但是,ThreeTen-Extra项目使用其他类扩展了java.time。该项目是将来可能向java.time添加内容的试验场。在其类中是Interval
。Interval
通过传递一对Instant
对象来构造一个。我们可以Instant
从ZonedDateTime
对象中提取一个。
Interval today = Interval.of( zdtStart.toInstant() , zdtTomorrowStart.toInstant() );
关于java.time
该java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.Date
,Calendar
,和SimpleDateFormat
。
要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要类。Hibernate 5和JPA 2.2支持java.time。java.sql.*
在哪里获取java.time类?
乔达时代
更新:Joda-Time项目现在处于维护模式,建议迁移到java.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();