What is wrong with java zoneinfo?

拈花ヽ惹草 提交于 2019-12-14 01:51:55

问题


I have a Europe/Moscow timezone in my Mageia 4.

The code like this

System.out.println(new java.util.Date());
System.out.println(System.getProperty("user.timezone"));

returns

Fri Oct 24 13:43:22 GMT+03:00 2014
GMT+03:00

if I set the system date in 24.10.2014

and that code returns

Sun Oct 26 14:44:26 GMT+03:00 2014
GMT+03:00

if I set the system date in 26.10.2014

In my point of view it is wrong behavior of java zoneinfo system. I downloaded the tzupdater and run it, the file Europe/Moscow was updated and now its size is 705 kB.

I try the code below:

TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"));
                System.out.println(new java.util.Date());
                System.out.println(java.util.TimeZone.getDefault());

and it returns

Fri Oct 24 15:10:34 MSK 2014
sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null]

and

Sun Oct 26 15:32:03 MSK 2014
sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null]

Why so? Why the offset is the same in these two cases?


回答1:


tl;dr

  • Do not use an offset (+03:00) when you mean a time zone (Europe/Moscow)
  • Never rely on JVM’s current default time zone.
  • Never use java.util.Date.

For a moment in UTC, use java.time.Instant.

Instant.now()

For a moment in a time zone, use java.time.ZonedDateTime.

ZonedDateTime.now(
    ZoneId.of( "Europe/Moscow" ) 
)

Offset versus time zone

As the comment by Jon Skeet notes, your JVM’s initial default time zone was not a time zone, it was merely an offset-from-UTC.

What is the difference? An offset is simply a number of hours, minutes, and seconds, positive (ahead of UTC) or negative (behind UTC). A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. The offset for a region can change whenever the politicians so deem. For example, many politicians buy into the lunacy of Daylight Saving Time (DST), and change the offset twice a year.

So if you set your time zone to a mere offset such as +03:00 (three hours ahead of UTC/GMT) rather than a time zone such as Europe/Moscow, your current date-time will always be reported as three hours ahead of UTC. Changes in offset for your region such as DST will be ignored, because you said so, you said "Always three hours ahead of UTC".

java.time

You are using terrible date-time classes that were supplanted years ago by the java.time classes defined in JSR 310.

Instead of TimeZone, use ZoneId.

ZoneId z = ZoneId.of( "Europe/Moscow" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;  // Capture the current moment as seen in the wall-clock time used by the people of a particular region (a time zone).

Avoid setting default time zone

You should only set the default time zone of your JVM as a last-ditch act of desperation.

Setting the default time zone (and default locale, by the way) immediately affects all code in all threads of all apps running within that JVM. You will be rudely changing the zone behind the backs of other programmers. You might even find that their code changes the zone behind your back, during runtime.

Better to write all your date-time handling to never rely on the current default zone (or locale). Specify explicitly your desired/expected time zone by passing optional arguments. Personally, I wish those time zone arguments were required rather than optional, to help educated programmers about date-time issue.

We can see an example of this in the code above. Notice how we pass a ZoneId for Russia to the now method. Otherwise, we would be capturing the current moment in the wall-clock time of whatever region happens to be the JVM’s current default time zone.

Tip: If critical, always confirm the time zone with the user.

java.util.Date::toString lies

Be aware that the toString method on the Date objects you were calling has the anti-feature of dynamically applying the JVM’s current default time zone while generating the text to represent that moment. While well-intentioned, this unfortunate design decision has confused countless programmers trying to wrangle date-time values in Java. A java.util.Date is actually in UTC, as a count of milliseconds since the first moment of 1970 in UTC. The time zone shown in the string is not actually in the Date object.

But this is moot, as this is one of many reasons to avoid this class entirely. Use java.util.Instant instead. Instead of GregorianCalendar, use ZonedDateTime.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.




回答2:


The problem was solved by adding the definition of correct timezone.

TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"));



回答3:


Your second test (26.10.2014) is after the change to wintertime so you probably need to correct the time as well by -1 hour



来源:https://stackoverflow.com/questions/26674248/what-is-wrong-with-java-zoneinfo

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!