Как получить часовой пояс клиента (браузера) в потоке Vaadin?

#timezone #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()));
 

Это работает, и я читаю «Европа/Рим», но его значение не похоже на то, что я могу сопоставить с ZoneId на Java.

Я мог бы немного подробнее изучить объект зоны Javascript, но я также не смог найти, куда на самом деле пошел мой код, чтобы отладить его с помощью отладчика chrome (в документе Vaadin нет упоминания о том, куда идет код).

Я мог бы поработать над возвращаемым значением и попытаться его интерпретировать, но я бы не хотел изобретать велосипед заново.

У кого-нибудь есть какой-нибудь работающий код?

Ответ №1:

Европа/Рим прекрасно подходят для Явы.

Вы можете просто позвонить:

 ZoneId.of("Europe/Rome");
 

Комментарии:

1. Спасибо, Саймон, я думаю, что мой вопрос был частично неполным. Позвольте мне объяснить необходимость: я показываю пользователю поле со списком, в котором она выберет часовой пояс (и это работает). Я получаю возможные часовые пояса, вызывая ZoneId.getAvailableZoneIds(). Затем я бы установил начальное значение по умолчанию для клиента. Бывает (кроме локализации), что Рима там нет (ни на английском, ни на каком другом языке). Это, наверное, Берлин или что-то еще. Но ваше предложение навело меня на мысль: я пройдусь по набору и посмотрю, совпадают ли равные с любыми другими.

Ответ №2:

retrieveExtendedClientDetails является асинхронным вызовом, поэтому результат доступен внутри обратного вызова, а не сразу после запуска обратного вызова (на месте, которое вы прокомментировали).

Кроме того, UI.getCurrent() может вернуться null , если вызовется слишком рано, например, в конструкторе.

retrieveExtendedClientDetails совершает поездку клиента туда и обратно при первом запуске (документы).

Если он уже получен, обратный вызов вызывается напрямую. В противном случае будет осуществлена поездка на стороне клиента туда и обратно.

Это означает, что вы можете попробовать запустить его при инициализации пользовательского интерфейса, а позже он должен быть готов сразу после запуска.

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

Другим решением является считывание зоны внутри обратного вызова.

Комментарии:

1. Нет, если вы посмотрите на мой код, я сохраню переменную и использую ее позже. То, что вы видите, используется, — это переменная, установленная после выполнения обещания, то же самое, что я делаю в своем коде js. Что произойдет (если я отлажу код), так это то, что внутри вызова что-то пойдет не так. Вот почему я решил вызвать js вместо отладки чужого кода.

Ответ №3:

попробуйте это

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

Комментарии:

1. Да, это сработало (верните Европу/Рим, как мой JS), но затем не смог найти совпадение в списке, это была реальная проблема.

Ответ №4:

Спасибо всем!

Наконец, основная проблема заключалась не в получении часового пояса от клиента, а в сопоставлении его со списком доступных часовых поясов, возвращенных клиентом ZoneId.getAvailableZoneIds() . Дело в том, что для CEST (Центральная Европа), как и для любой другой страны, существовало несколько часовых поясов. Я насчитал 7 для Бразилии, в которой всего 5 часовых поясов!

Честно говоря, я немного несведущ в часовых поясах (мир был бы намного проще, если бы мы могли использовать только 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()));
    });
  }

}

 

Комментарии:

1. Если это решит вашу проблему, не могли бы вы также отметить принятый ответ?