Step right up and select your time zone

Posted by    |      

Prior to revision 2.0, the JavaServer Faces specification states that all dates and times should be treated as UTC, and rendered as UTC, unless a time zone is explicitly specified in the timeZone attribute of the <f:convertDateTime> converter tag. This is an extremely inconvenient default behavior. This open proposal, targeting the 2.1 release, extends the Locale configuration to accommodate a default time zone preference that is used by default when a date is rendered.

Note: Dates are always assumed to be defined using UTC, so this proposal merely addresses the display offset.

Making the system time zone the default

The JavaServer Faces 2.0 (proposed) specification offers a way to override the standard time zone setting of the JavaServer Faces application so that it uses the time zone where the application server is running (which happens to be the default behavior for Java SE according to java.util.TimeZone) rather than UTC. This override is activated using the following web.xml context parameter declaration, documented in section 11.1.3:

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

Before diving into the implications of using the system time zone, I want to first suggest either renaming this parameter or removing it in favor of a comparable setting defined in faces-config.xml. Assuming the context parameter remains the preferred way of making the system time zone the default, there's no way anyone except JSF experts will be able to commit this parameter to memory due to its unnecessarily long name. I would like to put forth an alternate, simpler name:

javax.faces.USE_SYSTEM_TIMEZONE

In addition to name being shorter, it's much less specific, opening the door for other areas of JSF to take advantage of this setting. (An alternative configuration defined in faces-config.xml is proposed further down).

Naming aside, this setting is important because it allows the developer to restore the default time zone behavior of the JVM. However, it does not address the real problem that JSF was attempting to solve, which is that the time zone used by the application should not be affected by the geographic location of the deployment. Optimally, the default time zone should reflect the time zone preference of the user (i.e., it should be a user time zone).

Setting the default time zone in JSF

Time zones are extremely important in data-driven web applications. That's why the JSF runtime should make it easy for the developer to get the time zone setting correct. Both the UTC and the system default time zone choices are inadequate. The UTC default is annoying because it forces every developer to deal with the time zone issue on day one (no good default). The system default is risky because it's dependent on the geographic location where the application is deployed.

Let's consider a scenario where the system default is inadequate.

If I am deploying my application to a server in California and servicing New York customers, the default time zone is not what the customers expect. Now, I could start my application server instance with the time zone of New York. But what if I have a separate application on that server that services customers in California? Then I have a major conflict.

The correct solution is to allow the time zone to be set per JSF application, configured in faces-config.xml. In fact, there is already an ideal place to define this configuration: within the <locale-config> element, complementing the default locale. The developer would specify a time zone ID in the new <default-time-zone-id> element and JSF would resolve a TimeZone object from this ID. Here's an example showing the default time zone set to New York.

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

However, a static value is not always sufficient since the users of an application may reside in different time zones. Therefore, while a static value is permitted, this element should also accept a ValueExpression that is resolved when the time zone value is needed (i.e., such as during encoding). The permitted use of a ValueExpression allows the default time zone to be dynamic, but still a default since it can be overridden by the <f:convertDateTime> component tag.

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

This configuration setting provides three important features:

  1. Provides a good application default (which may be customized to the user's preference using a ValueExpression)
  2. Remains stable across deployment locations (doesn't change just because the application is deployed to a different time zone, as is the case with the system default)
  3. Isolated per application running on the same server

With this feature available, JSF will perform the following time zone resolution algorithm.

If a default time zone ID is specified in a configuration resources (i.e., faces-config.xml), then the TimeZone resolved from the specified ID is used as the default time zone in the JSF application. If one is not specified, JSF will use the system default time zone if the javax.faces.USE_SYSTEM_TIMEZONE context parameter value is true or UTC otherwise.

But instead of relying on a context parameter to set the time zone to the system default, it may be better to introduce the <system/> element as a possible value for <default-time-zone-id> to keep the time zone setting contained within the JSF configuration. (Context parameters in the web.xml configuration should only be used for defining implementation-specific settings).

The addition of the <default-time-zone-id> child element in <locale-config> requires the following API additions to javax.faces.application.Application.

/**
  * <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();

What about auto-detecting the user's time zone?

The main challenge is that browsers don't send a preferred timezone header like they do a preferred language header. And you can't rely on a geo-IP address resolution since VPNs and other network configurations can move the source address across timezones. For now, it's the application's responsibly to collect the user's preference and store it in either the database or a browser cookie. The ValueExpression defined in the <default-time-zone-id> element is expected to return this stored value.

Provide your feedback

Let me know what you think about this solution. Does it seem adequate? Do you think the configuration is simple enough? Am I missing something? (Please see the JSF 2.1 page on the Seam Wiki for the latest revision of this proposal).

I encourage you to start participating now in these and other issues so that JSF can continue to improve as a result of community participation and feedback. JSF 2 is a big step in the evolution of JSF, but it is by no means the last.


Back to top