Java time ignores daylight saving rules - java

I am exporting TZ variable in POSIX format to set timezone on Linux. For instance:
export TZ="EST+5EDT,M3.2.0/02:00,M11.1.0/02:00"
Linux date command returns:
Wed Mar 14 03:47 EDT 2018
Java ZonedDateTime.now() returns:
2018-03-14T02:47:36.808[GMT-05:00]
It seems that Java does not take into account DST rule. What can be wrong?

I'm not sure what linux version you're using, but I've tested in Red Hat 4.4 and it accepts IANA's names:
export TZ=America/New_York
date
And the output is:
Qua Mar 14 08:37:25 EDT 2018
I've also checked some articles on web and all examples use names like "America/New_York", "Europe/London" and so on.
But anyway, if your linux version doesn't work with this, it's better to change your code to not use the JVM default timezone:
ZonedDateTime.now(ZoneId.of("America/New_York"));
Actually, I think it's better to use a specific timezone, because the default can be changed at runtime, for any application running in the same JVM. Even if you have control over what the applications do, some infrastructure/environment maintainance can change that, either on purpose or by accident - it happened to me once, which made me start using explicit timezones names everywhere.
And always prefer IANA's names, in the format Continent/Region. Names like EST+5EDT are fixed, in the sense that they represent just an offset (in this case, GMT-05:00), without any Daylight Saving rules. Only names like America/New_York contains DST rules.

Related

Is DateTimeFormatter Operating System Dependent?

The following code:
Locale locale = new java.util.Locale("en", "AU");
DateTimeFormatter fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(locale);
ZonedDateTime zdt = ZonedDateTime.parse("2021-11-19T14:13:12Z");
return fmt.format(zdt);
gives me different results depending on operating system:
OS
Format
Java
Windows
19 Nov. 2021, 2:13:12 pm
1.8.0_312
macOS
19 Nov 2021, 2:13:12 pm
1.8.0_312
Linux/Ubuntu
19/11/2021, 2:13:12 PM
1.8.0_292
is this expected? My unit tests fail in different contexts, and it seems rather unexpected to me
Java 8: no expected dependency on operating system
The Java Runtime engine has had locale data built-in since early versions of Java. From Java 8 data from Unicode Common Locale Data Repository, CLDR, are also included with Java, but default Java’s own data are still preferred.
The result from formatting date and time using your formatter is expected to depend on three or four factors:
On locale.
On the provider of the locale data. Java can get its locale data from up to four sources: the JRE’s own data, CLDR, an installed service provider (so yourself, so to speak) and the host operating system. Which ones are used and with what priority is controlled by the java.locale.providers system property.
On the versions of the locale data from the selected providers.
In case of the HOST locale data provider obviously on the operating system.
In Java 8 the default is to use the JRE’s own locale data as first priorioty and any configured service provider second. Not the host operating system in any case. So not setting the system property java.locale.providers is equivalent to setting it to JRE,SPI. Which in turn does not incur any dependency on the operating system.
In Java 9 the default was changed to be equivalent to CLDR,COMPAT where COMPAT is the new name for JRE. Which in turn still does not incur any dependency on the operating system.
Why did you observe different results?
Assuming that you are relying on the default locale data providers, JRE and SPI, and that you have not added your own service provider (SPI), your different results must be caused by different versions of Java’s locale data. So apparently they updated the locale data from Java 1.8.0_292 to 1.8.0_312.
The slight difference between Windows and Mac both running 1.8.0_312, the dot after Nov, then? While I would have expected them to ship the same version of the locale data with Java installers for different operating systems, the explanation that I can think of is that in this case they may not have.
More observed results
I did some runs of your code too. The following table includes your observations and mine. In all runs I have set java.locale.providers to JRE,SPI to get Java 8 behaviour on Java 9 and later too.
Java
Format
OS
Tester
1.8.0_121
19/11/2021, 2:13:12 PM
MacOS
Me
1.8.0_271
19/11/2021, 2:13:12 PM
Windows
Me
1.8.0_292
19/11/2021, 2:13:12 PM
Ubuntu
You
1.8.0_312
19 Nov. 2021, 2:13:12 pm
Windows
You
1.8.0_312
19 Nov 2021, 2:13:12 pm
MacOS
You
9.0.4
19 Nov 2021, 2:13:12 pm
MacOS
Me
15.0.1
19/11/2021 2:13:12 PM
Windows
Me
The last result is funny, as is your result with the dot.
Links
Documentation of LocaleServiceProvider
Internationalization Enhancements in JDK 9 — CLDR Locale Data Enabled by Default

How to change Tomcat 7's server timezone?

My application deployed in a Debian vps in US, Los Angeles. So code like new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date()) will return current time of America/Los Angeles.
Can I do some setting in Tomcat's configuration file (server.xml or catalina.sh or what?) so that get current time will return a specified TimeZone like GMT+8 or Asia/Taipei ???
With all of the different places where you can set timezones, it's (in general) always best to explicitly set the timezone when you're dealing with times. Yes, your server is in Los Angeles, but where are your users?
As explicitly dealing with timezones makes your application somewhat more complex (but also more correct, less surprising, harder to test) the next best would be to explicitly make tomcat (java) know what timezone your server clock is set to. Careful: There are some levels to set this: Set your server clock to UTC, configure your server OS to be PST and then let java know of the timezone that your server is on, e.g. in setenv.sh do CATALINA_OPTS="$CATALINA_OPTS -Duser.timezone=America/Los_Angeles" (or whatever your timezone is) to configure Java for your timezone.
Test, rinse, repeat until happy with the configuration. But make it an explicit choice on all different levels that you can set your hands on. Resolving the timezone is rather a java than a tomcat feature.
It's quite important for maintainability of your software to always store times in UTC. If you ever store in your local timezone, calculating any other timezone will be a mess - think daylight savings times, change of timezones of different areas of the world etc.
So: Set your server to UTC, then get the current time, check if it's correct. For display purposes, you might use the (user's) local timezone (e.g. PST), but for storage and calculation, UTC is highly recommended.
Tomcat's personal timezone would be specified in its startup script in a form like:
-Duser.timezone=GMT
In the Linux just add the following line in setenv.sh which is at CATALINA_HOME/bin/.
CATALINA_OPTS="-Duser.timezone=Asia/{your zone}"
For Windows - Go to catalina.bat and add CATALINA_OPTS property(under start).
:doStart
shift
set CATALINA_OPTS=-Duser.timezone=America/Denver
If you want to change it from eclipse... Run --> Run configuration --> Apache tomcat --> Tomcat Server --> VM arguments add -Duser.timezone=America/Montreal

