如何获取一天的开始时间和结束时间?tl;博士开始新的一天半开式避免使用旧日期时间类java.time关于 java.time 城大时间

2022-08-31 07:18:28

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

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

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

它不能精确到毫秒。


答案 1

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

更新:我已将这两种方法添加到我的Java实用程序类

它位于 Maven Central Repository 中:

<dependency>
  <groupId>com.github.rkumsher</groupId>
  <artifactId>utils</artifactId>
  <version>1.3</version>
</dependency>

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

答案 2

tl;博士

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.

开始新的一天

获取在时区中看到的今天的完整长度。

使用半开放式方法,其中开头是包容性的,而结尾是排他性的。这种方法解决了代码中未能解释当天最后一秒的缺陷。

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

Z = 2020-01-29T23:00:00Z

z = 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

这些对象已经是 UTC 格式,但如果需要这些对象,可以调用这些对象,这些对象根据定义始终采用 UTC 格式。OffsetDateTime toInstant

Instant start = odtStart.toInstant() ;
Instant stop = odtStop.toInstant() ;

开始时间:2020-01-29T06:00:00Z

z = 2020-01-30T06:00:00Z

提示: 您可能有兴趣将 ThreeTen-Extra 库添加到项目中,以使用其 Interval 类来表示这对对象。此类提供了有用的比较方法,如 、、 等。Instantabutsoverlapscontains

Interval interval = Interval.of( start , stop ) ;

时间: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等数据库将日期时间解析为微秒。
  • 一些夏令时更改发生在午夜,这可能会进一步混淆事情。

enter image description here

Joda-Time 2.3为此提供了一种方法,以获得一天中的第一刻:withTimeAtStartOfDay()。同样,在 java.time 中,.LocalDate::atStartOfDay

在 StackOverflow 中搜索“joda half-open”,查看更多讨论和示例。

请参阅比尔·施耐德(Bill Schneider)的这篇文章,时间间隔和其他范围应该是半开放的

避免使用旧日期时间类

java.util.Date 和 .日历课程是出了名的麻烦。避免它们。

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

java.time

java.time 框架内置于 Java 8 及更高版本中。在ThreeTen-Backport项目中向后移植到Java 6和7,在ThreeTenABP项目中进一步适应Android。

即时是时间轴上以 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 );

Table of all date-time types in Java, both modern and legacy

目前,java.time框架缺少一个类,如下所述的Joda-Time。但是,ThreeTen-Extra项目使用其他类扩展了java.time。这个项目是未来可能添加到java.time的试验场。它的类之一是间隔。通过传递一对对象来构造 。我们可以从对象中提取一个。IntervalIntervalInstantInstantZonedDateTime

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

关于 java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧日期时间类,如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅 Oracle 教程。搜索 Stack Overflow 以获取许多示例和解释。规格是JSR 310

Joda-Time 项目现在处于维护模式,建议迁移到 java.time 类。

您可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。不需要字符串,不需要类。Hibernate 5 & JPA 2.2 支持 java.timejava.sql.*

从哪里获取 java.time 类?


城大时间

更新:Joda-Time项目现在处于维护模式,并建议迁移到java.time类。我将这一部分保留为历史。

Joda-Time 有三个类,以各种方式表示时间跨度:、 和 。An在宇宙的时间轴上有一个特定的开始和结束。这符合我们表示“一天”的需要。IntervalPeriodDurationInterval

我们调用该方法,而不是将一天中的时间设置为零。由于夏令时和其他异常,一天中的第一刻可能不是 。withTimeAtStartOfDay00: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();