Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to handle user specific date/timezone conversion with Spring MVC + Freemarker

I want to display a stored date (timezone is UTC) formatted with the user's timezone (may vary per each user and is stored in profile).

The solution I came up with is the following one and I want to know if this is the best way to do it, or if there is another, maybe simpler solution.

Timezone set to UTC with:

-Duser.timezone=UTC

Controller, Freemarker-Template, HandlerInterceptor:

Controller:

@Controller
@RequestMapping("/test")
public class TestController {

        @RequestMapping
        public String dateTest(Model model){
                final Date date = new Date();
                model.addAttribute("formattedDate", new SimpleDateFormat("hh:mm:ss").format(date));
                model.addAttribute("date", date);              
                return "test";
        }
}

Freemarker-Template:

<#setting time_zone="${currentTimeZone}">

UTC Time: ${formattedDate}<br/>
Localized time for timezone <i>${currentTimeZone}</i>: ${date?time}

HandlerInterceptor:

public class TimezoneHandlerInterceptor extends HandlerInterceptorAdapter{

        @Override
        public void postHandle(HttpServletRequest request,
                        HttpServletResponse response, Object handler,
                        ModelAndView modelAndView) throws Exception {

                //Only for testing, in production something like: 
                //final String currentUserTimezone = userService.getCurrentUser().getTimezoneId();
                final String currentUserTimezone = "Europe/Berlin";

                modelAndView.addObject("currentTimeZone", currentUserTimezone);            
        }
}

Output:

UTC Time: 08:03:53
Localized time for timezone Europe/Berlin: 09:03:53

So is there a more standard or even out of the box way to achieve the same result? Thanks for your help.

like image 649
Patrick Meier Avatar asked Feb 26 '14 08:02

Patrick Meier


3 Answers

Since you print the same date twice, only rendered differently (different time zone), it's probably just a presentation (MVC View) issue, and as such it should not be solved in the Model. Instead, you could do something like this is the template:

<#import "/lib/utils.ftl" as u>
...
UTC Time: ${u.utcTime(date)}<br/>
Localized time for timezone <i>${currentTimeZone}</i>: ${date?time}

utcTime should be defined inside utils.ftl like <#assign u = "com.something.freemarker.UtcTimeMethod"?new()>, where com.something.freemarker.UtcTimeMethod is a TemplateMethodModelEx implementation. There are other ways of doing this, like u is maybe a shared variable defined in the FreeMarker configuration, etc. The point is, that you need to print the UTC time doesn't affect the model.

As of the <#setting time_zone=currentTimeZone> part (note that there's no need for ${...} there), surely it depends on the web application framework, but the nice solution would be if you set the time zone (like based on the visitor's locale) before the template is called. FreeMarker supports that, via Template.createProcessingEnvironment (see JavaDoc), but maybe Spring MVC doesn't.

Also, a Date object always store the date-time in UTC. You don't need to set the time zone for that with -D.

like image 181
ddekany Avatar answered Nov 12 '22 21:11

ddekany


No, that looks good. I don't recommend relying on user.timezone for your string formatting, since eventually someone will start your webapp with the wrong timezone; instead, you can use

DateFormat formatter = new SimpleDateFormat("hh:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

Incidentally, have you seen CodeReview? This question is equally appropriate for both sites, given then way you've phrased it.

like image 2
Paul Hicks Avatar answered Nov 12 '22 21:11

Paul Hicks


You can override FreeMarkerView like that

public static class YourFreemarkerView extends FreeMarkerView {
    @Override
    protected void processTemplate(Template template, SimpleHash model, HttpServletResponse response)
            throws IOException, TemplateException {

        HttpRequestHashModel httpRequestHashModel = (HttpRequestHashModel)model.get(FreemarkerServlet.KEY_REQUEST);

        Environment env = template.createProcessingEnvironment(model, response.getWriter(), null);
        env.setLocale(RequestContextUtils.getLocale(httpRequestHashModel.getRequest()));
        env.setTimeZone(RequestContextUtils.getTimeZone(httpRequestHashModel.getRequest()));
        env.process();

    }       
}
like image 1
hoikin Avatar answered Nov 12 '22 20:11

hoikin