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

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

这样的代码不准确:

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 asked 2019-08-28T21:05:53Z


Java 8

public static Date atStartOfDay(Date date) {
LocalDateTime localDateTime = dateToLocalDateTime(date);
LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
return localDateTimeToDate(startOfDay);
}
public static Date atEndOfDay(Date date) {
LocalDateTime localDateTime = dateToLocalDateTime(date);
LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
return localDateTimeToDate(endOfDay);
}
private static LocalDateTime dateToLocalDateTime(Date date) {
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
private static Date localDateTimeToDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

Update: I've added these 2 methods to my Java Utility Classes here

DateUtils.atStartOfDay

DateUtils.atEndOfDay

它位于Maven Central Repository中:

com.github.rkumsher

utils

1.3

Java 7和早期版本

使用Apache Commons

public static Date atEndOfDay(Date date) {
return DateUtils.addMilliseconds(DateUtils.ceiling(date, Calendar.DATE), -1);
}
public static Date atStartOfDay(Date date) {
return DateUtils.truncate(date, Calendar.DATE);
}

没有Apache Commons

public Date atEndOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
return calendar.getTime();
}
public Date atStartOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}


半开

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.

当前的日期时间框架是Java(java.util.Date/Calendar和Joda-Time),它们都使用了纪元的毫秒数。 但在Java 8中,新的JSR 310 java.time。*类使用纳秒分辨率。 如果切换到新类,基于强制一天中最后一刻的毫秒计数而编写的任何代码都将是不正确的。

如果采用其他分辨率,则比较来自其他来源的数据会出错。 例如,Unix库通常使用整秒,而Postgres等数据库将日期时间解析为微秒。

一些夏令时变化发生在午夜,这可能会进一步混淆事情。

Joda-Time 2.3提供了一种方法,用于获取当天的第一时刻:LocalDate。类似于java.time,LocalDate。

Search StackOverflow for "joda half-open" to see more discussion and examples.

比尔施奈德看到这篇文章,时间间隔和其他范围应该是半开放的。

避免遗留日期时间类

众所周知,java.util.Date和.Calendar类很麻烦。 避免他们。

使用Joda-Time或最好使用java.time。 java.time框架是非常成功的Joda-Time库的官方继承者。

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.

LocalDate是UTC时间轴上的一个时刻,分辨率为纳秒。

Instant instant = Instant.now();

应用时区来获取某个地方的挂钟时间。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

要获得当天的第一个时刻,请通过LocalDate类及其withTimeAtStartOfDay方法。

ZonedDateTime zdtStart = zdt.toLocalDate().atStartOfDay( zoneId );

使用半开放式方法,获得第二天的第一时刻。

ZonedDateTime zdtTomorrowStart = zdtStart.plusDays( 1 );

目前,java.time框架缺少withTimeAtStartOfDay类,如下面针对Joda-Time所述。 但是,ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 其中的类别是withTimeAtStartOfDay.通过传递一对Duration对象构造00:00:00。 我们可以从我们的ZonedDateTime个目标中提取Interval。

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

乔达时间

更新:Joda-Time项目现在处于维护模式,并建议迁移到java.time类。 我将这段保留完整的历史记录。

Joda-Time has three classes to represent a span of time in various ways: withTimeAtStartOfDay, 00:00:00, and Duration. An Interval has a specific beginning and ending on the timeline of the Universe. This fits our need to represent "a day".

我们将方法称为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();


在getEndOfDay中,您可以添加:

calendar.set(Calendar.MILLISECOND, 999);
Although mathematically speaking, you can't specify the end of a day other than by saying it's "before the beginning of the next day".
So instead of saying, if(date >= getStartOfDay(today) && date <= getEndOfDay(today)), you should say: if(date >= getStartOfDay(today) && date < getStartOfDay(tomorrow)). That is a much more solid definition (and you don't have to worry about millisecond precision).


java.time

使用内置于Java 8中的java.time框架。

import java.time.LocalTime;
import java.time.LocalDateTime;
LocalDateTime now = LocalDateTime.now(); // 2015-11-19T19:42:19.224
// start of a day
now.with(LocalTime.MIN); // 2015-11-19T00:00
now.with(LocalTime.MIDNIGHT); // 2015-11-19T00:00
// end of a day
now.with(LocalTime.MAX); // 2015-11-19T23:59:59.999999999


对于java 8,以下单行语句正在运行。 在这个例子中,我使用UTC时区。 请考虑更改您当前使用的TimeZone。

System.out.println(new Date());
final LocalDateTime endOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
final Date endOfDayAsDate = Date.from(endOfDay.toInstant(ZoneOffset.UTC));
System.out.println(endOfDayAsDate);
final LocalDateTime startOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
final Date startOfDayAsDate = Date.from(startOfDay.toInstant(ZoneOffset.UTC));
System.out.println(startOfDayAsDate);

如果与输出没有时差。 尝试:ZoneOffset.ofHours(0)


使用java8 java.time.ZonedDateTime而不是通过LocalDateTime查找开始日期的其他方法只是将输入ZonedDateTime截断为DAYS:

zonedDateTimeInstance.truncatedTo( ChronoUnit.DAYS );


另一个不依赖于任何框架的解决方案是:

static public Date getStartOfADay(Date day) {
final long oneDayInMillis = 24 * 60 * 60 * 1000;
return new Date(day.getTime() / oneDayInMillis * oneDayInMillis);
}
static public Date getEndOfADay(Date day) {
final long oneDayInMillis = 24 * 60 * 60 * 1000;
return new Date((day.getTime() / oneDayInMillis + 1) * oneDayInMillis - 1);
}

请注意,它返回基于UTC的时间


Java 8或ThreeTenABP

ZonedDateTime
ZonedDateTime curDate = ZonedDateTime.now();
public ZonedDateTime startOfDay() {
return curDate
.toLocalDate()
.atStartOfDay()
.atZone(curDate.getZone())
.withEarlierOffsetAtOverlap();
}
public ZonedDateTime endOfDay() {
ZonedDateTime startOfTomorrow =
curDate
.toLocalDate()
.plusDays(1)
.atStartOfDay()
.atZone(curDate.getZone())
.withEarlierOffsetAtOverlap();
return startOfTomorrow.minusSeconds(1);
}
// based on https://stackoverflow.com/a/29145886/1658268
LocalDateTime
LocalDateTime curDate = LocalDateTime.now();
public LocalDateTime startOfDay() {
return curDate.atStartOfDay();
}
public LocalDateTime endOfDay() {
return startOfTomorrow.atTime(LocalTime.MAX); //23:59:59.999999999;
}
// based on https://stackoverflow.com/a/36408726/1658268

我希望能有所帮助。


我试过这个代码,效果很好!

final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
final ZonedDateTime startofDay =
now.toLocalDate().atStartOfDay(ZoneOffset.UTC);
final ZonedDateTime endOfDay =
now.toLocalDate().atTime(LocalTime.MAX).atZone(ZoneOffset.UTC);


public static Date beginOfDay(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
public static Date endOfDay(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 999);
return cal.getTime();
}


我试过这段代码,效果很好!:

Date d= new Date();
GregorianCalendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
String s_d=d.getYear()+"-"+(d.getMonth()+1)+"-"+d.getDay();
DateFormat dateFormat = DateFormat.getDateInstance();
try {
// for the end of day :
c.setTime(dateFormat.parse(s_d+" 23:59:59"));
// for the start of day:
//c.setTime(dateFormat .parse(s_d+" 00:00:00"));
} catch (ParseException e) {
e.printStackTrace();
}