【问题标题】:SimpleDateFormat behaves inconsistentlySimpleDateFormat 行为不一致
【发布时间】:2015-07-03 11:33:02
【问题描述】:

请看以下代码:

String timeString = "1980-01-01T14:00:00+0300";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date2 = sdf.parse(timeString);

// sdf.getCalendar().get(Calendar.ZONE_OFFSET);
System.out.println(sdf.format(date2));

现在,我所在的国家/地区拥有+2h 偏移量、+1 夏令时(目前)。 如果我按原样运行此代码,它将打印

1980-01-01T13:00:00+0200

如果我取消注释询问日历偏移量的行,程序的输出是

1980-01-01T14:00:00+0300

知道为什么会发生这种情况,我怎样才能获得一致的输出?

为了避免更不清楚的事情: 由于我正在处理一些遗留代码,因此 java 8 不是一个选项。是的,这里的关键是为什么,而不是解决方法是什么? 并且有 2 个 WHY:

  1. 为什么我传递一个 +0300 TZ 而默认情况下我收到一个 +0200 ? (SimpleDateFormat 应始终使用 TimeZone.getDefault,除非 另有规定)。
  2. 为什么仅仅因为我在它的日历实例上调用了一个 getter,它就会给出不同的答案。

【问题讨论】:

  • 其中有什么不一致的地方?当您不使用偏移量时,它会按原样为您提供日期,而当您使用它时,它会根据时区而变化。
  • @RamanShrivastava 调用 getter 一次不应更改格式函数的输出。

标签: java simpledateformat


【解决方案1】:

问题是,Calendar#get 不是一个简单的 getter,它看起来像这样:

public int  get(int field)
{
    complete();
    return internalGet(field);
}

根据 Javadoc,complete 在哪里执行此操作:

填写日历字段中任何未设置的字段。首先,如果尚未根据日历字段值计算时间值(与 Epoch 的毫秒偏移量),则调用 computeTime() 方法。然后调用computeFields()方法计算所有日历字段值。

我查了源代码here,但最新Java版本的代码也是一样的。

【讨论】:

  • 是的,我知道。但这只是一种“它做到了,因为它做到了”。对我来说,它绝对看起来像一个错误。
  • 我想说,这是一个值得商榷的设计决定。 Java 8 有一个新的日期和时间 API 是有原因的 :)
  • 这个答案对于一个小的 sn-p 来说是完美的,OP 如何更改他的代码以避免 complete() 破坏他的代码(例如,在日历实例上设置一些特定字段等)跨度>
  • 那不解释了。 SimpleDateFormat.format() 实际上也调用了Calendar.get()
【解决方案2】:

sdf.parse 将格式化程序的内部日历区域偏移更改为 +0300

    System.out.println(sdf.getCalendar());
    sdf.parse(timeString);
    System.out.println(sdf.getCalendar());

您可以在输出行的末尾看到差异

... ,ZONE_OFFSET=7200000,DST_OFFSET=0]
... ,ZONE_OFFSET=10800000,DST_OFFSET=0]

sdf.getCalendar().get(Calendar.ZONE_OFFSET); 将日历的时区偏移恢复到当前时区

【讨论】:

  • 问题是:它为什么要这么做?我的预感如下:日历有一个时区对象的引用。解析日期时,无法从“+0300”确定时区。它只是在日历的ZONE_OFFSET 字段中存储 3 小时的偏移量。 get() 方法在内部计算日历的所有字段时,它尚未对所有字段(包括 ZONE_OFFSET 字段)进行计算。日历使用它知道的时区来计算偏移量并确定不同的偏移量。
  • 是的,我假设这是最准确的答案。实际上,正如我在最初的帖子中所写的那样,我现在所处的位置是 +2 偏移,+1 dst 区域。这在 SimpleDateFormat 模式中以任何方式都不可代表。这就是我们将其组合为 +3 的原因。但是,在 Calendar 和 SimpleDateFormat 所做的整个内部处理中仍然存在一个错误,因为在 (+2 +1) 中转换 (+3 +0) 或保持原样的行为应该是一致的,并且不受影响得到。由于我不会在创建后以任何方式修改 sdf,因此 sdf.format(sdf.parse(txt)) 应该始终为 txt。
【解决方案3】:

SimpleDateFormate 使用日历来创建日期。日期对象是使用时间戳创建的,它在java.util.GregorianCalendar.computeTime() 中计算。在第 2789 行中使用了初始时区(设置为 java.text.SimpleDateFormat.initializeCalendar(Locale))。这就是您看到错误时区的原因。

当您调用get(Calendar.ZONE_OFFSET) 时,会调用方法java.util.GregorianCalendar.computeFields(),它使用最初设置的时区。

我猜这是一个错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    • 2020-12-21
    相关资源
    最近更新 更多