【问题标题】:In JSF - Getting the Client's Locale (To Display time in browser's timezone)在 JSF 中 - 获取客户端的区域设置(在浏览器的时区中显示时间)
【发布时间】:2012-01-25 21:29:18
【问题描述】:

我正在用 JSF 2.0 编写一个 webapp,它将时间戳作为信息的一部分显示给用户。 我希望用户看到本地化到他们的位置(浏览器的语言环境)的时间戳。

到目前为止,无论我做什么,时间戳都会显示为本地化到服务器的时区。

我尝试使用以下方法获取语言环境:

Locale browserLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();

Locale browserLocale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale();

两者都返回了服务器的语言环境。

然后我使用带有SimpleDateFormat 的语言环境对象来打印时间戳。

我是否使用了正确的 API?
我在某处读到您必须使用客户端代码(Javascript)来获取浏览器的时区。那是对的吗?你会怎么做?

感谢'n' Advance。

更新 找到 this (jsTimezoneDetect) JavaScript 代码。但我仍然不确定如何将时区转换为 Java Locale Object

【问题讨论】:

    标签: javascript jsf localization jsf-2


    【解决方案1】:

    您可能想尝试jsTimezoneDetect 在客户端检测时区并发送到服务器。

    更新:要获取用户的区域设置,您可以尝试以下操作:

    HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
    Locale locale = request.getLocale();
    

    返回一个 Locale 对象的枚举,以递减方式指示 从首选语言环境开始的顺序,即 基于 Accept-Language 标头,客户端可以接受。如果 客户端请求不提供 Accept-Language 标头,此方法 返回一个包含一个语言环境的枚举,默认语言环境 服务器。

    【讨论】:

    • 谢谢,我实际上已经找到了这个脚本(它是更新中的链接)。我不确定如何将其转换为 Java 语言环境对象。
    【解决方案2】:

    如果您只需要显示带有用户本地时间的时间戳,则不需要 Locale 对象(您需要将用户小时偏移量添加到 GMT + 0 时间),Y 您需要将 timezone.offset() 的值(来自链接中的示例)发送到服务器(您可以使用带有参数的 servlet post 来完成)

    然后将此偏移量添加到服务器上创建的日期对象(将服务器上的区域设置为 GMT + 00)

    这样您将使用登录到您的网络应用程序的用户的正确时间创建时间戳

    (这就是我自己做的......)

    【讨论】:

      【解决方案3】:

      成功了。 这是我所做的:

      向 JSF 添加了一个隐藏的输入字段,以便我可以将 JavaScript 值发送到服务器:

      <h:form prependId="false">
          <h:inputText id="timezone_holder" value="#{bean.timezone}" styleClass="hide">
              <f:ajax listener="#{bean.timezoneChangedListener}"></f:ajax>
          </h:inputText>
      </h:form>
      

      使用上面的插件,我运行了JavaScript 代码来检索浏览器的偏移量。

      $(document).ready(function() {
          var timezone = jstz.determine_timezone();
      
          $("#timezone_holder").val(timezone.offset());
          $("#timezone_holder").change();
      });
      

      当更改时区输入时(从上面的 javascript 代码启动),我在 eventListener 中运行此代码:

      String strFromJavaScript = getTimezone();
      HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext()
                      .getRequest();
      Locale browserLocale = request.getLocale();
      TimeZone tz = TimeZone.getTimeZone("GMT" + strFromJavaScript);
      
      // set time zone
      SimpleDateFormat formatter = new SimpleDateFormat("MMM d, yyyy, HH:mm", browserLocale);
      formatter.setTimeZone(tz);
      

      然后,每当我需要格式化日期时,我都会使用上面创建的日期格式化程序。

      【讨论】:

      • 没有必要强制转换为HTTPServletRequest,因为ExternalContext 已经提供了适当的方法(它们做同样的事情)。我在您的回答中看到的另一个问题是 硬编码日期格式 这使它有点不适合 localization 标记。除此之外,干得好:)
      • @PawełDyda 感谢 cmets。正式注明。
      • 你的 bean 会话有作用域吗?如果第一个请求是对具有您要使用客户端时区格式化的日期的页面发出的,会发生什么?直到第二次请求才可用,对吗?
      • @Ghasfarost 一年半后...假设时区在每个会话中确定一次。否则,将生成一个请求范围 bean 来更改它。
      【解决方案4】:

      您可能应该使用内置格式标记而不是 SimpleDateFormat。您的问题意味着您想向国际用户显示日期和时间,在这种情况下,您应该真正使用用户的本地格式(您知道,它们往往会有所不同)。

      就时区而言,它与国际化和本地化无关,即美国有几个不同的时区。您可以在这里使用两种方法:

      1. 在用户配置文件中存储时区信息(如果有的话)。这是最简单的方法,允许您使用内置的&lt;f:convertDateTime&gt; 标签。

      2. 从 Web 浏览器获取时区信息。您可以像 Ben 的示例一样通过 Ajax 请求获取它。从技术上讲,您也可以在此处使用&lt;f:convertDateTime&gt; 标签。

      3. 1234563 /p>

      下面会有一些例子,但让我先解释一下。

      Locale browserLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
      

      为您提供网络浏览器的区域设置(但不是时区,因为这与区域设置相关)。它实际上会读取 HTTP Accept-Language 标头的内容并选择最佳的语言环境。如果它不适合您,请确保您在 faces-config.xml 中正确设置了支持的语言环境。通过最好的语言环境,我知道它将尝试使用用户最喜欢的语言环境(如果您的应用程序支持),然后是第二好的,依此类推。如果不支持任何语言环境,它将回退到您的应用程序的默认语言环境(同样,faces-config.xml 必须具有此设置)或者如果缺少此设置(或者至少我认为是这样,它会返回到服务器的默认语言环境)有道理)。

      Locale browserLocale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale();
      

      这将为您提供来自 Accept-Language 标头的顶级语言环境。请检查您的网络浏览器设置 - 几乎无法为您提供服务器区域设置,除非它们与您的网络浏览器完全相同。当且仅当 JVM 不支持任何 Web 浏览器的 Locale(这似乎不太可能)时,它才能为您提供服务器的默认值。

      顺便说一句。 FacesContext.getCurrentInstance().getExternalContext().getRequestLocales() 将为您提供迭代器,以便您可以手动迭代 Accept-Language 标头中的区域设置列表。只是为了让你知道,你可能不应该使用它(UIViewRoot 真的足够了)。

      现在,假设您有一些 bean,其中包含用户配置文件和为您提供时区的方法。这种方法比 Ajax 调用要好,因为两个不同的时区可能具有相同的 UTC 偏移量,但在不同的日期切换夏令时(换句话说,某些时间戳会打印不正确)。无论如何,在这种情况下,您可以像这样格式化您的时间戳(日期也来自某个 bean):

      <h:outputText value="#{someBean.timestamp}">
        <f:convertDateTime type="both" dateStyle="default" timeStyle="default" timeZone="#{userProfile.timeZone}" />
      </h:outputtext>
      

      现在让我解释一下属性:

      • 类型 - 要显示的内容,均表示日期和时间
      • dateStyle - 日期样式(短、中、长、完整、默认)。您真的应该在这里使用默认值,因为这将为每个区域设置使用最合适的格式
      • timeStyle - 类似于日期样式,但用于时间部分
      • timeZone - 采用 UTC 偏移量(因此您无需转换任何内容)或时区名称(即 America/Los_Angeles)。

      标签默认使用当前视图语言环境,所以你不必担心这部分,特别是如果你正确设置了语言环境支持。

      将它与 Ajax 相结合(请参阅 Ben 的回答)会很容易(我认为)。

      我还提到你可以写出不变的日期,在客户端解析它们,然后用 Globalize 格式化它们。假设您已经解析了日期(这取决于您要使用的表示形式,因此我将跳过这部分),可以这样做:

      // you also have to assign the culture based on UIViewRoot locale and send it out with JavaScript
      Globalize.culture(theLocale);
      var formattedDateTime = Globalize.format(parsedDateTime, "f"); // this will use short date time format
      

      与 Java 不同,Globalize 只有短 ("f") 和长 ("F") 日期和时间格式。所以我决定使用短的。
      还请记住,Globalize 文化是通过连字符而不是下划线分隔的,因此您需要“fr-CA”,而不是“fr_CA”。
      如果您想使用该方法并需要更具体的示例,请告诉我。

      【讨论】:

      • 谢谢!伟大的,彻底的答案。 (顺便说一句 - 请注意我是 Ben,但你的回答绝对值得 V :-))
      • 在第一个示例中,您写道:“如果它不适合您,请确保您在 faces-config.xml 中正确设置了支持的语言环境”。我不确定当时情况如何,但我现在刚刚使用 JSF 2.2 进行了测试,如果您使用 FacesContext 设置语言环境,则不需要 faces-config.xml。 Faces-config.xml 用于让浏览器选择语言环境。
      【解决方案5】:

      你也可以像这样直接在JSF代码中使用:

      <script type="text/javascript">
              $(document).ready(function () {
                  var timezone = jstz.determine_timezone();
                  $("#timezone_holder").val(timezone.name()); //use TimeZone name instead of offset
                  $("#timezone_holder").change();
              });
      </script>
      

      然后您可以在 JSF 转换器中重用时区名称:

      <f:convertDateTime pattern="dd.MM.yyyy HH:mm" timeZone="#{bean.timezone}" />
      

      【讨论】:

        【解决方案6】:

        另一种选择是在 Javascript 上创建一个 cookie,该 cookie 在主页准备好时执行。之后,cookie 将存在于每个后续请求中,并且可用

        您的 Javascript 可以使用 jQuery 和 jsTimezoneDetect

            $(document).ready(function() {  
                setTimezoneCookie();
            });
        
            function setTimezoneCookie() {
        
                var timezone = jstz.determine().name();
        
                if (null == getCookie("timezoneCookie")) {
                document.cookie = "timezoneCookie=" + timezone;
                }
            }
        
            function getCookie(cookieName) {
                var cookieValue = document.cookie;
                var cookieStart = cookieValue.indexOf(" " + cookieName + "=");
                if (cookieStart == -1) {
                    cookieStart = cookieValue.indexOf(cookieName + "=");
                }
                if (cookieStart == -1) {
                    cookieValue = null;
                } else {
                    cookieStart = cookieValue.indexOf("=", cookieStart) + 1;
                    var cookieEnd = cookieValue.indexOf(";", cookieStart);
                    if (cookieEnd == -1) {
                        cookieEnd = cookieValue.length;
                    }
                    cookieValue = unescape(cookieValue.substring(cookieStart, cookieEnd));
                }
                return cookieValue;
            }
        

        如果存在,您的 Facelet 将使用 cookie 的值:

        <h:outputText value="#{bean.time}">
           <f:convertDateTime
               dateStyle="full"
               timeStyle="full"
               type="both"
               timeZone="#{cookie.timezoneCookie.value}">
           </f:convertDateTime>
        </h:outputText>
        

        【讨论】:

        • 很高兴我们不需要任何 java 重新编译
        猜你喜欢
        • 2015-08-04
        • 2011-10-19
        • 2011-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-22
        • 1970-01-01
        相关资源
        最近更新 更多