In java getting different value for TimeZone.getDefault().getID() on different system

I am getting different value for TimeZone.getDefault().getID() on different systems.
For example, in case of Indian standard time,
On one of the system we are getting "GMT+5:30":
while on another we are getting "Asia/Calcutta".
We are expected to get "Asia/calcutta" strings on all machines.
Why is there an inconsistency for such behavior?
Is there any way to get consistent behavior across different systems windows/MAC?
What is the best way to get client time zone programmatically using Java?
It sounds like the two machines are configured differently - it's as simple as that. For example, if your first system is Windows you may have unticked the box saying "Automatically adjust for Daylight Saving Time".
If you know you need Asia/Calcutta for all systems, then use that explicitly. I try to avoid using the default time zone wherever possible, to be honest. Even when I do use it, I try to use it explicitly so that it's obvious from the code that I'm trying to use the system time zone, rather than it just being accidental.
In case someone has the same problem on Linux and can't find an answer:
Two linux machines set to UTC
one JVM reporting incorrect timezone (ex CET)
the other reporting correctly UTC
The problem could be:
The faulty machine has this symlink: /etc/localtime -> /usr/share/zoneinfo/Europe/Paris
/usr/share/zoneinfo/Europe/Paris contains UTC information
The cause for this could have been a simple cp /usr/share/zoneinfo/UTC /etc/localtime replacing the contents of /usr/share/zoneinfo/Europe/Paris instead of overwriting /etc/localtime which was a symlink instead of a file!

Glassfish Logs Date and DST?

So, the system time on my Ubuntu server updated appropriately with DST on Sunday.
However, the timestamp on my glassfish logs are still an hour off. I've restarted the application server, but this doesn't seem to have changed anything.
A quick hello-world indicates that the JVM grabs the (correct) system time. Any ideas on why
the time in the glassfish logs do not match the system or JVM date and time?
As suggested, I've also run the tzupdate tool. This does not seem to have done much.
Any ideas what's going on? How can I repair this?
You might need to update the JVM to get the latest time zone information. Or use the Timezone Updater Tool.

Joda time zone different than JDK's

In my client, I have this code:
System.out.println("Java tz: " + TimeZone.getDefault());
System.out.println("Joda tz: " + ISOChronology.getInstance());
These two lines run one after another. I never set time zone or user.timezone manually, just rely on defaults read from the OS & local system.
When executed, they produce:
Java tz: sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Joda tz: ISOChronology[America/Phoenix]
System time zone is indeed Phoenix, not UTC. How can Joda be correct and JDK be wrong?
EDIT: This is a Windows 7 x64 host, JRE is 1.6.22 x64.
EDIT 2: Don't try to reproduce it. It only fails on some, not all systems (like a few dozen in our 3k user base). I already know Joda checks user.timezone and then TimeZone.getDefault(). So I am looking for an explanation on how can it be different between me calling TimeZone directly and Joda doing it by itself.
When you say "defaults read from the OS & local system", there isn't a single, well-defined place to read this default from. Even the API documentation itself says
Gets the default TimeZone for this host. The source of the default TimeZone may vary with implementation.
So the simple answer is that Joda and your JVM are inferring the default time zone from different sources of information. The point to remember about this is that the default is a guess, not something that the JVM can definitively get access to.
For Sun's 1.5.0_06 JVM on Linux, the following order is used:
Looks to environment variable TZ
Looks for the file /etc/sysconfig/clock and tries to find the "ZONE" entry.
Compares contents fo /etc/localtime with the contents of every file in /usr/share/zoneinfo recursively. When the contents matches, it returns the path and filename, referenced from /usr/share/zoneinfo.
Joda 1.6.2 uses:
The system property user.timezone.
If the above is null or not a valid identifier, the value of the JDK's TimeZone default is used.
If that fails, UTC is used.
So if you have the above versions of JDK and Joda, I suggest that the user.timezone property may be set in your environment. Other versions may will use other algorithms to acquire the default, though.
Edit: In Sun's JDK 1.6.0_22, the default search first inspects the user.timezone property as well, and if that isn't found it looks up the user.country property in order to get the default value for a country. If neither is set, the default of GMT is used. So the results you observe may well change with your JVM version.
Edit 2: If you've got the source to both (and indeed the sources are both available), then you can simply trace it! Step through the Joda call, to see if it does indeed defer to java.util.TimeZone.getDefault(), and see what the returned value is. Then invoke the JDK method directly and see what you get.
Looking at the JDK sources, it seems that the default timezone is accessed via an inheritable thread local. Thus if someone somewhere calls TimeZone.setDefault(), it may or may not be visible in other threads, depending on whether they've looked up the version already. If you're getting apparently anomalous results from debugging the calls, it could well be simply due to the fact that different threads can have different default TimeZones.

Categories