温馨提示:本文翻译自stackoverflow.com,查看原文请点击:java - How to obtain the start time and end time of a day?
date java

java - 如何获取一天的开始时间和结束时间?

发布于 2020-03-28 23:23:40

如何获取一天的开始时间和结束时间?

像这样的代码是不正确的:

 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();
}

它不精确到毫秒。

查看更多

查看更多

提问者
kalman03
被浏览
159
Basil Bourque 2020-02-11 01:40

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

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

Half-Open

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.

  • The current date-time frameworks of Java (java.util.Date/Calendar and Joda-Time) both use milliseconds from the epoch. But in Java 8, the new JSR 310 java.time.* classes use nanoseconds resolution. Any code you wrote based on forcing the milliseconds count of last moment of day would be incorrect if switched to the new classes.
  • Comparing data from other sources becomes faulty if they employ other resolutions. For example, Unix libraries typically employ whole seconds, and databases such as Postgres resolve date-time to microseconds.
  • Some Daylight Saving Time changes happen over midnight which might further confuse things.

在此处输入图片说明

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.

Avoid legacy date-time classes

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.

java.time

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中的日期时间类型表,包括现代的和传统的。

当前,java.time框架缺少Interval如下文针对Joda-Time所述类。但是,ThreeTen-Extra项目使用其他类扩展了java.time。该项目是将来可能向java.time添加内容的试验场。在其类中是IntervalInterval通过传递一对Instant对象来构造一个我们可以InstantZonedDateTime对象中提取一个

Interval today = Interval.of( zdtStart.toInstant() , zdtTomorrowStart.toInstant() );

乔达时代

更新:Joda-Time项目现在处于维护模式,建议迁移到java.time类。我保留此部分的历史记录。

乔达时间有三个类来表示各种方式的时间跨度:IntervalPeriod,和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();