如何获取Java中包括最后一天在内的两个日期之间的差异?

时间:2018-11-23 18:51:23

标签: java date

我正在为rest API编写自动的bdds。 API会返回一个日期。我想获取API返回的日期与今天的当前日期之间的差额。

例如,API返回“ 2018年3月13日下午12:00”

今天的日期是“ 2018年3月11日下午12:00”

时间总是一样的,只是改变的日子。该API还将返回将来的日期。

我有这段代码:

Date currentDate = Date.from(Instant.now());
// endDate comes from the API
long diff = endDate.getTime() - currentDate.getTime();
long differenceInDays = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);

这将返回1,但我希望它包括最后一天。我只可以在+1的末尾添加endDate.getTime() - currentDate.getTime();,但是我不确定这是否是正确的方法。

我还读到,这通常不是一个好的解决方案,因为它没有考虑夏令时。我不确定夏时制到来时会如何或是否会影响我的自动bdd。捕获几天差异的最佳方法是什么?

将其视为how many days do I have left until expiration

1 个答案:

答案 0 :(得分:1)

您真正的问题是您的后端REST服务设计不良。

ISO 8601

首先,交换的日期时间值应采用标准ISO 8601格式,而不是某些本地化的表示字符串。

在解析/生成文本时, java.time 类中默认使用标准格式。

java.time

永远不要使用可怕的Date类。该类以及CalendarSimpleDateFormat等在多年前被JSR 310中定义的 java.time 类所取代。

  

Date.from(Instant.now())

请不要将可怕的旧式日期时间类(Date)与它们的替代物(Instant)混合在一起,即现代的 java.time 类。混合这些是不必要的并且令人困惑。

java.time 类完全替换了它们的前身。

  

时间总是一样的,只是改变的日子。该API还将返回将来的日期。

如果您只想交换日期值,没有时间,没有时区或偏移量,请使用LocalDate类,并交换ISO 8601格式YYYY-MM-DD,例如{{ 1}}。呼叫2018-03-11LocalDate.parse

  

long differentInDays = TimeUnit.DAYS.convert(diff,TimeUnit.MILLISECONDS);

将天数表示为毫秒数而不包含时区或UTC偏移量是很鲁ck的。日子并非总是24小时。它们可以是23、23.5、25或其他一些小时数。

如果您打算使用UTC来始终保持24小时工作日,请这样说。用时区或偏移量表示日期时间。例如,标准格式:LocalDate::toString,其中2018-03-11T00:00Z的末尾表示UTC,并发音为“ Zulu”。

因此,您的整个问题可以减少到此一线。

Z

未分区

如果您无法解决所有这些凌乱的设计问题,那么让我们继续前进,尝试使用这些凌乱的数据。

首先修复应该大写的ChronoUnit.DAYS.between( LocalDate.now( ZoneId.of( "America/Montreal" ) ) , // Get the current date as seen in the wall-clock time used by the people of a particular region (a time zone). LocalDate.parse( "2019-01-23" ) // Parse a string in standard ISO 8601 format for a date-only value. ) // Returns a `long` integer number of days elapsed. / am

pm

定义格式模式以匹配您的输入字符串。

指定 String input = "March 13, 2018 12:00 pm".replace( " am" , " AM" ).replace( " pm" , " PM" ); ,以确定在翻译文本时要使用的人类语言和文化规范。

Locale

解析为Locale locale = Locale.US; DateTimeFormatter f = DateTimeFormatter.ofPattern( "MMMM d, uuuu HH:mm a" ); ,因为您的输入缺少时区或UTC偏移量指示符。

LocalDateTime
  

ldt.toString():2018-03-13T12:00

故意LocalDateTime ldt = LocalDateTime.parse( input , f ); 没有时区或UTC偏移量的概念。因此,此类不能表示时刻,不是时间线上的一点。

如果您希望全天24小时工作,而不考虑各个地方(例如Daylight Saving Time (DST)的各个人使用的挂钟时间的异常情况,我们可以继续使用此类。

获取您的应用所针对的人(时区)所用的挂钟时间的当前日期。

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

如果未指定时区,则JVM隐式应用其当前的默认时区。该默认值可能在运行时(!)期间change at any moment,因此您的结果可能会有所不同。最好将desired/expected time zone明确指定为参数。

LocalDateTime的格式指定proper time zone name,例如continent/regionAmerica/MontrealAfrica/Casablanca。切勿使用2-4个字母的缩写,例如Pacific/AucklandEST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

IST

如果要使用JVM的当前默认时区,请提出要求并作为参数传递。如果省略,则会隐式应用JVM的当前默认值。最好明确一点,因为默认值可能会在运行时的任何时候被JVM中任何应用程序的任何线程中的任何代码更改。

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

在该日期的中午,没有特定的时区。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

计算过去的天数。

LocalDateTime ldtTodayNoon = LocalDateTime.of( today , LocalTime.NOON ) ;

当然,我们也可以只使用long daysElapsed = ChronoUnit.DAYS.between( ldtTodayNoon , ldt ) ; 而不是LocalDate来完成此操作,但是我按照您写的问题陈述进行操作。

请注意,在给定的示例中,字符串表示过去的日期。因此,我们的天数将为负。

分区

如果您确实要考虑某些区域某些日期的异常现象,那么应该正确地表示一个时刻,如上所述,并带有时区或UTC偏移量。

LocalDateTime

或者您今天想在所需的时区中午。如果在该区域的该日期中午不是该日的有效时间,则ZoneId z = ZoneId.of( "Africa/Tunis" ) ; ZonedDateTime zdt = ldt.atZone( z ) ; ZonedDateTime zdtNow = ZonedDateTime.now( z ) ; 类将进行调整。请务必阅读ZonedDateTime.of JavaDoc,以了解该调整的算法。

ZonedDateTime

根据小数秒或整个日历天来计算经过时间。

LocalDate today = LocalDate.now( z ) ;
ZonedDateTime zdtTodayNoon = ZonedDateTime.of( today , LocalTime.NOON , z ) ;

如果您坚持以毫秒为单位进行跟踪,请致电Duration::toMillis

Duration d = Duration.between( zdtTodayNoon , zdt ) ;  // For a calculation based in whole seconds plus a fractional second in nanoseconds without regard for a calendar, just using generic 24-hour days.
Period p = Period.between( zdtTodayNoon , zdt ) ;  // For a calculation based in whole days, for a number of years-months-days based on calendar dates.

所有这些已经在Stack Overflow上讨论过很多次了。您可以通过搜索这些 java.time 类名来了解更多信息并查看更多示例。


关于 java.time

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

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

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要long millisecondsElapsed = d.toMillis() ; // Entire duration as a total number of milliseconds, ignoring any microseconds or nanos. 类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore