Joda time zone different than JDK's - java

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.

Related

Java time ignores daylight saving rules

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.

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

Setting the system time of JVM

I have a java app which runs on a linux/unix machine.
When setting the system time via system command, on the console using date I see that the date/time has changed, but it seems as my JVM didn't recognize this change.
It seems that this is not a rights problem, because in the shell I see that the date/time has changed. Only my JVM didn't recognize this change.
Any hints where to search ?
Take a look at this post over on the super user stack exchange. There are 2 different clocks in linux. One is hardware (hwclock) and the other is os/system.

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!

How do I set the timezone in Tomcat for a single web app?

What is the best way to set the time zone in Tomcat for a single web app? I've seen options for changing the command-line parameters or environment variables for Tomcat, but is there a way to set it that is self-contained in the WAR file and not dependent on any Tomcat configuration?
Edit: to reemphasize, I'm looking for a solution that can be contained within a WAR file, not dependent on Tomcat configuration. To put it another way, can one web app be configured to have a different time zone than other apps running in the same Tomcat instance?
The only way I found is to setup a filter and change the timezone in the filter,
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
TimeZone savedZone = TimeZone.getDefault();
TimeZone.setDefault(webappZone);
chain.doFilter(request, response);
TimeZone.setDefault(savedZone);
}
The setDefault() changes the zone for the thread. So everything running in the thread inside the filter will have a different default timezone. We have to change it back because the thread is shared by other apps. You also need to do the same for your init(), destroy() methods and any other thread you might start in your application.
I had to do this because a third-party library assumes default timezone and we don't have source code. It was a mess because this changes log timezone but we don't want log in different times. The correct way to handle this is to use a specific timezone in any time value exposed to end users.
EDIT: I was wrong about this. This edit corrects it.
The answer is that you cannot portably set the (default) timezone for a single webapp. But if you are using Java 6 (at least), the java.util.TimeZone class implements the default timezone methods getDefault(), setDefault() and setDefault(TimeZone) using an inheritable thread local. In other words, calling setDefault() only affects the current thread and future child threads.
The behaviour is not documented in the Sun Javadocs. It works for Java 6 and 5 (see above), but there are no guarantees it will work in older or newer Sun JREs. However, I would be very surprised if Sun decided to change/revert to a 'global' model for the default TimeZone. It would break too many existing applications, and besides globals are BAD.
Set the system variable to CATALINA_OPTS=-Duser.timezone=America/Denver
You can also specify the CATALINA_OPTS in the $TOMCAT_HOME/bin/catalina.sh or %TOMCAT_HOME%\bin\catalina.bat file as well.
Here's a list of acceptable timezones.
Source
In JDK 6, Sun/Oracle has changed the implementation of Timezone. In JDK 5.0 setDefault sets the timezone in a thread local variable always and not across the JVM and that caused a few issues. Sun acknowledged this as a bug and fixed in JDK 1.6.
In JDK 1.6 onwards ( I checked the source code for both JDK 1.6 and JDK 1.7) if the JVM is not started with a security manager ( or it's not set with System.SetsecurityManager()), setDefault method sets it globally across the JVM and not in a thread specific way. If you want to set it only for a given thread then you have to start JVM with a security manager.
When you start Tomcat JVM with a security manager you need to provide individual permissions which was a no starter for us as we were late in the release cycle. Hence in the security policy file we provided all permissions and we overrode the default JAVA security manager to selectively deny the timezone write access. Due to lazy initialization issue with Timezone class I needed to call Timezone.getDefault() in the static block that makes the Timezone class initialized before the securityManager comes into play.
Here is my test program.
--Policy file test.policy
grant {
permission java.security.AllPermission;
};
-- Custom Security Manager
import java.security.Permission;
import java.util.TimeZone;
public class CustomSecurityManager extends SecurityManager {
static {
TimeZone.getDefault().getDisplayName();
}
public CustomSecurityManager() {
super();
}
public void checkPermission(Permission perm) throws SecurityException,NullPointerException
{
String propertyName = perm.getName();
String actionName = perm.getActions();
if(propertyName != null && actionName != null)
{
if(propertyName.equalsIgnoreCase("user.timezone")
&& actionName.equalsIgnoreCase("write"))
{
throw new SecurityException("Timezone write is not permitted.");
}
}
}
}
-- JVM startup Parameters
-Djava.security.manager=CustomSecurityManager -Djava.security.policy=C:/workspace/test/src/test.policy
Check out the SimpleTimeZone. You can create an instance based on a time zone ID and use that to display dates/times using that time zone. If you wanted, you could read that ID from a project specific configuration file.
With Java 7/Tomcat 7, there's a workaround/hack that allows the developer to set a unique timezone per webapp. We had to implement this in our application, as we had to support multiple webapps that run with different default timezones in the same JVM.
Other solutions that I've seen on stack overflow do not completely address the issue.
Using Timezone.setDefault() doesn't work, because it changes the timezone across the JVM.
Using servlet filters is completely thread unsafe, and does not account for child threads
Using SecurityManager approaches also does not account for child thread related timezone issues.
I also investigated the Java source code for TimeZone, I found a way to have a dynamic timezone returned as the default timezone for all callers by injecting a custom implementation of the JavaAWTAccess interface. This can be done by looking at the Thread class loader, and determining the actual webapp context from it, then handling that appropriately based on some webapp name to timezone mapping.
Once again, this is application server specific, and must be done differently for Tomcat, Jetty, Jboss, etc. This approach is also JVM implementation specific (only works on Oracle/Sun), but I believe can be extended to OpenJDK and others.
We have a verified working solution for Oracle JDK 7 SE + Tomcat 7, deployed on both Windows and Linux, hosting multiple webapps in different timezones.
You can also use a VM argument to define it
-Djdk.util.TimeZone.allowSetDefault=true
Updated answer from '21: These days, you should only run one application per JVM, and set the TZ in system properties.
Obsolete answer from '09: The best way is to modify the web application so it accepts explicit time zone configuration through web.xml instead of using the default JVM timezone.

Categories