Java计算时差错误

时间:2014-01-11 05:59:55

标签: java date time

我写了以下测试代码。

SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss",Locale.JAPAN);
Date date1 = format.parse("08:00:01");
Date date2 = format.parse("23:00:05");
Date date = new Date(date2.getTime() - date1.getTime());
System.out.println(format.format(date));

但令我惊讶的是结果是00:00:04,我无法理解为什么。 如果我将date2更改为“08:00:01”,结果将更改为09:00:00。 怎么会发生这种情况?

4 个答案:

答案 0 :(得分:2)

Date是时刻的类型,而不是时间间隔(时间)。

因此,如果您想计算两个时间点(Date对象)之间的时差(time inverval),您将获得另一种类型的时差,例如long(以秒为单位)或输入外部图书馆。

请参阅http://www.mkyong.com/java/how-to-calculate-date-time-difference-in-java/

第一个代码使用外部库,但您必须将毫秒转换为秒,分钟,小时等。

这是重要的部分:

Date d1 = ...;
Date d2 = ...;

//in milliseconds
long diff = d2.getTime() - d1.getTime();

long diffSeconds = diff / 1000 % 60;
long diffMinutes = diff / (60 * 1000) % 60;
long diffHours = diff / (60 * 60 * 1000) % 24;
long diffDays = diff / (24 * 60 * 60 * 1000);

System.out.print(diffDays + " days, ");
System.out.print(diffHours + " hours, ");
System.out.print(diffMinutes + " minutes, ");
System.out.print(diffSeconds + " seconds.");

第二个代码 使用外部库Joda-Time,它是时间最着名的Java库之一。

这是重要的部分:

DateTime dt1 = new DateTime(d1);
DateTime dt2 = new DateTime(d2);

System.out.print(Days.daysBetween(dt1, dt2).getDays() + " days, ");
System.out.print(Hours.hoursBetween(dt1, dt2).getHours() % 24 + " hours, ");
System.out.print(Minutes.minutesBetween(dt1, dt2).getMinutes() % 60 + " minutes, ");
System.out.print(Seconds.secondsBetween(dt1, dt2).getSeconds() % 60 + " seconds.");

如果您想使用Joda-Time库,您还可以使用其他类型进行时间间隔Intervalhttp://joda-time.sourceforge.net/key_interval.html

<小时/> 的修改Date类使用内部长变量来表示时刻。

长值是某个时刻之间的时间差(以毫秒为单位),
January 1, 1970, 00:00:00 GMT(在http://docs.oracle.com/javase/7/docs/api/java/util/Date.html中指定),
由于JST( UTC +09:00 ),日本标准时间为January 1, 1970, 09:00:00

所以,23:00:05 - 08:00:01将是15:00:04,并将转换为日本标准时间(UTC +09:00)= 24:00:04(1970年1月1日) ),即00:00:04(1970年1月2日)。

如果您计算08:00:01 - 08:00:01,那么它将是UTC的1970-01-01 00:00:00,即日本的1970-01-01 09:00:00标准时间。

<小时/> EDIT2 :我犯了一些错误。解析时我没有使用日本标准时间。无论如何,结果是一样的。

date1 = 08:00:01 in Japan = 23:00:01 in UTC
date2 = 23:00:05 in Japan = 14:00:05 in UTC
date = 14:00:05 in UTC - 23:00:01 in UTC = 15:00:04 in UTC
     = 23:00:05 in Japan - 08:00:01 in Japan = 15:00:04 in UTC (NOT in JAPAN)

为什么呢?因为计算是使用前面提到的长值完成的,该值是以 GMT (UTC +00:00,或仅UTC)计算的。 即使您指定使用日本时间,长值也是UTC。 Date将其转换为UTC,因此它会获得您提供-09:00的时间,Date中的内部流程将使用UTC完成。如果您要打印它,Date只需添加+09:00计算。

您还可以将其视为:
23:00:05 with (+09:00) - 08:00:01 with (+09:00) = 15:00:04 with (+00:00)

无论如何,15:00:04 UTC00:00:04 JST,因此如果您打印它,您将看到日本时间00:00:04,因为您指定使用的是日本标准时间。

答案 1 :(得分:2)

您的问题

