英文:
Past date increased by one hour when converted to Europe/Moscow timezone
问题
<p>能有人解释一下为什么当我将这个过去的日期转换为莫斯科时区时,它会增加一个小时吗?</p>
<p>我正在使用JDK 1.6.0_12版本。</p>。
2011-04-02T11:39:46+0300 --> Sat Apr 02 12:39:46 MSK 2011 // 11:39 --> 12:39
我的当前系统时区是“Europe/Moscow” UTC+3。
<p>还请注意,这个过去的日期处于DST(夏令时)时区期间,之前在俄罗斯使用的是UTC+4。
自2014年10月以来,俄罗斯的时区定义发生了立法变化。从那以后,俄罗斯整年都使用UTC+3。</p>
<p>我已经检查过
<a href="https://stackoverflow.com/questions/26908043/getting-incorrect-time-leading-by-1-hour-with-europe-moscow-timezone"><b>这篇2014年的旧帖子<b></a>。</p>但我认为这个问题看起来不同。
<p>我们的开发人员期望每个过去的日期(比如“2011-04-02T11:39:46+0300”这样的日期,而且处于DST期间),应该包含当前的时区偏移值,即+0300,而不是+0400。他们认为JRE将其错误地转换为UTC+4,尽管“默认时区偏移量”显示为+3。处理过去日期的时区偏移值的方式是否正确?</p>
<p>在JRE 1.8上输出相同,我认为这是一个更新的版本,在JRE 1.8中不应该有时区定义的问题。</p>
提前致谢!
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.
英文:
<p>Could some one explain why this past date getting increased by one hour , when I convert it to Moscow Timezone ?</p>
<p>I'm using JDK 1.6.0_12 version. </p>.
2011-04-02T11:39:46+0300 --> Sat Apr 02 12:39:46 MSK 2011 // 11:39 --> 12:39
My current system time-zone is "Europe/Moscow" UTC+3 .
<p>Also please note that this past date is in DST(Daylight Saving ) time-zone period UTC+4 , earlier used in Russia.
There was a legislative change of Russian time-zone definitions in October 2014 . Since then Russia uses UTC+3 all through out a year .</p>
<p>I already checked
<a href="https://stackoverflow.com/questions/26908043/getting-incorrect-time-leading-by-1-hour-with-europe-moscow-timezone"><b>this old post of 2014 <b></a> .</p> But I think this issue looks different.
<p>Our developers expect that every past date (like "2011-04-02T11:39:46+0300" and which is in DST period ), should contain current time zone offset value i.e +0300 , not +0400 . And they think JRE is converting it incorrectly to UTC+4 , though "Default Time Zone Offset" shows +3 here . Is this way of handling time-zone offset value for past dates correct?</p>
<p> Same output is given on JRE 1.8 , which I think is an updated version ,there shouldn't be any issue in TZ definition in JRE 1.8.
Thanks in Advance !
Java Code:
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.");
}
}
Output :
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.
答案1
得分: 3
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 日期和时间 API —— java.time
来处理这项任务的人。SimpleDateFormat
是一个臭名昭著的麻烦制造者类,Date
和 TimeZone
也设计得非常糟糕和令人困惑。所有这些都已经过时。现代 API 处理起来要舒服得多。
例如:
ZoneId zone = ZoneId.of("Europe/Moscow");
ZonedDateTime zdt = ZonedDateTime.of(2011, 4, 2, 11, 39, 46, 0, zone);
System.out.println(zdt);
输出:
> 2011-04-02T11:39:46+04:00[Europe/Moscow]
您还可以从输出中看出,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,这是现代类的后备(ThreeTen 用于 JSR 310;请参见底部的链接)。
- 在较旧的 Android 设备上,可以使用 desugaring 或 ThreeTen Backport 的 Android 版本。后者称为 ThreeTenABP。在这种情况下,确保从
org.threeten.bp
及其子包中导入日期和时间类。
链接
- 莫斯科多年来的时间变化
- Oracle 教程:日期时间,介绍如何使用 java.time。
- Java 规范请求 (JSR) 310,首次描述了
java.time
。 - ThreeTen Backport 项目,将
java.time
后备到 Java 6 和 7(ThreeTen 用于 JSR-310)。 - 通过 desugaring 可用的 Java 8+ API
- ThreeTenABP,ThreeTen Backport 的 Android 版本
- 问题:如何在 Android 项目中使用 ThreeTenABP,附有非常详细的解释。
英文:
12:39 is the correct time
You are getting the correct result. In your string, 2011-04-02T11:39:46+0300
, the trailing +0300
is an offset from UTC. So the point in time is the same as 2011-04-02T08:39:46+00:00
(UTC). As you say yourself, Moscow was at UTC offset +04:00 from 27 March 2011 to 26 October 2014. So to get the correct time for Moscow Java needs to add 1 hour to the hour in the string. Or 4 hours to the UTC hour of 08:39:46. In any case the time in Moscow was 12:39:46 at this point in time.
Or to answer your question:
> … why this past date getting increased by one hour , when I convert it
> to Moscow Timezone ?
Because Moscow on that date was 1 hour ahead of the time in the string.
java.time
That said I agree with those who recommend java.time, the modern Java date and time API, for the job. SimpleDateFormat
is a notorious troublemaker of a class, and Date
and TimeZone
are poorly and confusingly designed too. All are long outdated. The modern API is so much nicer to work with.
For example:
ZoneId zone = ZoneId.of("Europe/Moscow");
ZonedDateTime zdt = ZonedDateTime.of(2011, 4, 2, 11, 39, 46, 0, zone);
System.out.println(zdt);
Output:
> 2011-04-02T11:39:46+04:00[Europe/Moscow]
You can also see from the output that Java knows that Moscow was at offset +04:00 back then.
Your question very well illustrates why java.time (opposite the old TimeZone
class) makes the distinction between a time zone and an offset. A time zone includes all historic, the present and all known future offsets from UTC. This is what you need to represent historic times in Moscow correctly. In java.time a time zone is identified by a ZoneId
object and obeys a ZoneRules
object (most often we need not concern ourselves with the latter and can just trust Java to make the right conversions). A UTC offset is represented by a ZoneOffset
object.
Question: how could I use java.time with Java 1.6?
This is your lucky day. java.time exactly requires at least Java 6.
- In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
- In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
- On older Android either use desugaring or the Android edition of ThreeTen Backport. It’s called ThreeTenABP. In the latter case make sure you import the date and time classes from
org.threeten.bp
with subpackages.
Links
- Time Changes in Moscow Over the Years
- Oracle tutorial: Date Time explaining how to use java.time.
- Java Specification Request (JSR) 310, where
java.time
was first described. - ThreeTen Backport project, the backport of
java.time
to Java 6 and 7 (ThreeTen for JSR-310). - Java 8+ APIs available through desugaring
- ThreeTenABP, Android edition of ThreeTen Backport
- Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
答案2
得分: 0
现代的 Java 日期/时间 API 和旧版本(在 JDK 1.6 中使用)都依赖于系统的 Unix 时间和随 JRE 捆绑的 tzdata 文件。看起来开发者是正确的,你的 Java 正在使用一个非常旧版本的 tzdata,你的开发者是正确的。
此外,tzdata 保留有关法律变更的信息,如果您尝试转换过去的日期/时间,它将应用当时相关的转换规则。
关于 JDK 1.8:在 8u101 中更新了俄罗斯时区信息,因此您应该至少使用 8u101 以获得更好的时区转换。
对于您来说,最好的决策是使用现代的 Java 或者在确实需要使用旧版本时手动更新您的 JRE tzdata。
英文:
Both modern java date/time api and legacy one (that is used in jdk1.6) rely on system unix time and the tzdata file bundled with the JRE. Looks like the developers are right and your java is using a very old one version of tzdata and your developers are right.
Also, the tzdata keeps information about legal changes and if you are trying to convert date/time in the past, it will apply conversion rules that were relevant at that time.
Regarding JDK 1.8: there was an update to Russian timezone information in 8u101, so you should use at least 8u101 for a better timezone conversion.
The best decision for you would be to use modern java or update your JREs tzdata manually if you really need to use an old one.
答案3
得分: 0
你需要按照以下所示设置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")); // 设置时区
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
。
英文:
You need to set time-zone to SimpleDateFormat
as shown below:
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));
}
}
Output:
2011-04-02T12:39:46+0400
Note that java.util.Date
does not have time-zone information. It's simply the number of milliseconds from the standard Java epoch of 1970-01-01T00:00:00Z
where Z
stands for UTC (0
hour offset), also known as Zulu
time-zone. At any given moment, you will get the same number of milliseconds on the JVMs sitting in any part of the word. When you try to print an object of java.util.Date
, the date-time string for the JVM's time-zone is calculated from this milliseconds value and the same is displayed. If you want to get the date-time String
in a specific time-zone, you need to set it explicitly to the SimpleDateFormat
and use the same to format the java.util.Date
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论