如何确定Java之间的现在和任意未来日期之间的时间

我目前正在尝试确定两个日期之间的时间量(其中一个是当前日期/时间,另一个是任意的未来日期)。 我只使用原生Java和Android API,但我遇到GregorianCalendar的一些问题。 到目前为止,我的代码可以在下面看到,但我遇到的问题是两个日期之间的时间非常不准确。 你可以看到我在这个例子中将未来的日期设定为圣诞节,但它告诉我那时已有超过62天,这显然是错误的。

date = new GregorianCalendar(); currentTime = date.getTimeInMillis(); calendar = new GregorianCalendar(); calendar.set(2011, 12, 25, 0, 0, 0); long difference = calendar.getTimeInMillis()-currentTime; long x = difference / 1000; seconds = x % 60; x /= 60; minutes = x % 60; x /= 60; hours = x % 24; x /= 24; days = x; 

调试时我添加了date.set(2011,11,23,13,1,15); 哪个更准确,但是当我相信正确的天数是31天时它仍然显示32天。

非常感谢您的任何帮助,非常感谢。

您的代码中的问题是方法calendar.set(2011, 12, 25, 0, 0, 0); 你想要的是calendar.set(2011, 11, 25, 0, 0, 0);

你也可以使用Calendar.DECEMBER

javadoc对于这种方法很清楚:

  • @param month用于设置MONTH日历字段的值。 *月值为0。 例如,0表示1月份。

而不是这样做:

  long x = difference / 1000; seconds = x % 60; x /= 60; minutes = x % 60; x /= 60; hours = x % 24; x /= 24; days = x; 

使用两个日期之间的差异(长值)构建一个Date对象。 然后,您可以在Calendar对象上执行setDate(Date)并解析它。

或者,你可以看一下相对容易使用的Joda Time Api。

java.time

Java 8及更高版本中内置的新java.time框架取代了旧的令人困惑的java.util.Date/.Calendar类

该教程演示了如何将一段时间定义为总天数以及期间,逻辑年数,月数和天数。

 LocalDate today = LocalDate.now(); LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1); Period p = Period.between(birthday, today); long p2 = ChronoUnit.DAYS.between(birthday, today); System.out.println("You are " + p.getYears() + " years, " + p.getMonths() + " months, and " + p.getDays() + " days old. (" + p2 + " days total)"); 

代码生成类似于以下内容的输出:

你有53岁零4个月29天。 (总共19508天)

我会通过将时区传递给now来改进Tutorial的示例,而不是隐含地依赖于JVM当前的默认时区,该时区可以随时改变。 虽然LocalDate没有分配时区(这就是本地的意思),但在确定时区是至关重要的。 例如,巴黎的新日早些时候比蒙特利尔早。

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

要获得直到下一个生日的日子,请通过替换年份来构建未来日期。 Java.time框架使用不可变对象,因此显示的语法基于原始值创建新对象,同时保持原始完整且不受影响。

 LocalDate birthdayThisYear = birthday.withYear( today.getYear() ); if ( birthdayThisYear.isBefore( today ) ) { birthdayThisYear.plusYears( 1 ); } long daysUntilBirthday = ChronoUnit.DAYS.between( today , birthdayThisYear ); 

乔达时间

Joda-Time库为java.time提供了灵感。 在Java 8技术不可用时使用Joda-Time,例如在Android中。 对于Android,你可以从其他来源找到Joda-Time的特殊版本来调解Dalvik中的一些缓慢加载问题。

在这种情况下,Joda-Time和java.time之间的代码类似。

 DateTimeZone zone = DateTimeZone.forID( "America/Montreal" ); LocalDate today = LocalDate.now( zone ); LocalDate birthdate = new LocalDate( 1966 , 1 , 2 ); LocalDate nextBirthday = birthdate.withYear( today.getYear() ); if ( nextBirthday.isBefore( today ) ) { nextBirthday = nextBirthday.plusYears( 1 ); } int daysUntilBirthday = Days.daysBetween( today , nextBirthday ).getDays(); 

Joda-Time还提供Period类。 但是,此类处理部分天,而java.time Period仅处理整天。 因此,我们需要将LocalDate实例转换为DateTime实例以提供给Period构造函数。

 DateTime start = today.toDateTimeAtStartOfDay( zone ); DateTime stop = nextBirthday.toDateTimeAtStartOfDay( zone ); Period period = new Period( start , stop ); 

默认格式化程序使用ISO 8601标准格式。 因此,调用period.toString()呈现三个月和两天:

P3M2D

首先,我建议您使用库方法进行计算。 首先,你不应该重新发明轮子。 其次,一些计算可能会变得棘手,例如,如果您还想报告数月或数年,并且需要考虑不同的月份长度和闰年。 尤其是,虽然在编写代码时很容易将60除以从秒转换为分钟,但在读取代码时,其中包含“分钟”或“秒”的方法名称将更清楚地表达为什么要除以60。

这些天Java有两个类的时间量:

  • Duration是指时钟时间之间的时间,例如2小时46分30秒(它也可以处理数天,但不知道数周或数月)。
  • Period是指日期之间的时间,例如2个月9天。

对于一般解决方案,我将展示如何将两者用于您的任务。 我首先找到日期之间的时间段。 如果那是从今天下午2点到后天上午10点,你将得到两天,我们想要1天20小时,所以我们需要在计算Duration之前减去1天进行调整。

  LocalDateTime dateTimeOne = LocalDateTime.of(2016, Month.APRIL, 25, 9, 5, 30); LocalDateTime dateTimeTwo = LocalDateTime.of(2017, Month.MAY, 15, 9, 10, 50); Period p = Period.between(dateTimeOne.toLocalDate(), dateTimeTwo.toLocalDate()); LocalDateTime dtOnePlusPeriod = dateTimeOne.plus(p); if (dtOnePlusPeriod.isAfter(dateTimeTwo)) { // too far p = p.minusDays(1); } Duration d = Duration.between(dateTimeOne.plus(p), dateTimeTwo); long hours = d.toHours(); d = d.minusHours(hours); long minutes = d.toMinutes(); d = d.minusMinutes(minutes); long seconds = d.getSeconds(); if (p.isZero() && d.isZero()) { System.out.println("0 seconds"); } else { if (p.getYears() != 0) { System.out.print("" + p.getYears() + " years "); } if (p.getMonths() != 0) { System.out.print("" + p.getMonths() + " months "); } if (p.getDays() != 0) { System.out.print("" + p.getDays() + " days "); } if (hours != 0) { System.out.print("" + hours + " hours "); } if (minutes != 0) { System.out.print("" + minutes + " minutes "); } if (seconds != 0) { System.out.print("" + seconds + " seconds"); } System.out.println(); } 

这打印

 1 years 20 days 5 minutes 20 seconds 

通过使用LocalDateTime我忽略了夏令时(DST)的变化。 如果您需要考虑这些因素,请使用ZonedDateTime

所有类都在java.time包中。 我将在底部包含指向Oracle教程的链接。

如果你想在Android上使用java.time ,你可以在ThreeTenABP中获得它们。 以下更多链接。

使用Java 9将在DurationtoHoursPart()toMinutesPart()toSecondsPart()中提供一些新方法,它们将为我们提供我们想要的小时,分​​钟和秒,就像我已经使用Period完成的方式一样。 然后我们将不再需要在计算单个字段时先减去小时数然后再减去分钟数。

链接

  • Oracle教程:跟踪:日期时间
  • ThreeTenABP
  • 问题:如何在Android Project中使用ThreeTenABP ,这是一本全面的入门指南