您的代码存在问题:

  • 您正尝试使用时间点对象(java.util.Date)表示时间跨度。日期基于Unix Epoch以来的毫秒数。通过减去差异,您得到54,004,000毫秒的结果,相当于15小时4秒。当用于构造Date实例时,这意味着在1970年第一天下午3点后几秒钟(1970-01-01T15:00:04.000Z)。然后使用自动使用本地时区偏移的格式进行打印,以调整字符串中呈现的时间。在美国西海岸(从UTC的-08:00)运行相同的代码,结果是1970年1月1日的07:00:04。这是15:00:04调整回8小时。
  • 您认为将Locale传递给java.text.SimpleDateFormat会影响时区。它不是。区域设置不是时区。区域设置代表国家/文化和语言,与时区无关。在使用之前,您应该阅读the documentation方法。该文档称传递区域设置会影响the given pattern and the default date format symbols
  • 如果您只是处理时间而没有日期,则不需要语言环境。
  • 通常一个坏主意只能使用时间,没有日期和时区。您将忽略夏令时(DST)和影响结果的其他异常。如果您有一些假设情况而不是真实的商业数据,那么您应该只使用没有日期的时间。
  • 您正在使用应该避免使用的java.util.Date类。
  • 尝试使用时钟格式(XX:XX:XX)来表示时间跨度是很尴尬的。它会导致混乱,因为它很容易被误认为一段时间。它假设结果将少于24小时,(实际上)在投入使用时可能是一个错误的假设。

如果您有兴趣搜索StackOverflow.com或其他来源,您会发现许多示例和讨论。您应该(a)阅读您正在使用的课程的文档,以及(b)谷歌/ bing的日期,时间,间隔,期间,持续时间,跨度等词语。

ISO 8601持续时间

标准ISO 8601定义了一种将时间跨度描述为PnYnMnDTnHnMnS // © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so. // import org.joda.time.*; // import org.joda.time.format.*; // Better to specify a time zone explicitly rather than rely on default. // Time Zone list… http://joda-time.sourceforge.net/timezones.html (not quite up-to-date, read page for details) DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" ); DateTime dateTime1 = new DateTime( 2014, 1, 2, 8, 0, 1, timeZone ); DateTime dateTime2 = new DateTime( 2014, 1, 2, 23, 0, 5, timeZone ); // Calculate the span of time between them. Period period = new Period( dateTime1, dateTime2 ); 格式的字符串的方式。

Joda-Time使用ISO 8601作为其大部分默认值。

约达时间

Duration 2.3库有多个类,专门用于表示时间跨度:Period,Interval和Duration。

即将推出的Java 8中新的java.time。*类(受Joda-Time启发)有类似的类。这些类取代了java.util.Date&amp;目前捆绑在Java中的日历类。

示例代码

System.out.println( "dateTime1: " + dateTime1 );
System.out.println( "dateTime2: " + dateTime2 );
System.out.println( "period in ISO Duration format: " + period );
System.out.println( "period in words: " + PeriodFormat.getDefault().print( period ) );
System.out.println( "period in words, en français: " + PeriodFormat.wordBased( Locale.FRANCE ).print( period ) );
System.out.println( "period in words, for Japan: " + PeriodFormat.wordBased( Locale.JAPAN ).print( period ) );

转换为单词,并转储到控制台......

dateTime1: 2014-01-02T08:00:01.000+01:00
dateTime2: 2014-01-02T23:00:05.000+01:00
period in ISO Duration format: PT15H4S
period in words: 15 hours and 4 seconds
period in words, en français: 15 heures et 4 secondes
period in words, for Japan: 15時間4秒

跑步时......

{{1}}

答案 2 :(得分:1)

Date date = new Date(date2.getTime() - date1.getTime());
  • date2.getTime() - date1.getTime()将始终返回相同的long值。
  • 当您使用Date创建long对象时,它表示自标准基准时间称为“纪元”以来的指定毫秒数,即1970年1月1日,00:00:00 <强> GMT
  • 当您要求SimpleDateFormatter日本TZ 显示时间时,它会将 09:00:00 添加到您的ans。 (回答您的以下问题)
  

我无法理解为什么。如果我将date2更改为“08:00:01”,结果将更改为09:00:00。怎么会发生这种情况?

在代码中添加以下行:

System.setProperty("user.timezone", "<your preferred timezone>");

答案 3 :(得分:0)

我使用joda-time,但是,从这段代码(只是将代码包装在适当的java类中):

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class DateTest {
    public static void main (String[] args) throws Throwable{
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss",Locale.JAPAN);
Date date1 = format.parse("08:00:01");
Date date2 = format.parse("23:00:05");
Date date = new Date(date2.getTime() - date1.getTime());
System.out.println(format.format(date));
}
}

我明白了:

07:00:04

这是在Mac OS X Mavericks上的JDK 1.7.0_40上。