java 为什么 SimpleDateFormat.parse().getTime() 返回不正确的(负)值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15003498/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 18:10:00  来源:igfitidea点击:

Why does SimpleDateFormat.parse().getTime() return an incorrect (negative) value?

javadate

提问by user2052015

I have a time-stamp of type String and I am trying to convert it to a double (and find the result in seconds) and here is what I have done:

我有一个 String 类型的时间戳,我试图将它转换为双精度(并以秒为单位找到结果),这是我所做的:

double mytimeStamp = 0;

String timeStamp = new SimpleDateFormat(" mm ss S").format(new Date( ));   

SimpleDateFormat dateFormat = new SimpleDateFormat(" mm ss S");

try {
  mytimeStamp = ((double)dateFormat.parse(timeStamp).getTime())/1000;
} catch (ParseException e1) {
  // TODO Auto-generated catch block
  e1.printStackTrace();
}

System.out.println("timeStamp is: "+ mytimeStamp);

The problem is that I obtain a value such as -2722.515and I don't know why.

问题是我获得了一个值-2722.515,但我不知道为什么。

Why is it negative?

为什么是负面的?

Is there something wrong with the code?

代码有问题吗?

When I convert this time-stamp to mm ss Sdoes not match with the real time and this seems to be another problem!

当我将此时间戳转换为mm ss S与实时不匹配时,这似乎是另一个问题!

回答by Bernhard Barker

It's a time zone discrepancy issue.

这是一个时区差异问题。

Since you only specified the minute and second, the date will be on 1 Jan 1970 00:mm:ss(mmand ssbeing the minutes and seconds of the current time).

由于您只指定了分钟和秒,日期将打开1 Jan 1970 00:mm:ssmm并且ss是当前时间的分钟和秒)。

I simplified your example to:

我将您的示例简化为:

String timeStamp = "00 00 00";
SimpleDateFormat dateFormat = new SimpleDateFormat("HH mm ss");
double hour = dateFormat.parse(timeStamp).getTime()/1000.0/60/60;
System.out.println("hour is: "+ hour);

The hour printed out should be GMT's offset from the local time zone.

打印出来的小时应该是GMT本地时区的偏移量。

The reason for this is:

这样做的原因是:

SimpleDateFormatis locale-sensitive, so dateFormat.parse(timeStamp)will return create a Dateobject for a given time zone (the default is the local time zone). Then getTime()gets the number of milliseconds from midnight 1 Jan 1970 **GMT**. So the value will be offset by how far the local time zone is from GMT.

SimpleDateFormat是语言环境敏感的,因此dateFormat.parse(timeStamp)将返回Date为给定的时区创建一个对象(默认为本地时区)。然后从 中getTime()获取毫秒数midnight 1 Jan 1970 **GMT**。因此,该值将被本地时区距GMT.

How to fix it:

如何修复:

You could fix it by setting the time zone of the dateFormatobject before parseis called as follows:

您可以通过在dateFormat调用之前设置对象的时区来修复它,parse如下所示:

dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));

回答by Edwin Buck

--- Actually there's a much better way to do this, but if you want to use dates, skip to the pre-edit answer ---

--- 实际上有一个更好的方法来做到这一点,但如果你想使用日期,请跳到预编辑答案---

Dates don't actually do what you want, which appears to be time calculations outside of an actual need to pick times off a real world calendar.

日期实际上并没有做你想要的,这似乎是在实际需要之外的时间计算,从现实世界的日历中挑选时间。

You'd be much better off writing your own class, to avoid all the nasty special handling that Dates must do in order to keep up with the Gregorian Calendar. This special handling includes (but is not limited to) timezone awareness, daylight savings, declared "skipped days", leap seconds, leap years, etc.

您最好编写自己的类,以避免 Dates 为了跟上公历而必须进行的所有令人讨厌的特殊处理。这种特殊处理包括(但不限于)时区感知、夏令时、声明的“跳过天数”、闰秒、闰年等。

public TimeOnly {

   private long timestamp;
   private int millis;
   private int seconds;
   ... etc ...

   public TimeOnly(int hours, int minutes, int seconds, int millis) {
     this.timestamp = millis + seconds * 1000L + minutes * 60000L + hours * 3600000L; 
     this.millis = millis;
     this.seconds = seconds;
     ... etc ...
   }

   private TimeOnly(long timestamp) {
     this.timestamp = timestamp;
     this.millis = timestamp % 1000;
     this.seconds = timestamp % 60000L - this.millis;
     ... etc ...
   }

   public long getTimestamp() {
     return timestamp;
   }

   public int getMillis() {
     return millis;
   }

   public int getSeconds() {
     return seconds;
   }

   ... etc ...
}

