null

Клиентская таймзона в JSF

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

Рассмотрим решение одной из таких проблем в JSF.

Как известно, JVM использует таймзону системы и конвертирует всю дату с использованием этой таймзоны, однако, компонент <f:convertDateTime/> по умолчанию использует таймзону GMT+00, что зачастую приводит к несовпадению даты в самом приложении и в представлении, например: если в приложении установлена таймзона GMT+03, то дата 12.01.2018 00:00:00 при отображении превратится в 11.01.2018 21:00:00. Чтобы исключить такое поведение, необходимо в файл web.xml добавить следующий параметр:

<context-param>
    <param-name>javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE</param-name>
    <param-value>true</param-value>
</context-param>

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

Для этого решения этой задачи можно выполнить следующие действия:

  1. На стороне клиента, с помощью Javascript сохраняем в Cookie значение клиентской таймзоны
     (function setTimeZone() {
                var _myDate = new Date();
                var _offset = _myDate.getTimezoneOffset();
                document.cookie = "TIMEZONE_COOKIE=" + _offset;
            })();
  2. Создаём javax фильтр, в котором будем получать значение данной куки и сохранять его в Session Scope бине.
    public class TimezoneFilter implements Filter {
        @Autowired
        private ApplicationContext appContext;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {}
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            Cookie[] cookies = httpServletRequest.getCookies();
            if (cookies != null && cookies.length > 0) {
                Optional<Cookie> cookieOpt = Arrays.stream(cookies)
                        .filter(c -> c.getName().equals("TIMEZONE_COOKIE"))
                        .findFirst();
                if (cookieOpt.isPresent()) {
                    ClientInfoBean clientInfoBean = appContext.getBean(ClientInfoBean.class);
                    String timezone = "GMT";
                    int offset = -1 * Integer.parseInt(cookieOpt.get().getValue()) / 60;
                    if (offset >= 0) {
                        timezone = timezone + "+" + String.valueOf(offset);
                    } else {
                        timezone = timezone + String.valueOf(offset);
                    }
                    clientInfoBean.setTimezone(timezone);
                }
            }
            chain.doFilter(request, response);
        }
    
        @Override
        public void destroy() {}
    }
    
  3. Устанавлием значение таймзоны в компоненте  <f:convertDateTime/>
    <h:outputText value="#{someBean.someDate}">
           <f:convertDateTime pattern="dd.MM.yyyy HH:mm:ss" timeZone="#{clientInfoBean.timezone}"/>
    </h:outputText>

Таким образом, данные, чувствительные к таймзоне, будут корректо отображаться у клиентов из любого региона