I have web apps for 3 regions(Mongolia,Turkmenia, etc). And they are deployed on tomcat's virtual host. Now I need set timezone for each application. How can I do that? I implemented ServerContextListener interface for each apps to set TimeZone:
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
TimeZone timeZone=TimeZone.getTimeZone("Asia/Ulan_Bator");
// TimeZone timeZone=TimeZone.getTimeZone("Asia/Ashgabat");
timeZone.setDefault(timeZone);
}
But after deploy each apps has same TimeZone.
P.S: Sorry for my English :)
ZoneId
, not TimeZone
The terrible legacy date-time classes were supplanted years ago by the modern java.time classes with the adoption of JSR 310.
TimeZone
in replaced by ZoneId
& ZoneOffset
.
ZoneId z = ZoneId.of( "Asia/Ulan_Bator" ) ;
Avoid relying on the JVM’s current default time zone.
There is only one default time zone for the JVM. Calling TimeZone.setDefault
immediately affects all code in all threads of all apps within that JVM.
But after deploy each apps has same TimeZone.
So you cannot set a different default time zone per web app. If all the web apps are running in the same Servlet container, then all the web apps share the same default time zone.
A better approach is to explicitly pass the desired/expected time zone as the optional argument to various java.time methods. For example, if you want to capture the current moment as seen in a particular time zone, pass the desired ZoneId
object to the now
method.
ZoneId z = ZoneId.of( "Asia/Ashgabat" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Rather than depend on the JVM’s current default time zone, set a time zone for each of your web apps.
ServletContextListener::contextInitialized
You were close to a solution. When each web app launches, you need to specify its default. The place to do that is in the contextInitialized
method in your class implementing the ServletContextListener
as shown in your Question.
Inside that method, specify the desired time zone.
ZoneId z = ZoneId.of( "Asia/Ashgabat" ) ;
But that variable goes out of scope and disappears at the end of the contextInitialized
method. So we need to store a reference to that ZoneId
object somewhere. But where? The Servlet spec defines a place just for such “global” variables or constants you need across code within your app.
Your contextInitialized
method is passed a ServletContextEvent
. That object holds a reference to the ServletContext
object representing the current instance of your web app that is launching.
ServletContext myWebAppContext = servletContextEvent.getContext() ;
That ServletContext
conveniently maintains a simple key-value store of “attributes”. The key is a String
, the value an Object
. We can make up a string name for our per-web-app default time zone, and pass the ZoneId
as the Object
value. Pass these to ServletContext::setAttribute
.
String key = "com.example.acme_webapp.zoneid" ;
Object value = ZoneId.of( "Asia/Ashgabat" ) ;
myWebAppContext.setAttribute( key , value ) ;
In your app's code, look up the context and attribute anytime you need. Ask the Servlet request being serviced for its context.
ServletContext context = request.getServletContext() ;
Ask for the value from the stored attribute.
Object value = context.getAttribute( "com.example.acme_webapp.zoneid.mongolia" ) ;
Cast the Object
back to a ZoneId
. We know that should work, but for defensive programming you may want to check instanceof
.
ZoneId z = ( ZoneId ) value ;
Then proceed with your work. Perhaps you want to capture the time as seen in that zone, just as we discussed above.
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Tip: You might want to define an enum to handle those multiple region strings for the attributes names. Copy-pasting string values is risk business. See tutorial.
Note that while each web app instance has its own ZoneId
object, that object is being shared across threads. A Servlet environment is by definition highly-threaded, each request being serviced on a thread. So that ZoneId
is being shared across any number of threads. But that is okay. Fortunately, the java.time classes are thread-safe by design, using the immutable objects pattern.
Obligatory Servlet threading concurrency tip: Anyone doing Servlet coding should be reading and re-reading this book: Java Concurrency in Practice by Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea.