问题描述
有人可以解释一下,当我将其转换为莫斯科时区时,过去的日期为什么会增加一小时吗?
我正在使用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
是一类臭名昭著的麻烦制造者,而Date
和TimeZone
的设计也很差且令人困惑。一切早已过时。现代化的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
导入日期和时间类。
链接
- Time Changes in Moscow Over the Years
- Oracle tutorial: Date Time解释如何使用java.time。
- Java Specification Request (JSR) 310,其中首先描述了
java.time
。 - ThreeTen Backport project,即
java.time
向Java 6和7(JSR-310的ThreeTen)的反向端口。 - Java 8+ APIs available through desugaring
- ThreeTenABP,Android版本的ThreeTen Backport
- Question: How to use ThreeTenABP in Android Project,其中有非常详尽的解释。
现代的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
。