What is wrong with java zoneinfo?

2019-07-15 09:32发布

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?

标签: java timezone
3条回答
【Aperson】
2楼-- · 2019-07-15 09:54

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

TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"));
查看更多
孤傲高冷的网名
3楼-- · 2019-07-15 09:58

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

查看更多
我只想做你的唯一
4楼-- · 2019-07-15 10:00

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.

table of date-time classes in Java, both modern and legacy.


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?

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.

查看更多
登录 后发表回答