Formatting date in Thymeleaf

June 21, 2020 No comments Thymeleaf Date Format calendars

1. Introduction

In this article, we are going to present ways to format Date in Thymeleaf templates. We will use special #calendars util available in Thymeleaf engine, created for date manipulations on templates.

2. Using #calendar utility class

Thymeleaf comes with a special utility class called #calendars, that can be used in expression statements to format date.

Example of date formatting with specific pattern is shown in the following code:

${#calendars.format(cal, 'dd/MMM/yyyy HH:mm')}

In patterns we can use the following letters (very similar to SimpleDateFormat pattern):

G - Era designator (before christ, after christ)
y - Year (e.g. 12 or 2012). Use either yy or yyyy.
M - Month in the year. Number of M's determine length of format (e.g. MM, MMM or MMMMM)
d - Day in a month. Number of d's determine length of format (e.g. d or dd)
h - Hour of the day, 1-12 (AM / PM) (normally hh)
H - Hour of the day, 0-23 (normally HH)
m - Minute in an hour, 0-59 (normally mm)
s - Second in a minute, 0-59 (normally ss)
S - Millisecond in second, 0-999 (normally SSS)
E - Day in a week (e.g Monday, Tuesday, etc.)
D - Day in the year (1-366)
F - Day of week in the month (e.g. 1st Thursday of December)
w - Week in the year (1-53)
W - Week in a month (0-5)
a - AM / PM marker
k - Hour in day (1-24, unlike HH's 0-23)
K - Hour in day, AM / PM (0-11)
z - Time Zone

3. Real-life example

Now let's use #calendars utility class in a real-life application.

The project structure is as follows:

── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── frontbackend
│   │   │           └── thymeleaf
│   │   │               ├── Application.java
│   │   │               ├── config
│   │   │               │   └── WebConfig.java
│   │   │               └── controller
│   │   │                   └── IndexController.java
│   │   └── resources
│   │       ├── messages_en_US.properties
│   │       ├── messages_es.properties
│   │       ├── messages.properties
│   │       └── templates
│   │           └── index.html

All GET requests to the root context are handled by IndexController. Additionally, we set exampleDate variable with the date 20/06/2020. We will use this variable to check how the formatting will change by changing the locale.

package com.frontbackend.thymeleaf.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/")
public class IndexController {

    private final static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

    @GetMapping
    public String main(Model model) throws ParseException {
        model.addAttribute("exampleDate", sdf.parse("20/06/2020"));
        return "index";
    }
}

WebConfig class is used to change default Spring Boot locale configuration and to set LocaleChangeInterceptor - that will handle language change initialized on the frontend.

package com.frontbackend.thymeleaf.config;

import java.util.Locale;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver localeResolver = new SessionLocaleResolver();
        localeResolver.setDefaultLocale(Locale.US);
        return localeResolver;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        return localeChangeInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

Message bundle for localized messages has the following content:

messages.properties:

home.title=Title
date.format=MM/dd/yyyy

messages_es.properties

home.title=Título
date.format=dd/MM/yyyy

Finally our Thymeleaf template - index.html:

<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Spring Boot Thymeleaf Application - Bootstrap Date Format</title>

    <link th:rel="stylesheet" th:href="@{assets/bootstrap-slider/css/bootstrap-slider.css}"/>
    <link th:rel="stylesheet" th:href="@{webjars/bootstrap/4.0.0-2/css/bootstrap.min.css} "/>
</head>
<body>

<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
    <div class="container">
        <a class="navbar-brand" href="/">Thymeleaf - Bootstrap Date Format</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
                aria-controls="navbarResponsive"
                aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
            <ul class="navbar-nav ml-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="#">Home
                        <span class="sr-only">(current)</span>
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">About</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Services</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Contact</a>
                </li>
                <li class="nav-item dropdown">
                    <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button"
                       data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
                       th:text="${#strings.toUpperCase(#locale)}">
                    </a>
                    <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                        <a class="dropdown-item" th:href="@{/(lang=en)}">English</a>
                        <a class="dropdown-item" th:href="@{/(lang=es)}">Spanish</a>
                    </div>
                </li>
            </ul>
        </div>
    </div>
</nav>

<div class="container">

    <div class="row">
        <div class="col mt-5">

            <h1 th:text="#{home.title}"></h1>

            <div th:with="df=#{date.format}">Date formatted by locale: <strong
                    th:text="${#calendars.format(exampleDate,df)}"></strong></div>

            <div>Custom date format 1: <strong
                    th:text="${#calendars.format(exampleDate,'yyyy.MM.dd G,  HH:mm:ss z')}"></strong></div>

            <div>Custom date format 2: <strong
                    th:text="${#calendars.format(exampleDate,'yyyy-MM-dd HH:mm:ss.SSSZ')}"></strong></div>

            <div>Custom date format 3: <strong
                    th:text="${#calendars.format(exampleDate,'EEE, d MMM yyyy HH:mm:ss Z')}"></strong></div>
        </div>
    </div>
</div>


<script th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script th:src="@{/webjars/popper.js/umd/popper.min.js}"></script>
<script th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>

</body>
</html>

We used locale variables to set df object with localized date format.

<div th:with="df=#{date.format}">Date formatted by locale: <strong
                    th:text="${#calendars.format(exampleDate,df)}"></strong></div>

The application presents the following functionality:

Thymeleaf bootstrap date formatting

4. Conclusion

In this article, we presented how to format the date in Thymeleaf templates. We used a sample Spring Boot application and changed default locale configuration to present how to use locale-dependent date formats.

As usual, the code used in this article is available under our GitHub repository

{{ message }}

{{ 'Comments are closed.' | trans }}