Java SimpleDataFormat的行为不一致

Java SimpleDataFormat的行为不一致,java,simpledateformat,Java,Simpledateformat,请参阅以下代码: 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))

请参阅以下代码:

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
你知道为什么会这样吗?我怎样才能得到一致的输出

为了避免任何不清楚的事情: 因为我正在处理一些遗留代码,所以Java8不是一个选项。是的,这里的关键是为什么,而不是解决办法是什么? 有两个原因:

  • 为什么我通过+0300 TZ,而默认情况下我收到+0200 TZ?(SimpleDataFormat应始终使用TimeZone.getDefault,除非 除非另有规定)
  • 为什么仅仅因为我调用了它的日历实例上的getter,它就会给出不同的答案呢

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

    public int  get(int field)
    {
        complete();
        return internalGet(field);
    }
    
    其中,
    complete
    根据Javadoc执行此操作:

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


    我查找了源代码,但最新Java版本的代码也相同。

    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)将日历的区域偏移恢复到当前时区

    SimpleDataFormat使用日历创建日期。Date对象是用时间戳创建的,它是在
    java.util.Gregoriacalendar.computeTime()中计算的。第2789行使用了初始时区(设置为
    java.text.simpleDataFormat.initializeCalendar(Locale)
    )。这就是为什么你看到了错误的时区

    调用
    get(Calendar.ZONE\u OFFSET)
    时,将调用方法
    java.util.Gregoriacalendar.computeFields()
    ,该方法使用最初设置的时区


    我想这是一个Bug。

    其中有什么不一致之处?当您不使用偏移量时,它会按原样给出日期,当您使用它时,它会根据时区的不同而变化。@RamanShrivastava调用一次getter不会改变format函数的输出。是的,我知道这一点。但这只是一种“它这样做是因为它这样做”。对我来说,这绝对像一个bug。我会说,这是一个有争议的设计决策。Java 8有一个新的日期和时间API是有原因的:)这个答案非常完美,只需一个小片段,说明OP如何更改他的代码,以避免
    complete()
    破坏他的代码(例如,在日历实例上设置一些特定字段等),而这并不能解释它
    SimpleDataFormat.format()
    实际上也调用了
    Calendar.get()
    。问题是:它为什么这样做?我的直觉是:日历引用了一个时区对象。解析日期时,无法从“+0300”确定时区。它只在日历的
    ZONE\u offset
    字段中存储3小时的偏移量。
    get()
    方法在尚未对所有字段(包括
    ZONE\u OFFSET
    字段)进行内部计算时,会对日历的所有字段进行计算。日历使用它知道的时区来计算偏移量并确定不同的偏移量。是的,我假设这是最准确的答案。事实上,我现在所在的位置,正如我在最初的帖子中所写的,我处于+2偏移量+1 dst区域。在SimpleDataFormat模式中,这是不可重复的。这就是为什么我们将其合并为+3。但是,Calendar和SimpleDataFormat所做的整个内部处理中仍然存在一个bug,因为在(+2+1)中转换(+3+0)或保持原样的行为应该是一致的,并且不受get的影响。因为我在创建后并没有以任何方式修改sdf,所以sdf.format(sdf.parse(txt))应该始终是txt。