#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. Если это решит вашу проблему, не могли бы вы также отметить принятый ответ?