public TimeFormatter {

  public TimeFormatter() {

  }

  public String format(Time time) {
    StringBuilder builder = new StringBuilder();
    builder.append(String.valueOf(time.getHours()));
    builder.append(":");
    builder.append(String.valueOf(time.getMinutes()));
    builder.append(":");
    builder.append(String.valueOf(time.getSeconds()));
    builder.append(".");
    if (time.getMillis() < 10) {
      builder.append("00");
    } else if (time.getMillis() < 100) {
      builder.append("0");
    }
    builder.append(time.getMillis());
    return builder.toString();
}

This solution may seem like it is reinventing the wheel, but really it is avoiding the use of an octagon as a wheel. Date's behavior doesn't seem to be what you want, although you could possibly make Date work for some limited range of values.

这个解决方案看起来像是在重新发明轮子,但实际上它是在避免使用八角形作为轮子。Date 的行为似乎不是您想要的,尽管您可以使 Date 对某些有限范围的值起作用。

If you want to get really fancy, you could make the above implement comparable, etc. However, I would advise against on thing. Don't provide update methods after construction, as this forces some pretty nasty recalculations and makes the code harder to maintain. Instead provide methods that return new TimeOnlys in response to the operations you wish to implement.

如果你想变得真正花哨,你可以让上面的工具具有可比性等等。但是,我建议不要这样做。不要在构造后提供更新方法,因为这会强制进行一些非常讨厌的重新计算并使代码更难维护。而是提供返回新 TimeOnlys 以响应您希望实现的操作的方法。

public TimeOnly addSeconds(int value) {
  int stamp = this.timestamp;
  stamp += value * 60000L;
  if (stamp < timestamp) {
    throw new Excepton("overflow");
  }
  return new TimeOnly(stamp);
}

Also, don't implement what you aren't going to use. Unused code tends to be fertile soil for bugs.

另外,不要实现你不会使用的东西。未使用的代码往往是错误的沃土。

And of course, the stock answer for all "time" things, consider using JodaTime, which differentiates all the different types of time measurement. However, for just a small problem like this, it's akin to using a tank to kill an ant.

当然,对于所有“时间”事物的常规答案,请考虑使用 JodaTime,它区分所有不同类型的时间测量。然而,对于这样一个小问题,它类似于用坦克杀死一只蚂蚁。

--- The pre-edit answer ---

--- 预编辑答案 ---

Without a full specification of the time (year, month, day, hour, minute, second, milliseconds) your time value as formatted in the first step will have lots of fields that are not specified. What goes in those fields will likely be garbage.

如果没有完整的时间规范(年、月、日、小时、分钟、秒、毫秒),您在第一步中格式化的时间值将有许多未指定的字段。这些领域的东西很可能是垃圾。

Then getTime()acts on the entire Dateobject translating both the valid fields and the garbage into a value, where the garbage may even modify the valid values (96 sec = 1 minute and 36 seconds, as the fields interact).

然后getTime()作用于整个Date对象,将有效字段和垃圾转换为一个值,其中垃圾甚至可以修改有效值(96 秒 = 1 分 36 秒,因为字段交互)。

The best way to go about this is to have all your "time only" Dates initialized to one known day, so when you do comparisons and math operations (is, 3 11 23> 1 02 10?) you get consistent results (yes, 3 11 23> 1 02 10, because it is actually 2013 02 10 00 03 11 23> 2013 02 10 00 03 11 23and not 2013 02 10 00 03 11 23compared to 2000 02 10 00 03 11 23

解决此问题的最佳方法是将所有“仅限时间”日期初始化为一个已知日期,因此当您进行比较和数学运算(是,3 11 23> 1 02 10?)时,您会得到一致的结果(是的,3 11 23> 1 02 10,因为它实际上是2013 02 10 00 03 11 23>2013 02 10 00 03 11 23并没有2013 02 10 00 03 11 23比较2000 02 10 00 03 11 23

When selecting the day to use, avoid days adjacent to Feb 29th, days that are near daylight savings shifts, etc.

在选择要使用的日期时,请避免临近 2 月 29 日的日子、接近夏令时的日子等。