一、核心概念

1.1 日期时间类型

在 FreeMarker 中,日期时间类型主要包括以下几种:

类型 说明
date 仅包含日期部分(年月日)
time 仅包含时间部分(时分秒及毫秒)
datetime 包含完整的时间和日期
iso_utc_* 系列 用于 ISO 8601 格式的 UTC 时间输出

1.2 内建函数(Built-ins)

FreeMarker 提供了以下用于格式化的内建函数:

内建函数 说明
?date 格式化为日期(YYYY-MM-DD)
?time 格式化为时间(HH:mm:ss)
?datetime 格式化为完整的时间戳(YYYY-MM-DD HH:mm:ss)
?iso_utc_* 系列 输出 ISO 8601 格式的时间(UTC 时间)

1.3 时区处理

FreeMarker 默认使用 JVM 的默认时区(可通过 Configuration.setTimeZone() 设置),但输出 ISO 格式时可指定为 UTC。


二、操作步骤(详细)

步骤 1:准备数据模型

确保你传递给模板的数据中包含 Date 类型或 java.util.Calendar 类型的字段。

Map<String, Object> model = new HashMap<>();
model.put("now", new Date());

步骤 2:在模板中使用内建函数

示例 1:格式化为日期

当前日期:${now?date}

输出:

当前日期:2025-07-28

示例 2:格式化为时间

当前时间:${now?time}

输出:

当前时间:15:42:00

示例 3:格式化为完整日期时间

当前完整时间:${now?datetime}

输出:

当前完整时间:2025-07-28 15:42:00

示例 4:ISO 8601 UTC 格式

ISO UTC 时间:${now?iso_utc_datetime}

输出:

ISO UTC 时间:2025-07-28T15:42:00Z

三、格式化格式自定义(高级)

虽然 ?date, ?time, ?datetime 有默认格式,但你也可以使用 ?string 指定自定义格式。

使用 ?string 自定义格式

自定义格式:${now?string("yyyy年MM月dd日 HH:mm:ss")}

输出:

自定义格式:2025年07月28日 15:42:00

常用格式化符号

符号 含义
yyyy 四位年份
MM 月份(01-12)
dd 日期(01-31)
HH 小时(24小时制)
mm 分钟
ss
S 毫秒
a 上午/下午(AM/PM)

四、常见错误与注意事项

4.1 类型不匹配错误

错误示例:

${"2025-07-28"?date}

错误原因: 字符串 "2025-07-28" 不是 Date 类型,不能直接使用 ?date

解决办法: 使用 ?date("yyyy-MM-dd") 明确指定格式解析字符串为日期。

${"2025-07-28"?date("yyyy-MM-dd")}

4.2 时区问题

FreeMarker 默认使用 JVM 的时区,可能导致输出时间与预期不符。

解决办法:

  • 设置 FreeMarker 的全局时区:
configuration.setTimeZone(TimeZone.getTimeZone("GMT+8"));
  • 或者在模板中使用 UTC 格式:
${now?iso_utc_datetime}

4.3 日期格式不一致

使用 ?date, ?time, ?datetime 时,输入的日期格式必须与解析格式一致。

示例:

${"2025/07/28"?date("yyyy/MM/dd")}

如果格式不一致,会抛出异常。


五、使用技巧

5.1 转换字符串为日期

<#assign dateStr = "2025-07-28">
<#assign myDate = dateStr?date("yyyy-MM-dd")>

5.2 获取当前时间

使用内置变量 now

当前时间:${now?datetime}

5.3 判断是否为日期类型

<#if someVar is "date">
  这是一个日期
</#if>

5.4 计算时间差(需配合 Java 工具类)

FreeMarker 本身不支持时间差计算,但可以传入 Java 工具类:

model.put("dateUtil", new DateUtils());

模板中调用:

${dateUtil.daysBetween(startDate, endDate)}

六、最佳实践

6.1 统一使用 UTC 时间输出

在国际化项目中,推荐使用 UTC 时间,避免时区混乱。

${now?iso_utc_datetime}

6.2 使用 ?string 自定义格式

避免依赖默认格式,提高可读性和可维护性。

${now?string("yyyy年MM月dd日 HH:mm:ss")}

6.3 避免在模板中进行复杂逻辑

如需复杂日期处理,应在 Java 层处理后再传入模板。


七、性能优化

7.1 避免频繁转换

在模板中频繁使用 ?date("yyyy-MM-dd") 解析字符串会影响性能,建议在 Java 层将字符串转为 Date 类型再传入模板。

7.2 使用缓存

如果模板中多次使用相同格式化后的日期,可以将其赋值给一个变量:

<#assign formattedDate = now?string("yyyy-MM-dd")>

7.3 避免在循环中格式化日期

<#list> 循环中频繁格式化日期会降低性能,应提前在 Java 中格式化好。


八、总结

内容 说明
核心函数 ?date, ?time, ?datetime, ?iso_utc_*
自定义格式 使用 ?string("yyyy-MM-dd HH:mm:ss")
常见错误 类型不匹配、格式不一致、时区问题
最佳实践 使用 UTC 时间、统一格式、避免复杂逻辑
性能优化 缓存结果、避免重复格式化、提前处理

九、附录:常用格式化表达式示例

表达式 输出示例
${now?date} 2025-07-28
${now?time} 15:42:00
${now?datetime} 2025-07-28 15:42:00
${now?string("yyyy年MM月dd日")} 2025年07月28日
${now?string("yyyy-MM-dd HH:mm:ss")} 2025-07-28 15:42:00
${now?iso_utc_datetime} 2025-07-28T15:42:00Z

如需更高级功能(如日期加减、比较等),建议使用 Java 工具类(如 java.time.LocalDate, LocalDateTime)处理后传入模板。