转换为欧洲/莫斯科时区后,过去的日期增加了一个小时

问题描述

有人可以解释一下,当我将其转换为莫斯科时区时,过去的日期为什么会增加一小时吗?

我正在使用JDK 1.6.0_12版本。

2011-04-02T11:39:46+0300 --> Sat Apr 02 12:39:46 MSK 2011     // 11:39 --> 12:39

我当前的系统时区是“欧洲/莫斯科” UTC + 3。

还请注意,过去的日期是俄罗斯早期使用的DST(夏令时)时区UTC + 4。 2014年10月,俄罗斯时区定义发生了立法变更。从那时起,俄罗斯一直使用UTC + 3。

我已经检查过 this old post of 2014

但是我认为这个问题看起来有所不同。

我们的开发人员希望每个过去的日期(例如DST期间的“ 2011-04-02T11:39:46 + 0300”)都应包含当前时区偏移值,即+0300而不是+0400。他们认为JRE将其错误地转换为UTC + 4,尽管“认时区偏移”在此处显示为+3。这种处理过去日期的时区偏移值的方法正确吗?

在JRE 1.8上给出了相同的输出,我认为这是更新版本,JRE 1.8中的TZ定义应该没有任何问题。

预先感谢!

Java代码

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

public class HelloWorld{
    public static void main(String []args)
    {
        String dateInString = "2011-04-02T11:39:46+0300";
        System.out.println(dateInString);

        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
            Date date = dateFormat.parse(dateInString);
            System.out.println(date);
        } catch (Exception e) {
            System.out.println(e);
        }

        final TimeZone tzone = TimeZone.getDefault();
        System.out.println("Default Time Zone ID - " + tzone.getID());
        System.out.println("Default Time Zone Offset - (" + (tzone.getRawOffset() / 60 / 60 / 1000) + ") hour.");
    }
}

输出

2011-04-02T11:39:46+0300
Sat Apr 02 12:39:46 MSK 2011
Default Time Zone ID - Europe/Moscow
Default Time Zone Offset - (3) hour.

解决方法

12:39是正确的时间

您得到正确的结果。在您的字符串2011-04-02T11:39:46+0300中,结尾的+0300是UTC的偏移量。因此,时间点与2011-04-02T08:39:46+00:00(UTC)相同。就像您自己说的那样,从2011年3月27日到2014年10月26日,莫斯科的UTC偏移量为+04:00。因此,要获得正确的莫斯科时间,Java字符串中的小时数需要增加1小时。还是将UTC时间设置为08:39:46的4小时。无论如何,此时莫斯科的时间是12:39:46。

或回答您的问题:

...为什么过去的日期换算成一小时后才转换 到莫斯科时区?

因为莫斯科的日期比字符串中的时间早1小时。

java.time

那就是说,我同意为工作推荐Java.time(现代Java日期和时间API)的人。 SimpleDateFormat是一类臭名昭著的麻烦制造者,而DateTimeZone的设计也很差且令人困惑。一切早已过时。现代化的API更好用。

例如:

    ZoneId zone = ZoneId.of("Europe/Moscow");
    ZonedDateTime zdt = ZonedDateTime.of(2011,4,2,11,39,46,zone); 
    System.out.println(zdt);

输出:

2011-04-02T11:39:46 + 04:00 [欧洲/莫斯科]

您还可以从输出中看到Java知道莫斯科当时的偏移量为+04:00。

您的问题很好地说明了java.time(与旧TimeZone类相对)为什么在时区和偏移量之间进行区分。时区包括与UTC的所有历史,当前和所有已知的未来时差。这就是您正确代表莫斯科的历史所需要的。在java.time中,时区由ZoneId对象标识,并遵循ZoneRules对象(大多数情况下,我们不必关心后者,而可以信任Java进行正确的转换)。 UTC偏移量由ZoneOffset对象表示。

问题:如何在Java 1.6中使用java.time?

这是您的幸运日。 java.time确实至少需要 Java 6

  • 在Java 8和更高版本以及更新的Android设备(API级别26以上)中,内置了现代API。
  • 在非Android Java 6和7中,获得ThreeTen Backport,这是现代类的backport(JSR 310的ThreeTen;请参见底部的链接)。
  • 在较旧的Android上,请使用废除旧书或Android版本的ThreeTen Backport。称为ThreeTenABP。在后一种情况下,请确保使用子包从org.threeten.bp导入日期和时间类。

链接

,

现代的Java日期/时间api和旧版的API(在jdk1.6中使用)都依赖于系统unix时间以及与JRE捆绑在一起的tzdata file。看起来开发人员是正确的,并且您的Java使用的是tzdata的一个非常旧的版本,而您的开发人员是正确的。

此外,tzdata保留有关法律变更的信息,如果您尝试转换过去的日期/时间,它将应用当时相关的转换规则。

关于JDK 1.8:8u101中的俄罗斯时区信息已更新,因此您应至少使用8u101才能更好地进行时区转换。

如果确实需要使用旧的Java,那么最好的选择是使用现代Java或手动更新JRE tzdata。

,

您需要将时区设置为SimpleDateFormat,如下所示:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) throws ParseException {
        String dateInString = "2011-04-02T11:39:46+0300";
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));// Set time-zone
        Date date = dateFormat.parse(dateInString);
        System.out.println(dateFormat.format(date));
    }
}

输出:

2011-04-02T12:39:46+0400

请注意,java.util.Date没有时区信息。它只是距标准Java时期1970-01-01T00:00:00Z的毫秒数,其中Z代表UTC(0小时偏移),也称为Zulu时区。在任何给定的时刻,位于单词任何部分的JVM上的毫秒数都相同。当您尝试打印java.util.Date对象时,JVM时区的日期时间字符串是根据此毫秒值计算的,并且会显示相同的值。如果要获取特定时区中的日期时间String,则需要将其明确设置为SimpleDateFormat并使用相同的格式来格式化java.util.Date