如何在 Vaadin Flow 中检索客户端(浏览器)时区?

How to retrieve the client (browser) timezone in Vaadin Flow?

我需要确定浏览器时区。我尝试遵循 this post 但不起作用 (Vaadin 20)。这是我的代码:

    ZoneId myZoneId;
    ...

    UI.getCurrent().getPage().retrieveExtendedClientDetails(extendedClientDetails -> {
      myZoneId = ZoneId.of(extendedClientDetails.getTimeZoneId());
    });

    // here myZoneId has value null.

所以我尝试自己做,最初只是简单地展示它。

    UI.getCurrent().getPage()
            .executeJs("return Intl.DateTimeFormat().resolvedOptions().timeZone;")
            .then(value -> Notification.show(value.asString()));

它有效,我读到“Europe/Rome”,但它的值似乎无法映射到 Java 中的 ZoneId。

我可以进一步探索 Javascript zone 对象,但我也无法找到我的代码实际去哪里用 chrome 调试器调试它(Vaadin 文档中没有提及代码在哪里)。

我可以处理返回值并尝试解释它,但我想避免重新发明轮子。

有人有可用的代码吗?

Europe/Rome 适用于 Java.

您只需致电:

ZoneId.of("Europe/Rome");

retrieveExtendedClientDetails 是异步调用,因此结果在 inside 回调中可用,而不是在 after 触发回调时可用(在你评论的地方)。

此外,UI.getCurrent() 可以 return null 如果调用得太快,f.e。在构造函数中。

retrieveExtendedClientDetails 第一次使客户端往返 运行 (docs).

If already obtained, the callback is called directly. Otherwise, a client-side roundtrip will be carried out.

这意味着您可以在初始化 UI 时尝试 运行 它,稍后您应该在触发后立即准备好它。

@SpringComponent
public class MyVaadinServiceInitListener implements VaadinServiceInitListener {
    @Override
    public void serviceInit(ServiceInitEvent event) {
        event.getSource().addUIInitListener(uiEvent -> {   
            var ui = event.getUI();
            ui.getPage().retrieveExtendedClientDetails(detail -> {});
        });
    }    
}

另一个解决方案是读取回调中的区域。

试试这个

//Read the timezone and store in into the session class
if(UI.getCurrent() != null) {      
       UI.getCurrent().getPage().retrieveExtendedClientDetails(details -> {
                           
        // Set the time zone
        TimeZone uiTimeZone = TimeZone.getTimeZone(details.getTimeZoneId());
                            
      });
 };

谢谢大家!

最后,主要问题不是从客户端检索时区,而是将其与 ZoneId.getAvailableZoneIds() 返回的可用时区列表进行匹配。事实上,CEST(中欧)和任何其他国家都有多个时区。只有5个时区的巴西我算了7个!

老实说,我对时区一无所知(如果我们可以只使用 GMT 并相应地调整我们的日程安排,世界会变得非常简单)。解决方案是使用名称消化时区列表。我不知道这是对还是错,但它很简单而且有效,尽管如果我选择 CEST,我不知道它会是罗马、柏林还是其他任何地方。重要吗?

如果有一天有人需要

,这里是完整的代码
package net.cbsolution.scc.vaadin.comps;

import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.ItemLabelGenerator;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.data.renderer.TemplateRenderer;

import java.time.ZoneId;
import java.time.format.TextStyle;
import java.util.*;
import java.util.stream.Collectors;

public class TimeZoneSelector extends ComboBox<ZoneId> {
  private Locale locale = UI.getCurrent().getLocale();

  public TimeZoneSelector() {

    // Digest the timezone list
    final Map<String, ZoneId> cleanMap = new HashMap<>();
    ZoneId.getAvailableZoneIds().stream().map(z -> ZoneId.of(z)).forEach(z -> cleanMap.put(print(z), z));

    cleanMap.values().stream().collect(Collectors.toList());
    setWidth("20em");
    setDataProvider(new ListDataProvider<>(cleanMap.values().stream().collect(Collectors.toList())));
    setRenderer(TemplateRenderer.<ZoneId>of("<span>[[item.print]]</span>")
        .withProperty("print", tz -> print(tz))
    );
    setItemLabelGenerator((ItemLabelGenerator<ZoneId>) item -> print(item));
  }

  protected String print(ZoneId zoneId) {
    return zoneId.getDisplayName(TextStyle.FULL, UI.getCurrent().getLocale());
  }

  @Override
  protected void onAttach(AttachEvent attachEvent) {
    UI.getCurrent().getPage().retrieveExtendedClientDetails(details -> {
      TimeZone uiTimeZone = TimeZone.getTimeZone(details.getTimeZoneId());
      setValue(ZoneId.of(uiTimeZone.getID()));
    });
  }

}