Contents

Thymeleaf 日期处理

1. 简介

Thymeleaf 是一个能够直接与 Spring 一起工作的 Java 模板引擎。有关 Thymeleaf 和 Spring 的介绍,请查看这篇 文章。

除了这些基本功能外,Thymeleaf 还为我们提供了一组实用对象,可帮助我们在应用程序中执行常见任务。

在本教程中,我们将讨论新旧 Java Date类的处理和格式化以及 Thymeleaf 3.0 的一些特性。

2.Maven依赖

首先,让我们创建配置以将 Thymeleaf 与 Spring 集成到我们的pom.xml中:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>

可以在 Maven Central 上找到*thymeleaf thymeleaf-spring5 * 的最新版本。请注意,对于 Spring 4 项目, 必须使用thymeleaf-spring4库而不是thymeleaf-spring5

此外,为了使用新的 Java 8 Date类,我们需要向pom.xml添加另一个依赖项:

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

thymeleaf-extras 是一个可选模块,由 Thymeleaf 官方团队完全支持,它是为了与 Java 8 Time API 兼容而创建的。它在表达式评估期间将 #temporals对象添加到Context作为实用程序对象处理器。这意味着它可以用于评估对象图形导航语言 (OGNL) 和 Spring 表达式语言 (SpringEL) 中的表达式。

3. 新旧:java.utiljava.time

Time包是 Java SE 平台的新日期、时间和日历 API 。这个新 API 与旧的Date * API 之间的主要区别在于,新 API 区分了时间轴的机器视图和人类视图。机器视图显示一系列相对于epoch*的整数值,而人类视图显示一组字段(例如,年、月和日)。

要使用新的Time包,我们需要配置我们的模板引擎以使用新的Java8TimeDialect

private ISpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
    SpringTemplateEngine engine = new SpringTemplateEngine();
    engine.addDialect(new Java8TimeDialect());
    engine.setTemplateResolver(templateResolver);
    return engine;
}

这将添加类似于标准方言中的 #temporals对象,允许从 Thymeleaf 模板格式化和创建Temporal对象。

为了测试新旧类的处理,我们将创建以下变量并将它们作为模型对象添加到我们的控制器类中:

model.addAttribute("standardDate", new Date());
model.addAttribute("localDateTime", LocalDateTime.now());
model.addAttribute("localDate", LocalDate.now());
model.addAttribute("timestamp", Instant.now());

现在,我们准备好使用 Thymeleaf 的ExpressionTemporals实用程序对象。

3.1. 格式化日期

我们要介绍的第一个函数是Date对象的格式(添加到 Spring 模型参数中)。我们将使用ISO8601格式:

<h1>Format ISO</h1>
<p th:text="${#dates.formatISO(standardDate)}"></p>
<p th:text="${#temporals.formatISO(localDateTime)}"></p>
<p th:text="${#temporals.formatISO(localDate)}"></p>
<p th:text="${#temporals.formatISO(timestamp)}"></p>

无论我们的Date在后端是如何设置的,都会按照选择的标准显示在 Thymeleaf 中。standardDate将由*#dates实用程序处理。#temporals实用程序将处理新的LocalDateTime*、LocalDateInstant类。

此外,如果我们想手动设置格式,我们可以使用:

<h1>Format manually</h1>
<p th:text="${#dates.format(standardDate, 'dd-MM-yyyy HH:mm')}"></p>
<p th:text="${#temporals.format(localDateTime, 'dd-MM-yyyy HH:mm')}"></p>
<p th:text="${#temporals.format(localDate, 'MM-yyyy')}"></p>

正如我们所观察到的,我们无法使用 #temporals.format(…)处理Instant类——这将导致UnsupportedTemporalTypeException。此外,仅当我们仅指定特定日期字段,跳过时间字段时,才可能格式化LocalDate

让我们看看最终结果:

/uploads/dates_in_thymeleaf/1.png

3.2. 获取特定日期字段

为了获取java.util.Date类的特定字段,我们应该使用以下实用程序对象:

${#dates.day(date)}
${#dates.month(date)}
${#dates.monthName(date)}
${#dates.monthNameShort(date)}
${#dates.year(date)}
${#dates.dayOfWeek(date)}
${#dates.dayOfWeekName(date)}
${#dates.dayOfWeekNameShort(date)}
${#dates.hour(date)}
${#dates.minute(date)}
${#dates.second(date)}
${#dates.millisecond(date)}

对于新的java.time包,我们应该坚持使用*#temporals*实用程序:

${#temporals.day(date)}
${#temporals.month(date)}
${#temporals.monthName(date)}
${#temporals.monthNameShort(date)}
${#temporals.year(date)}
${#temporals.dayOfWeek(date)}
${#temporals.dayOfWeekName(date)}
${#temporals.dayOfWeekNameShort(date)}
${#temporals.hour(date)}
${#temporals.minute(date)}
${#temporals.second(date)}
${#temporals.millisecond(date)}

让我们看几个例子。首先,让我们显示一周中的今天:

<h1>Show only which day of a week</h1>
<p th:text="${#dates.day(standardDate)}"></p>
<p th:text="${#temporals.day(localDateTime)}"></p>
<p th:text="${#temporals.day(localDate)}"></p>

接下来,让我们显示工作日的名称:

<h1>Show the name of the week day</h1>
<p th:text="${#dates.dayOfWeekName(standardDate)}"></p>
<p th:text="${#temporals.dayOfWeekName(localDateTime)}"></p>
<p th:text="${#temporals.dayOfWeekName(localDate)}"></p>

最后,让我们显示当天的当前秒数:

<h1>Show the second of the day</h1>
<p th:text="${#dates.second(standardDate)}"></p>
<p th:text="${#temporals.second(localDateTime)}"></p>

请注意,为了使用时间部分,我们需要使用LocalDateTime,因为LocalDate会引发错误。

4. 如何在表单中使用日期选择器

让我们看看*如何使用日期选择器从 Thymeleaf 表单提交*Date 值。

首先,让我们创建一个具有Date属性的Student类:

public class Student implements Serializable {
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthDate;
}

@DateTimeFormat注释声明birthDate字段应格式化为Date

接下来,我们将创建一个 Thymeleaf 表单来提交*Date *输入:

<form th:action="@{/saveStudent}" method="post" th:object="${student}">
    <div>
        <label for="student-birth-date">Date of birth:</label>
        <input type="date" th:field="${student.birthDate}" id="student-birth-date"/>
    </div>
    <div>
        <button type="submit" class="button">Submit</button>
    </div>
</form>

当我们提交表单时,控制器会拦截在表单中映射的带有th:object 属性的Student对象。此外,th:field 属性将输入值与birthDate字段绑定。

现在,让我们创建一个控制器来拦截POST请求:

@RequestMapping(value = "/saveStudent", method = RequestMethod.POST)
public String saveStudent(Model model, @ModelAttribute("student") Student student) {
    model.addAttribute("student", student);
    return "datePicker/displayDate.html";
}

提交表单后,我们将 在另一个页面上以dd/MM/yyyy模式显示birthDate值:

<h1>Student birth date</h1>
<p th:text="${#dates.format(student.birthDate, 'dd/MM/yyyy')}"></p>

结果显示了带有日期选择器的表单:

/uploads/dates_in_thymeleaf/3.png

提交表单后,我们将看到选定的日期:

/uploads/dates_in_thymeleaf/5.png