过去的日期增加了一小时,当转换为Europe/Moscow时区时。

huangapple go评论65阅读模式
英文:

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 --&gt; Sat Apr 02 12:39:46 MSK 2011     // 11:39 --&gt; 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 = &quot;2011-04-02T11:39:46+0300&quot;;
		System.out.println(dateInString);

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

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

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 是一个臭名昭著的麻烦制造者类,DateTimeZone 也设计得非常糟糕和令人困惑。所有这些都已经过时。现代 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 及其子包中导入日期和时间类。

链接

英文:

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(&quot;Europe/Moscow&quot;);
	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.

答案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 = &quot;2011-04-02T11:39:46+0300&quot;;
		SimpleDateFormat dateFormat = new SimpleDateFormat(&quot;yyyy-MM-dd&#39;T&#39;HH:mm:ssZ&quot;);
		dateFormat.setTimeZone(TimeZone.getTimeZone(&quot;Europe/Moscow&quot;));// 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.

huangapple
  • 本文由 发表于 2020年10月13日 18:46:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/64333733.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定