请直接过来选择您的时区

发布者    |       讨论 JSF

在2.0版本之前,JavaServer Faces规范指出,所有日期和时间都应该被视为UTC,并以UTC渲染,除非在timeZone属性中显式指定<f:convertDateTime>转换标签。这是一个非常不方便的默认行为。这个面向2.1版本发布的开放提案,将区域设置扩展到可以容纳默认时区首选项,在渲染日期时默认使用该首选项。

注意:日期始终假定使用UTC定义,因此此提案仅解决显示偏移量。

将系统时区设置为默认值

JavaServer Faces 2.0(建议)规范提供了一种覆盖JavaServer Faces应用程序的标准时区设置的方法,使其使用应用程序服务器运行的时区(这恰好是Java SE的默认行为,根据java.util.TimeZone)而不是UTC。此覆盖通过以下web.xml上下文参数声明激活,在11.1.3节中进行了记录

<context-param>
    <param-name>javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE</param-name>
    <param-value>true</param-value>
</context-param>

在深入研究使用系统时区的影响之前,我想首先建议更改此参数的名称或删除它,以便在faces-config.xml中定义的类似设置中。假设上下文参数仍然是将系统时区设置为默认值的首选方法,由于名称过长,除JSF专家外没有人能够记住此参数。我建议使用一个更简单、更通用的名称

javax.faces.USE_SYSTEM_TIMEZONE

除了名称更短外,它还更加通用,为JSF的其他领域利用此设置打开了大门。(在下面提出的faces-config.xml中定义的替代配置)。

除了命名外,这个设置很重要,因为它允许开发者恢复JVM的默认时区行为。然而,它并没有解决JSF试图解决的问题,即应用程序使用的时区不应受部署地理位置的影响。理想情况下,默认时区应反映用户的时区偏好(即,它应该是用户时区)。

在JSF中设置默认时区

时区在数据驱动的Web应用程序中非常重要。这就是为什么JSF运行时应该让开发者轻松地获取正确的时区设置。UTC和系统默认时区选择都是不充分的。UTC默认设置很烦人,因为它迫使每个开发者第一天就要处理时区问题(没有好默认设置)。系统默认设置很危险,因为它依赖于应用程序部署的地理位置。

让我们考虑一个系统默认设置不充分的场景。

如果我正在将我的应用程序部署到加利福尼亚的服务器上,并为纽约的客户提供服务,则默认时区不是客户期望的。现在,我可以以纽约的时区启动我的应用程序服务器实例。但如果有另一个在该服务器上服务加利福尼亚客户的独立应用程序呢?那么我就会有一个严重的冲突。

正确的解决方案是允许为JSF应用程序设置时区,并在faces-config.xml中配置。实际上,已经有了一个定义此配置的理想位置:在<locale-config>元素内,补充默认区域设置。开发者将在新的<default-time-zone-id>元素中指定时区ID,JSF将从这个ID解析TimeZone对象。以下是一个将默认时区设置为纽约的示例。

<application>
    <locale-config>
        <default-time-zone-id>America/New_York</default-time-zone-id>
    </locale-config>
</application>

然而,静态值并不总是足够,因为应用程序的用户可能位于不同的时区。因此,虽然允许使用静态值,但此元素还应接受一个ValueExpression,在需要时区值时解析(例如,在编码期间)。允许使用ValueExpression的允许使用允许默认时区是动态的,但仍然是默认的,因为它可以由<f:convertDateTime>组件标签覆盖。

<application>
    <locale-config>
        <default-time-zone-id>#{userPreferences.timeZoneId}</default-time-zone-id>
    </locale-config>
</application>

此配置设置提供了三个重要功能

  1. 提供良好的应用程序默认设置(可以使用ValueExpression根据用户的偏好进行自定义)
  2. 跨部署位置保持稳定(不会因为应用程序部署到不同的时区而改变,就像系统默认设置那样)
  3. 每个在同一服务器上运行的应用程序都是隔离的

有了这个功能,JSF将执行以下时区解析算法。

如果在配置资源(即,faces-config.xml)中指定了默认时区ID,则从指定的ID解析的TimeZone用作JSF应用程序的默认时区。如果没有指定,JSF将使用系统默认时区,如果javax.faces.USE_SYSTEM_TIMEZONE上下文参数值设置为true,否则使用UTC。

但最好不要依赖于上下文参数来设置时区为系统默认值,而是引入<system/>元素作为<default-time-zone-id>的可能的值,以将时区设置包含在JSF配置中。(web.xml配置中的上下文参数应仅用于定义特定实现的设置)。

<default-time-zone-id>中添加<locale-config>需要向javax.faces.application.Application添加以下API扩展。

/**
  * <p>Return the default <code>TimeZone</code> for this application. This
  * method resolves the Value Expression returned by {@link Application#getDefaultTimeZoneId}
  * and uses that value to look up a <code>TimeZone</code> instance. If the
  * resolved time zone id is null, the <code>TimeZone</code> for UTC is returned.</p>
  */
public abstract TimeZone getDefaultTimeZone();

/**
 * <p>Set the default time zone for this application as a value expression that
 * is expected to resolve to a time zone id.</p>
 *
 * @param timeZoneId The new default time zone
 *
 * @throws NullPointerException if <code>timeZone</code>
 *  is <code>null</code>
 */
public abstract void setDefaultTimeZoneId(ValueExpression timeZoneId);

/**
 * <p>Return the default time zone for this application as a value expression
 * that is expected to resolve to a time zone id.<p>
 */
public abstract ValueExpression getDefaultTimeZoneId();

关于自动检测用户的时区怎么办?

主要挑战在于,浏览器不会像发送首选语言头一样发送首选时区头。而且,由于VPN和其他网络配置可能会将源地址移动到不同的时区,所以不能依赖地理IP地址解析。目前,应用程序有责任收集用户的偏好并将其存储在数据库或浏览器cookie中。在<default-time-zone-id>元素中定义的ValueExpression预期将返回此存储值。

请提供您的反馈

让我知道您对这个解决方案的看法。它看起来足够吗?您认为配置足够简单吗?我遗漏了什么?(请参阅Seam Wiki上的JSF 2.1页面,了解此提议的最新修订版)。

我鼓励您现在就开始参与这些问题以及其他问题,以便JSF可以通过社区参与和反馈继续改进。JSF 2是JSF演变中的重大一步,但绝对不是最后一步。


回到顶部