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 I've build up through the years is hard to give up. But with the advent of Liferay DXP it now seems the perfect time to make the switch. 

While working on a problem today, where an asset's (a document) modified date wasn't updated when you checkout/checkin a new version, I had to change something in a Freemarker display template a colleague made. 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 see if any improvements could be made. I first 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 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 some 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 a provision 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>

So now you can 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 then directly use the Freemarker ?datetime function on the date field of the asset entry. This will also apply to any further dates you want to print in this template using ?datetime (or ?date or ?time). As this only applies to one processor run, you can also 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 some more Freemarker related blog posts in the future.

Blogs
Thank you!
And if I would like to print a date differently based on the portal language setup, let's say 12/23/2016 for en_US and 23/12/2016 for it_IT, how to manage this? I've some problems in doing that, expecially regarding the 4 digits representation of the year...
Hi Alessandro,

The screenshot in the post basically shows a variant of the scenario you want. In it you can see that the dates in the 2 asset publishers are different based on the configured locale and datetime_format settings.

In your case you would just need to figure out the correct datetime_format to use. For an American date this would be MM/dd/yyyy and for a European date it would be dd/MM/yyyy.

If you want to easily test or experiment with formats you can use this online tool: http://www.sdfonlinetester.info/
Thank you Jan, I were able to print date with different locales.
The problem is that the default american date is not MM/dd/yyyy but MM/dd/yy (only two digits for the year). And the italian date is correctly represented as dd/MM/yy but also in this case only 2 digits for the year.
I cannot find any snippet to have instead MM/dd/yyyy and dd/MM/yyyy
I can change the value of the datetime_format to MM/dd/yyyy and it works fine for me and prints the date correctly. Example snippet: https://gist.github.com/planetsizebrain/a9dc41e72f3fba2a1bd84caef0741ac5