Java 如何在 Tomcat 中为单个 Web 应用程序设置时区?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1325075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I set the timezone in Tomcat for a single web app?
提问by sk.
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?
在 Tomcat 中为单个 Web 应用程序设置时区的最佳方法是什么?我已经看到了用于更改 Tomcat 的命令行参数或环境变量的选项,但是有没有一种方法可以将其设置为自包含在 WAR 文件中并且不依赖于任何 Tomcat 配置?
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?
编辑:再次强调,我正在寻找一种可以包含在 WAR 文件中的解决方案,而不依赖于 Tomcat 配置。换句话说,是否可以将一个 Web 应用程序配置为与在同一 Tomcat 实例中运行的其他应用程序具有不同的时区?
采纳答案by ZZ Coder
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.
在setDefault()
改变了线程的区域。因此,在过滤器内的线程中运行的所有内容都将具有不同的默认时区。我们必须将其改回来,因为该线程是由其他应用程序共享的。您还需要对您的init()
,destroy()
方法和您可能在应用程序中启动的任何其他线程执行相同的操作。
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.
我必须这样做,因为第三方库采用默认时区,而我们没有源代码。这是一团糟,因为这会更改日志时区,但我们不希望在不同时间登录。处理此问题的正确方法是在向最终用户公开的任何时间值中使用特定时区。
回答by Cuga
Set the system variable to CATALINA_OPTS=-Duser.timezone=America/Denver
将系统变量设置为 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.
您还可以在 $TOMCAT_HOME/bin/catalina.sh 或 %TOMCAT_HOME%\bin\catalina.bat 文件中指定 CATALINA_OPTS。
回答by Stephen C
EDIT: I was wrong about this. This edit corrects it.
编辑:我错了。此编辑更正它。
The answer is that you cannot portablyset 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.
答案是您无法为单个 web 应用程序可移植地设置(默认)时区。但是,如果您使用的是 Java 6(至少),则java.util.TimeZone
该类将实现默认时区方法getDefault()
,setDefault()
并setDefault(TimeZone)
使用可继承的本地线程。换句话说,调用setDefault()
只影响当前线程和未来的子线程。
The behaviour is not documentedin 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.
Sun Javadocs 中没有记录该行为。它适用于 Java 6 和 5(见上文),但不能保证它适用于较旧或较新的 Sun JRE。但是,如果 Sun 决定更改/恢复为默认时区的“全局”模型,我会感到非常惊讶。它会破坏太多现有的应用程序,而且全局变量很糟糕。
回答by Tai Squared
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.
查看SimpleTimeZone。您可以根据时区 ID 创建一个实例,并使用它来显示使用该时区的日期/时间。如果需要,您可以从项目特定的配置文件中读取该 ID。
回答by Mikael Gueck
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.
最好的方法是修改 Web 应用程序,使其通过 web.xml 接受显式时区配置,而不是使用默认的 JVM 时区。
回答by Brijoy
回答by Bala
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.
在 JDK 6 中,Sun/Oracle 更改了 Timezone 的实现。在 JDK 5.0 setDefault 中始终在线程局部变量中设置时区,而不是跨 JVM,这导致了一些问题。Sun 承认这是一个错误并在 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.
在 JDK 1.6 之后(我检查了 JDK 1.6 和 JDK 1.7 的源代码),如果 JVM 没有使用安全管理器启动(或者它没有设置为System.SetsecurityManager()
),setDefault
方法会在整个 JVM 中全局设置它,而不是以线程特定的方式. 如果您只想为给定线程设置它,那么您必须使用安全管理器启动 JVM。
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.
当您使用安全管理器启动 Tomcat JVM 时,您需要提供个人权限,这对我们来说是一个初学者,因为我们处于发布周期的后期。因此,在安全策略文件中,我们提供了所有权限,并覆盖了默认的 JAVA 安全管理器以有选择地拒绝时区写入访问。由于 Timezone 类的延迟初始化问题,我需要调用Timezone.getDefault()
静态块,使 Timezone 类在 securityManager 发挥作用之前进行初始化。
Here is my test program.
这是我的测试程序。
--Policy file test.policy
--策略文件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
-- JVM启动参数
-Djava.security.manager=CustomSecurityManager -Djava.security.policy=C:/workspace/test/src/test.policy
-Djava.security.manager=CustomSecurityManager -Djava.security.policy=C:/workspace/test/src/test.policy
回答by zzhu8192
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.
使用 Java 7/Tomcat 7,有一种解决方法/hack 方法允许开发人员为每个 web 应用程序设置唯一的时区。我们必须在我们的应用程序中实现这一点,因为我们必须支持在同一 JVM 中以不同默认时区运行的多个 web 应用程序。
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.
- 使用 Timezone.setDefault() 不起作用,因为它更改了整个 JVM 的时区。
- 使用 servlet 过滤器是完全线程不安全的,并且不考虑子线程
- 使用 SecurityManager 方法也没有考虑与子线程相关的时区问题。
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.
我还研究了 TimeZone 的 Java 源代码,我找到了一种方法,通过注入 JavaAWTAccess 接口的自定义实现,将动态时区作为所有调用者的默认时区返回。这可以通过查看 Thread 类加载器,并从中确定实际的 webapp 上下文,然后根据一些 webapp 名称到时区映射适当地处理来完成。
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.
同样,这是特定于应用程序服务器的,对于 Tomcat、Jetty、Jboss 等必须以不同的方式完成。这种方法也是特定于 JVM 实现的(仅适用于 Oracle/Sun),但我相信可以扩展到 OpenJDK 和其他。
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.
我们有一个经过验证的适用于 Oracle JDK 7 SE + Tomcat 7 的解决方案,部署在 Windows 和 Linux 上,在不同时区托管多个 web 应用程序。
回答by Icegras
You can also use a VM argument to define it
您还可以使用 VM 参数来定义它
-Djdk.util.TimeZone.allowSetDefault=true