在 JSF 中 - 获取客户端的区域设置(以浏览器时区显示时间)

发布于 2024-12-22 08:35:16 字数 780 浏览 5 评论 0原文

我正在 JSF 2.0 中编写一个 Web 应用程序,它将时间戳作为信息的一部分显示给用户。 我希望用户看到本地化到其位置(浏览器的区域设置)的时间戳。

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

我尝试使用这些方法获取区域设置:

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

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

两者都返回服务器的区域设置。

然后,我使用带有 SimpleDateFormat 的区域设置对象来打印时间戳。

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

谢谢你的提前。

更新发现 (jsTimezoneDetect) JavaScript代码。但我仍然不确定如何将时区转换为 Java Locale 对象

I'm writing a webapp in JSF 2.0 that displays Timestamps as part of the information to the user.
I would like the user to see the timestamps localized to their location (browser's locale).

So far, whatever I did, the timestamps would always appear localized to the timezone of the server.

I tried getting the locale with these methods:

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

or

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

Both returned the server's locale.

I then use the locale object with SimpleDateFormat to print timestamps.

Am I using the correct API?
I've read somewhere that you have to use client side code (Javascript) to get the browser's timezone. Is that correct? How would you do that?

Thank's 'n' Advance.

UPDATE found this (jsTimezoneDetect) JavaScript code. But I'm still not sure how to translate the timezone to a Java Locale Object

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

┊风居住的梦幻卍 2024-12-29 08:35:16

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

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

  1. 将时区信息存储在用户配置文件中(如果有)。这是最简单的方法,允许您使用内置 标记。

  2. 从网络浏览器获取时区信息。您可以通过 Ajax 请求获取它,就像 Ben 的示例一样。从技术上讲,您还可以在此处使用 标记。

  3. 您可以以某种常见的、与区域设置无关(或者如果您愿意的话,不变)格式发送 UTC 时间戳,在客户端解析它以使用 全球化

接下来是一些例子,但让我先解释一下。

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 浏览器的区域设置时(这似乎有点不太可能),它才能为您提供服务器的默认值。

顺便提一句。 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>

现在让我解释一下属性:

  • type - 显示什么,都表示日期和时间
  • dateStyle - 日期样式(out短、​​中、长、完整、默认)。您确实应该在这里使用默认值,因为这将为每个区域设置 timeStyle 使用最合适的格式
  • - 类似于日期样式,但对于时间部分
  • timeZone - 采用 UTC 偏移量(因此您不需要转换任何内容)或时区名称(即美国/洛杉矶)。

默认情况下,该标记将使用当前视图区域设置,因此您不必担心这部分,特别是如果您正确设置了区域设置支持。

将它与 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”。
如果您想使用该方法并且需要更具体的示例,请告诉我。

You probably should use built-in formatting tag instead of SimpleDateFormat. Your question implies that you want to show date and time to International user, and in this case you should really use user's local format (they tend to differ, you know).

In case of time zone, it has nothing to do with Internationalization and Localization, i.e. there are several different time zones in USA. There are two approaches you can use here:

  1. Store time zone information in the user profile (if you have one). This is the easiest way and allow you to use built-in <f:convertDateTime> tag.

  2. Get time zone information from web browser. You can get it via Ajax request just like in Ben's example. Technically you can also use <f:convertDateTime> tag here.

  3. You can send the timestamps in UTC in some common, locale-independent (or invariant if you prefer) format, parse it on the client side to create JavaScript's date object and format for locale with Globalize.

Some examples will follow but let me explain something first.

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

This will give you web browser's locale (but not time zone, since this is not locale related). It will actually read the contents of HTTP Accept-Language header and choose the best possible locale. If it is not working for you, please make sure that you have correctly set supported locales in your faces-config.xml. By best possible Locale, I understand that it will try to use user's most preferred Locale (if that's supported by your application), then second best and so on. If none of the Locales is supported, it will fall-back to your application's default Locales (again, faces-config.xml has to have this setting) or to server's default Locale if this setting is missing (or at least I think so, it kind of makes sense).

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

This one will give you the top Locale from Accept-Language header. Please check your web browser's settings - there is almost no way for it to give you the server Locale, unless they are exactly the same as your web browser's. It can give you server's defaults if and only if, none of the web browser's Locale is supported by JVM (which seems a bit unlikely).

BTW. FacesContext.getCurrentInstance().getExternalContext().getRequestLocales() will give you the Iterator so you can manually iterate through the list of Locales in Accept-Language header. It is just to let you know, you probably should not use it (UIViewRoot is really enough).

Now, suppose you have some bean with the user profile and the method which will give you the time zone. This method is better than Ajax call, in the sense that it might happen that two different time zones have the same UTC offset but switch Daylight Saving Time on different date (in other words some timestamps would be printed incorrectly). Anyway, in case like this, you can format your time-stamp like this (date also come from some bean):

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

Now let me explain the attributes:

  • type - what to show, both means date and time
  • dateStyle - style of date (out of short, medium, long, full, default). You should really use default here as this will use the most proper format for each Locale
  • timeStyle - similar to date style but for time part
  • timeZone - takes either an UTC offset (so you don't need to convert anything) or time zone name (i.e. America/Los_Angeles).

The tag will use current view Locale by default, so you do not have to worry about this part, especially if you set up Locale support correctly.

Combining it with Ajax (see Ben's answer) would be easy enough (I think).

I also mentioned that you can write out invariant dates, parse them on the client-side and then format them with Globalize. Assuming that you have parsed date already (it depends on the representation you want to use, so I will skip this part), it could be done like that:

// 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

Unlike Java, Globalize have only short ("f") and long ("F") date and time formats. So I decided on using short one.
Please also keep in mind, that Globalize cultures are separated via hyphen, not underscore, so you need "fr-CA", not "fr_CA" for example.
Please let me know if you want to use that method and need more concrete example.

驱逐舰岛风号 2024-12-29 08:35:16

成功了。
以下是我所做的:

向 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);

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

Succeeded.
Here is what I did:

Added to JSF a hidden input field so I can send JavaScript values to the server:

<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>

Using the plugin above, I ran JavaScript code that retrieved the offset of the browser.

$(document).ready(function() {
    var timezone = jstz.determine_timezone();

    $("#timezone_holder").val(timezone.offset());
    $("#timezone_holder").change();
});

When the timezone input is changed (initiated from the javascript code above) I run this code in the 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);

Then, whenever I need to format a date I use the Date Formatter that was created above.

嘿看小鸭子会跑 2024-12-29 08:35:16

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

您的 Javascript 可以使用 jQuery 和 jsTimezoneDetect< /a>

    $(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>

Another option would be to create a cookie on a Javascript that executes when the home page is ready. After that, the cookie will exist on each subsequent request and would be available

Your Javascript could use jQuery and 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;
    }

Your Facelet would then use the cookie's value if it exists:

<h:outputText value="#{bean.time}">
   <f:convertDateTime
       dateStyle="full"
       timeStyle="full"
       type="both"
       timeZone="#{cookie.timezoneCookie.value}">
   </f:convertDateTime>
</h:outputText>
赢得她心 2024-12-29 08:35:16

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

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

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

返回 Locale 对象的枚举,以降序表示
从首选语言环境开始的顺序,语言环境是
根据 Accept-Language 标头,客户端可以接受。如果
客户端请求不提供 Accept-Language 标头,此方法
返回一个包含一个区域设置的枚举,这是默认区域设置
服务器。

You may want to try jsTimezoneDetect to detect timezone on the client side and send to the server.

UPDATE: to get a user's Locale, you can try the following:

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

Returns an Enumeration of Locale objects indicating, in decreasing
order starting with the preferred locale, the locales that are
acceptable to the client based on the Accept-Language header. If the
client request doesn't provide an Accept-Language header, this method
returns an Enumeration containing one Locale, the default locale for
the server.

无妨# 2024-12-29 08:35:16

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

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

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

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

If all you need is to display time stamps with the users local time you dont need the Locale object (you need to add the users hours offset to GMT + 0 time), Y
You need to send the value of timezone.offset() (from the example in the link) to the server (you can do it by using servlet post with parameter)

and then add this offset to the date object created on your server (set your locale on the server to be GMT + 00)

that way you will create time stamps with the correct time of the user that is logged into your web app

(that's what I did myself...)

满栀 2024-12-29 08:35:16

您还可以直接在 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}" />

You can also use it like this direct in JSF code:

<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>

Then you can reuse the timezonename in a JSF converter:

<f:convertDateTime pattern="dd.MM.yyyy HH:mm" timeZone="#{bean.timezone}" />
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文