Java 时差-奇怪的结果

Java 时差-奇怪的结果,java,date,time,Java,Date,Time,我有非常简单的代码,可以计算两次之间的差异: import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; public class JavaApplication8 { private static final SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm:ss.SSS"); pu

我有非常简单的代码,可以计算两次之间的差异:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;

public class JavaApplication8 {
    private static final SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm:ss.SSS");

    public static void main(String[] args) throws InterruptedException {
        Date start = GregorianCalendar.getInstance().getTime();
        Thread.sleep(100);
        Date end   = GregorianCalendar.getInstance().getTime();

        long diff = end.getTime() - start.getTime();
        System.out.println(timeFormat.format(diff));
    }
}

但是它打印的是
01:00:00.100
,而不是
00:00:00.100
,为什么呢?

这是一个时区问题
DateFormat.format()
将默认设置默认时区中的日期格式,该时区似乎是
UTC+1

您应该将
timeFormat
的时区设置为
UTC

timeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(timeFormat.format(diff));
此外,您应该在日期格式中使用
HH
而不是
HH
hh
表示12小时制

new SimpleDateFormat("HH:mm:ss.SSS");

更新:

但还有其他一些主要问题。当前您正在尝试设置持续时间的格式,但不应这样做。Java Date API,没有任何周期或持续时间的概念

从格式化中得到的值只不过是从历元开始的毫秒数(相当于差值)。它返回的是一个
Date
对象。虽然结果看起来可能是正确的,但从技术上讲,两个日期之间的差异表示一个期间或持续时间,这与日期(表示时间的特定瞬间)不同


你应该考虑到这个任务,它有代表这些概念的类,如“代码>句号< /代码>,<代码>即时< /代码>,和<代码>持续时间< /代码>

,因为你的时区是GMT + 1。

,您将找到
getTime()
的作用:

返回自1970年1月1日00:00:00 GMT以来的毫秒数 由此日期对象表示

所以100毫秒意味着
1970年1月1日,00:00:00.100 GMT
,也就是
1970年1月1日,01:00:00.100 GMT+1

您只需设置要将时间转换为的时区:

timeFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
您需要24小时格式:
HH:mm:ss.SSS
,否则它将显示12小时而不是00小时


编辑:正如jarnbjo所说:您正在尝试将日期格式化程序应用于一段时间间隔,这显然不会像您所期望的那样工作。JavaAPI中没有这样的时间间隔格式化程序。你必须自己写。

解决这个问题的其他方法。实际上,你们拥有的时间差不是当前时间的毫秒。这只是时间差,所以做一个简单的划分,你可以有小时:分钟:秒。 而且相当快

Date start = GregorianCalendar.getInstance().getTime();
        Thread.sleep(100);
        Date end = GregorianCalendar.getInstance().getTime();

        long longVal = end.getTime() - start.getTime();

        long hours = longVal / 3600000;
        long mins = (longVal % 3600) / 60000;
        long secs = longVal % 60000;

        System.out.println(hours + " " + mins + " " + secs);

您混淆了两个概念:

  • 您正在测量时间间隔(两个时间点之间的差异)
  • 您正在打印日期(一个时间点)
这两者不兼容,你总是会得到这样奇怪的效果。在你的例子中,正如其他评论所指出的,时区被混入其中。时区的概念只存在于日期(时间点),但对于间隔没有意义

您可以使用Jodatime库或JSR310:Date和Time API(我认为是Java8附带的)

使用Jodatime,您可以明确地构造一个区间:

DateTime start = new DateTime(2004, 12, 25, 0, 0, 0, 0);
DateTime end = new DateTime(2005, 1, 1, 0, 0, 0, 0);
Period period = new Period(start, end);
然后用PeriodFormatter格式化

PeriodFormatter daysHoursMinutes = new PeriodFormatterBuilder()
   .appendDays()
   .appendSuffix(" day", " days")
    .appendSeparator(" and ")
    .appendMinutes()
    .appendSuffix(" minute", " minutes")
    .appendSeparator(" and ")
    .appendSeconds()
    .appendSuffix(" second", " seconds")
    .toFormatter();

System.out.println(daysHoursMinutes.print(period));

通过分离一个时间点和两个时间点之间的时间概念,您可以确保没有任何其他意外(例如闰秒)。

基本上是因为SimpleDataFormat设计用于格式化日期、时间或日期/时间组合,当您试图使用它设置句点或持续时间的格式时。可能重复@jarnbjo。链接的副本只是将持续时间转换为日历实例。因此,如果我使用
newdate(long)
将持续时间转换为日期,那么根据那篇文章,这是正确的。但是,创建新日期相当于使用
long
毫秒。对此有何评论?@RanEldan。我想你没有看到我的编辑。可能你试过使用12小时制的
hh
格式。嘿,这确实有效。但我想知道这是否是一个短期的解决方案。如果这个程序被生活在另一个时区的人使用呢?该计划的目的不是为我自己,而是为来自中国、美国等国的人。这不是一个时区问题。问题是,正如我在对问题的评论中所写的那样,他试图使用一种数据类型的格式化程序来格式化第二种数据类型。@jarnbjo Nope。这是一个时区问题。他没有使用第二种数据类型。他使用的是毫秒,这是DateFormat的数据类型。100毫秒是有效的日期时间,为什么不呢?那么你将如何表示时间的瞬间?@m0skit0:他有一个数字100,但这个数字的含义不是时间的瞬间,他可以用SimpleDateFormat格式化。数字的含义是一个持续时间或期间,标准Java API中没有格式化程序。。。。这反过来应该不会有任何问题,因为SimpleDateFormat和SimpleDateFormat仍然使用UnixTimeStamp,而as
diff
应该是
1970年1月1日00:00:00.100
没有任何错误。还是我的思想有错误?@LuigiEdlCarno你确实有错误。SimpleDataFormat确实关心您的时区,如果您不设置它,它将占用您的系统时区。是的,这一瞬间是普遍存在的,但在地球的不同地区,这一瞬间并不相同。现在对我来说是16:33,但对中国人来说是21:33,尽管我们的Java时间戳是完全相同的数字。这一点一开始很难理解,但一旦你想起来,就很容易了。当然,你是对的。我完全忽略了SimpleDataFormat必须是特定于时区的。