如何获取一天的开始时间和结束时间?
像这样的代码是不正确的:
private Date getStartOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DATE);
calendar.set(year, month, day, 0, 0, 0);
return calendar.getTime();
}
private Date getEndOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DATE);
calendar.set(year, month, day, 23, 59, 59);
return calendar.getTime();
}
它不精确到毫秒。
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
If you want the entire day of a date as seen in UTC rather than in a time zone, use 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
These OffsetDateTime
objects will already be in UTC, but you can call toInstant
if you need such objects which are always in UTC by definition.
Instant start = odtStart.toInstant() ;
Instant stop = odtStop.toInstant() ;
start.toString() = 2020-01-29T06:00:00Z
stop.toString() = 2020-01-30T06:00:00Z
Tip: You may be interested in adding the ThreeTen-Extra library to your project to use its Interval
class to represent this pair of Instant
objects. This class offers useful methods for comparison such as abuts
, overlaps
, contains
, and more.
Interval interval = Interval.of( start , stop ) ;
interval.toString() = 2020-01-29T06:00:00Z/2020-01-30T06:00:00Z
The answer by mprivat is correct. His point is to not try to obtain end of a day, but rather compare to "before start of next day". His idea is known as the "Half-Open" approach where a span of time has a beginning that is inclusive while the ending is exclusive.
Joda-Time 2.3 offers a method for this very purpose, to obtain first moment of the day: withTimeAtStartOfDay()
. Similarly in java.time, LocalDate::atStartOfDay
.
Search StackOverflow for "joda half-open" to see more discussion and examples.
See this post, Time intervals and other ranges should be half-open, by Bill Schneider.
The java.util.Date and .Calendar classes are notoriously troublesome. Avoid them.
Use either Joda-Time or, preferably, java.time. The java.time framework is the official successor of the highly successful Joda-Time library.
The java.time framework is built into Java 8 and later. Back-ported to Java 6 & 7 in the ThreeTen-Backport project, further adapted to Android in the ThreeTenABP project.
An Instant
is a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = Instant.now();
Apply a time zone to get the wall-clock time for some locality.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
To get the first moment of the day go through the LocalDate
class and its atStartOfDay
method.
ZonedDateTime zdtStart = zdt.toLocalDate().atStartOfDay( zoneId );
Using Half-Open approach, get first moment of following day.
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() );
更新: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();
我需要用
LocalDateTime