Liferay Freemarker Tips and Tricks: Date & Time
I’ve been working with Liferay for quite some time now, but I must confess that I still haven’t really made the switch from Velocity to Freemarker for my templates. Even though I know there are a lot of benefits to using Freemarker, like better error messages, the Velocity knowledge and library of snippets that I’ve build up through the years is hard to give up. But with the advent of Liferay DXP, it now seems like the perfect time to make the switch.
While working on a problem today, where an asset’s modified date wasn’t updated when checking out/checking in a new version, I had to change something in a Freemarker display template that was used to show a list of documents. At first, before I knew there was a problem in Liferay, I thought the issue was that the template wasn’t providing the timeZone to the dateUtil call in the template:
<ul>
<#list entries as entry>
<#assign dateFormat = "MMM d, HH':'mm" />
<li>${entry.getTitle()} - ${dateUtil.getDate(entry.getModifiedDate(), dateFormat, locale)}</li>
</#list>
</ul>
So this looked like the perfect time to see how to fix this and discover if any improvements could be made. I started off with fixing the line as-is and just added the timeZone (which is already present in the template context – see com.liferay.portal.template.TemplateContextHelper).
<ul>
<#list entries as entry>
<#assign dateFormat = "MMM d, HH':'mm" />
<li>${entry.getTitle()} - ${dateUtil.getDate(entry.getModifiedDate(), dateFormat, locale, timeZone)}</li>
</#list>
</ul>
While this solution does the trick and produces the correct datetime in the timezone we want, it did look a bit verbose. So I wondered if Freemarker had some stuff that might make it shorter/sweeter/better. After looking around, I found these 2 things: built-in date formatting and processing settings.
The first would allow us to drop the dateUtil, but doesn’t seem to have an arrangement for providing a locale and/or timezone. This is where the second article comes in. These processing settings allow us to set some stuff that further Freemarker processing will take into account and luckily for us the datetime stuff is one of those things. So with the combination of both, our template becomes:
<#setting time_zone=timeZone.ID>
<#setting locale=locale.toString()>
<#setting datetime_format="MMM d, HH':'mm">
<ul>
<#list entries as entry>
<li><li>${entry.title} - ${entry.modifiedDate?datetime}</li></li>
</#list>
</ul>
Now you see that we can just set up the processor beforehand to use the Liferay timezone and locale, but also our chosen datetime format. This allows us to directly use the Freemarker ?datetime function on the date field of the asset entry. It will also apply to any further dates you want to print in this template using ?datetime (or ?date or ?time). As it only applies to one processor run, you can have different templates, that set these processor settings differently, on the same page without them interfering with each other. The following screenshot shows the template above and the same template where the timezone, locale and datetime format are set differently:
The beauty and ease of use of this small improvement has already made me change my mind and hopefully I can write more Freemarker related blog posts in the future.