一、核心概念
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
)处理后传入模板。