我有 3个地区(蒙古,土库曼等)的网络应用程序。它们部署在 tomcat的虚拟主机上。现在我需要为每个应用程序设置 timezone 。我怎样才能做到这一点? 我为每个应用设置了 ServerContextListener 接口,以设置 TimeZone :
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
TimeZone timeZone=TimeZone.getTimeZone("Asia/Ulan_Bator");
// TimeZone timeZone=TimeZone.getTimeZone("Asia/Ashgabat");
timeZone.setDefault(timeZone);
}
但在部署后,每个应用都有相同的TimeZone。
P.S:对不起我的英文:)
答案 0 :(得分:2)
ZoneId
,而不是TimeZone
可怕的传统日期时间类在几年前被现代的 java.time 类所取代,并采用了JSR310。
将 TimeZone
替换为ZoneId
和ZoneOffset
。
ZoneId z = ZoneId.of( "Asia/Ulan_Bator" ) ;
避免依赖JVM当前的默认时区。
JVM仅 一个默认时区。调用TimeZone.setDefault
将立即影响该JVM中所有应用程序的所有线程中的所有代码。
但是在部署后,每个应用程序都具有相同的TimeZone。
因此,您不能为每个Web应用程序设置其他默认时区。如果所有Web应用程序都在同一个Servlet容器中运行,则所有Web应用程序共享相同的默认时区。
一种更好的方法是将所需的/期望的时区作为可选参数显式传递给各种 java.time 方法。例如,如果要捕获在特定时区看到的当前时刻,请将所需的ZoneId
对象传递给now
方法。
ZoneId z = ZoneId.of( "Asia/Ashgabat" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
为每个Web应用程序设置时区,而不是依赖JVM当前的默认时区。
ServletContextListener::contextInitialized
您已接近解决方案。启动每个Web应用程序时,您需要指定其默认值。这样做的地方是您的类中实现contextInitialized
的类中的ServletContextListener
方法,如您的问题所示。
在该方法内,指定所需的时区。
ZoneId z = ZoneId.of( "Asia/Ashgabat" ) ;
但是该变量超出范围,并在contextInitialized
方法的结尾消失。因此,我们需要在某个地方存储对该ZoneId
对象的引用。但是哪里? Servlet规范仅为应用程序中跨代码所需的“全局”变量或常量定义了一个位置。
您的contextInitialized
方法被传递了ServletContextEvent
。该对象包含对ServletContext
对象的引用,该对象代表正在启动的Web应用程序的当前实例。
ServletContext myWebAppContext = servletContextEvent.getContext() ;
ServletContext
可以方便地维护“属性”的简单键值存储。密钥是String
,值是Object
。我们可以为每个Web应用程序的默认时区组成一个字符串名称,并将ZoneId
传递为Object
值。将它们传递给ServletContext::setAttribute
。
String key = "com.example.acme_webapp.zoneid" ;
Object value = ZoneId.of( "Asia/Ashgabat" ) ;
myWebAppContext.setAttribute( key , value ) ;
在应用程序的代码中,look up the context并在需要时进行属性分配。向正在处理的Servlet请求询问其上下文。
ServletContext context = request.getServletContext() ;
询问存储的属性中的值。
Object value = context.getAttribute( "com.example.acme_webapp.zoneid.mongolia" ) ;
将Object
放回ZoneId
。我们知道这应该可行,但是对于防御性编程,您可能需要检查instanceof
。
ZoneId z = ( ZoneId ) value ;
然后继续您的工作。就像我们上面讨论的那样,也许您想捕获在该区域中看到的时间。
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
提示:您可能需要定义一个枚举来处理属性名称的多个区域字符串。复制粘贴字符串值是一项风险业务。参见tutorial。
请注意,尽管每个Web应用程序实例都有其自己的ZoneId
对象,但是该对象正在线程之间共享。根据定义,Servlet环境是高度线程化的,每个请求都在线程上进行服务。这样ZoneId
就可以在任意数量的线程之间共享。但这没关系。幸运的是, java.time 类是使用thread-safe模式设计的immutable objects类。
强制性的Servlet线程并发技巧:任何进行Servlet编码的人都应该阅读并重新阅读本书:Java Concurrency in Practice,作者:布莱恩·格茨,蒂姆·皮尔斯,约书亚·布洛赫,约瑟夫·鲍比尔,戴维·霍尔姆斯,道格·里阿。 >
答案 1 :(得分:0)
您可以为tomcat设置JVM时区。为此,您需要在Tomcat选项的tomcat配置文件中添加以下选项。
-Duser.timezone=UTC
使用您的时区代替UTC。它将在该时区启动您的JVM。