tio-boot enjoy 自定义指令 localeDate
背景
在使用 JFinal 模板引擎进行日期格式化时,默认的 #date
指令虽然支持多种格式化模式,但其在处理不同地区(Locale)时存在局限性。例如,默认情况下,#date(model.date, "MMMM dd, yyyy")
可能输出为 “一月 16, 2025”,而在某些项目中需要将其显示为 “January 16, 2025”。经过分析源码发现,#date
指令不支持传入 Locale.ENGLISH
参数,因此需要自定义一个新的指令 localeDate
以支持指定 Locale
。
本文将详细介绍如何实现和使用 localeDate
指令,以满足项目对多语言日期格式化的需求。
自定义指令 localeDate
目标
创建一个支持 Locale
参数的日期格式化指令 #localeDate
,以实现根据指定地区格式化日期。例如,将日期格式化为英文格式 "January 16, 2025"。
实现步骤
创建
LocaleDateDirective
类在项目中新增
LocaleDateDirective
类,继承自 JFinal 的Directive
类,并实现日期格式化逻辑,支持传入Locale
参数。package com.litongjava.template; import java.text.SimpleDateFormat; import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; import java.util.Date; import java.util.Locale; import com.jfinal.template.Directive; import com.jfinal.template.Env; import com.jfinal.template.TemplateException; import com.jfinal.template.expr.ast.Expr; import com.jfinal.template.expr.ast.ExprList; import com.jfinal.template.io.Writer; import com.jfinal.template.stat.ParseException; import com.jfinal.template.stat.Scope; /** * #localeDate 支持 Locale 参数的日期格式化指令 * * 用法示例: * 1:#localeDate(createAt) 使用默认的 datePattern 和系统默认 Locale 格式化日期 * 2:#localeDate(createAt, "yyyy-MM-dd HH:mm:ss") 指定 datePattern,使用系统默认 Locale 格式化日期 * 3:#localeDate(createAt, "MMMM dd, yyyy", "en") 指定 datePattern 和 Locale,格式化日期 * 4:#localeDate() 使用默认的 datePattern 和系统默认 Locale 输出当前日期 */ public class LocaleDateDirective extends Directive { private Expr dateExpr; private Expr patternExpr; private Expr localeExpr; @Override public void setExprList(ExprList exprList) { int paraNum = exprList.length(); if (paraNum == 0) { this.dateExpr = null; this.patternExpr = null; this.localeExpr = null; } else if (paraNum == 1) { this.dateExpr = exprList.getExpr(0); this.patternExpr = null; this.localeExpr = null; } else if (paraNum == 2) { this.dateExpr = exprList.getExpr(0); this.patternExpr = exprList.getExpr(1); this.localeExpr = null; } else if (paraNum == 3) { this.dateExpr = exprList.getExpr(0); this.patternExpr = exprList.getExpr(1); this.localeExpr = exprList.getExpr(2); } else { throw new ParseException("Wrong number parameter of #localeDate directive, three parameters allowed at most", location); } } @Override public void exec(Env env, Scope scope, Writer writer) { Object date; String pattern; Locale locale = Locale.getDefault(); // 默认使用系统默认 Locale // 处理日期参数 if (dateExpr != null) { date = dateExpr.eval(scope); } else { date = new Date(); } // 处理格式化模式参数 if (patternExpr != null) { Object temp = patternExpr.eval(scope); if (temp instanceof String) { pattern = (String) temp; } else { throw new TemplateException("The second parameter datePattern of #localeDate directive must be String", location); } } else { pattern = env.getEngineConfig().getDatePattern(); } // 处理 Locale 参数 if (localeExpr != null) { Object localeVal = localeExpr.eval(scope); if (localeVal instanceof Locale) { locale = (Locale) localeVal; } else if (localeVal instanceof String) { // 简单处理:假设传入的字符串为语言代码,如 "en"、"zh" 等 locale = new Locale((String) localeVal); } else { throw new TemplateException("The third parameter locale of #localeDate directive must be Locale or String", location); } } write(date, pattern, locale, writer); } private void write(Object date, String pattern, Locale locale, Writer writer) { try { if (date instanceof Date) { // 使用 SimpleDateFormat 处理 java.util.Date SimpleDateFormat sdf = new SimpleDateFormat(pattern, locale); writer.write(sdf.format((Date) date)); } else if (date instanceof Temporal) { // 使用 DateTimeFormatter 处理 Java 8+ 的 Temporal 类型 DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, locale); writer.write(formatter.format((Temporal) date)); } else if (date != null) { throw new TemplateException("The first parameter of #localeDate directive cannot be " + date.getClass().getName(), location); } } catch (TemplateException | ParseException e) { throw e; } catch (Exception e) { throw new TemplateException(e.getMessage(), location, e); } } }
注册自定义指令
在模板引擎配置中注册新的指令
localeDate
,以便在模板中使用。package com.litongjava.template; import com.jfinal.kit.Kv; import com.jfinal.template.Engine; import com.jfinal.template.Template; public class EnjoyTemplate { static { Engine.use().addDirective("localeDate", LocaleDateDirective.class); } public static String renderToString(String fileName, Kv by) { Template template = Engine.use().getTemplate(fileName); String html = template.renderToString(by); return html; } }
在控制器中使用模板
以
IndexController
为例,展示如何在控制器中使用自定义的localeDate
指令进行日期格式化。package com.litongjava.admin.blog.controller; import java.util.List; import com.jfinal.kit.Kv; import com.litongjava.admin.blog.model.TioBootAdminArticle; import com.litongjava.annotation.RequestPath; import com.litongjava.template.EnjoyTemplate; import com.litongjava.tio.boot.http.TioRequestContext; import com.litongjava.tio.http.common.HttpRequest; import com.litongjava.tio.http.common.HttpResponse; import com.litongjava.tio.http.server.util.Resps; @RequestPath public class IndexController { @RequestPath() public HttpResponse index(HttpRequest request) { String fileName = "/index.html"; List<TioBootAdminArticle> articles = TioBootAdminArticle.dao.find("select id,title,summary,tags,date from $table_name where visibility='public'"); Kv by = Kv.by("list", articles); String html = EnjoyTemplate.renderToString(fileName, by); return Resps.html(TioRequestContext.getResponse(), html); } }
使用 localeDate
指令
在模板文件(例如 index.html
)中,可以使用 #localeDate
指令进行日期格式化。以下是一些使用示例:
使用默认的
datePattern
和系统默认Locale
格式化日期#localeDate(model.date)
输出示例:
January 16, 2025
指定
datePattern
,使用系统默认Locale
格式化日期#localeDate(model.date, "yyyy-MM-dd HH:mm:ss")
输出示例:
2025-01-16 14:30:00
指定
datePattern
和Locale
,格式化日期#localeDate(model.date, "MMMM dd, yyyy", "en")
输出示例:
January 16, 2025
使用默认的
datePattern
和系统默认Locale
输出当前日期#localeDate()
输出示例:
January 16, 2025
说明
参数说明:
- 第一个参数(可选):日期对象(如
java.util.Date
、java.time.LocalDateTime
等)。若未提供,则默认使用当前日期。 - 第二个参数(可选):日期格式字符串(如
"MMMM dd, yyyy"
)。若未提供,则使用引擎配置的默认格式。 - 第三个参数(可选):
Locale
,可以是Locale
对象或语言代码字符串(如"en"
、"zh"
等)。若未提供,则使用系统默认Locale
。
- 第一个参数(可选):日期对象(如
类型支持:
- 对于
java.util.Date
类型,使用SimpleDateFormat
进行格式化。 - 对于 Java 8 及以上版本的
Temporal
类型(如LocalDateTime
、LocalDate
、LocalTime
),使用DateTimeFormatter
进行格式化。
- 对于
异常处理:
- 若参数类型不符合要求,会抛出相应的
TemplateException
,提示使用者参数类型错误。
- 若参数类型不符合要求,会抛出相应的
完整示例
假设有一个 index.html
模板文件,内容如下:
<!DOCTYPE html>
<html>
<head>
<title>文章列表</title>
</head>
<body>
<h1>最新文章</h1>
<ul>
#foreach(article in list)
<li>
<h2>${article.title}</h2>
<p>${article.summary}</p>
<p>标签:${article.tags}</p>
<p>发布日期:#localeDate(article.date, "MMMM dd, yyyy", "en")</p>
</li>
#end
</ul>
</body>
</html>
在上述模板中,#localeDate(article.date, "MMMM dd, yyyy", "en")
将会根据指定的格式和英文 Locale
将 article.date
格式化为 “January 16, 2025”。
输出结果
在控制器 IndexController
中渲染模板后,浏览器中显示的日期格式如下:
January 16, 2025
总结
通过自定义 localeDate
指令,可以在 JFinal 模板中灵活地根据不同的地区(Locale)格式化日期。这不仅增强了模板的国际化支持,还提高了项目的可维护性和扩展性。按照本文的步骤实现并注册 localeDate
指令后,即可在模板中轻松使用多语言日期格式化功能。