推荐用于Joda-Time DateMidnight

时间:2013-07-16 00:03:55

标签: java date jodatime

LocalDate#toDateMidnight的javdoc内容如下:

  

从v1.5开始,建议您避免使用DateMidnight   toDateTimeAtStartOfDay()因为异常详细   下方。

     

如果默认时区切换,此方法将抛出异常   到夏令时午夜时间,这个LocalDate代表   那个转换日期。问题是没有这样的时间   在所需日期的午夜,因此抛出了这样的例外。

某些时区中不存在午夜这一事实似乎足以避免完全使用DateMidnight(假设您的代码没有使用已知没有此DST情况的固定时区,并且永远不会需要在将来使用不同的时区。)

但是,DateMidnight未被弃用,并且DateMidnight类本身的javadoc中没有类似的建议或警告。此外,DateMidnight构造函数愉快地接受即时和时区,使得在给定的一天不存在午夜,而不是像IllegalArgumentException那样抛出LocalDate#toDateMidnight。结果DateMidnight的行为与DateTime的行为类似,并且在一天的开始时间。

当某一天午夜不存在时,为什么LocalDate#toDateMidnight会抛出异常,而DateMidnight构造函数却没有? DateMidnight的推荐用例是什么?

6 个答案:

答案 0 :(得分:44)

没有充分理由使用DateMidnightLocalDate是更好的选择。多数民众赞成因为午夜不会在某些时区发生一次,完全搞乱了班级的可用性,并在应用程序中造成了错误。

修复了构造函数以避免最严重的问题,但是看到一个内部毫秒值指向01:00的DateMidnight对象并不是很好。

答案 1 :(得分:34)

建议使用新的DateTime()。withTimeAtStartOfDay()

答案 2 :(得分:8)

或者更好地直接使用LocalDate方法toDateTimeAtStartOfDay来绕过DateTime对象的创建(与answer above相关)。

new LocalDate().toDateTimeAtStartOfDay( myDateTimeZone )

答案 3 :(得分:3)

TL;博士

使用java.time课程,特别是LocalDate::atStartOfDay,而不是“午夜”这个滑溜的想法。

ZoneId z = ZoneId.of( "America/Montreal" );  // A time zone.
ZonedDateTime todayStart = LocalDate.now( z ).atStartOfDay( z );  // Produces a LocalDate (a whole day without time zone), then transforms into a `ZonedDateTime` (a moment on the timeline)

java.time

由于Joda-Time项目现在处于维护模式,并且团队建议迁移到java.time类,我将使用java.time添加示例。

如果您想整天代表整天,请使用LocalDate课程。 LocalDate类表示没有时间且没有时区的仅限日期的值。

time zone对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。

ZoneId z = ZoneId.of( “America/Montreal” );
LocalDate today = LocalDate.now( z );

正如本页所讨论的那样,试图查明当天的结束是不好的做法。首先,你有一个无限可分的问题,那一天的最后一秒。您是否解决了毫秒,微秒,纳秒或其他问题,因为所有这些都是常用的?而是使用新一天的第一个时刻。

让java.time确定当天第一时刻的挂钟时间。不要假设时间为00:00:00,因为夏令时(DST)等异常可能意味着第一时刻是01:00:00之类的时间。此类DST调整目前用于多个国家/地区的时区。

所以,要获得片刻,时间轴上的实际点,一天的开始时调用LocalDate::atStartOfDay。请注意,这是方法名称的简短版本,而不是Joda-Time的withTimeAtStartOfDay方法。在ZoneId中指定所需/预期时区以生成ZonedDateTime对象。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = today.atStartOfDay( z );

半开

那么如何表示一段时间?如果我想确定这一天的开始和结束,我如何在遵循这一建议的同时做到这一点?日期工作中常用的解决方案是半开放式方法。在此方法中,跨度的开头是包含,而结尾是独占。因此,“今天”意味着从一天的第一时刻开始并一直向前跑,但包括第二天的第一时刻。

ZonedDateTime zdtStartToday = LocalDate.now( z ).atStartOfDay( z );
ZonedDateTime zdtStartTomorrow = zdtStartToday.plusDays( 1 );

顺便说一下,ThreeTen-Extra项目有一个方便的Interval课程,适用于这样的时间范围。

Interval todayInterval = Interval.of( 
        zdtStartToday.toInstant() ,
        zdtStartTomorrow.toInstant()
    )

关于 java.time

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

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 4 :(得分:0)

查看我的代码中的异常

Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-27T00:00:00.000 (Asia/Amman)
    org.joda.time.IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-27T00:00:00.000 (Asia/Amman)

现在我使用

解决了这个问题
LocalDate currentDate=new LocalDate();
someMethodSetsTheDate(currentDate.toDateTimeAtStartOfDay().toDate());

而不是

someMethodSetsTheDate(new DateMidnight(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth()).toDate());

现在我建议使用 的 .toDateTimeAtStartOfDay() 避免类似的例外。

请随意编辑我的答案谢谢

答案 5 :(得分:0)

这里有一个更简单的解决方案,它将检查dateTime是否在当地时间的午夜

private boolean isAtMidnight(org.joda.time.DateTime dateTime) {
   return dateTime.toLocalDateTime().getMillisOfDay() == 0;
}