tio-boot enjoy 自定义指令 `localeDate`

原创
01/16 11:35
阅读数 151

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"。

实现步骤

  1. 创建 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);
        }
      }
    }
    
  2. 注册自定义指令

    在模板引擎配置中注册新的指令 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;
      }
    
    }
    
  3. 在控制器中使用模板

    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 指令进行日期格式化。以下是一些使用示例:

  1. 使用默认的 datePattern 和系统默认 Locale 格式化日期

    #localeDate(model.date)
    

    输出示例:

    January 16, 2025
    
  2. 指定 datePattern,使用系统默认 Locale 格式化日期

    #localeDate(model.date, "yyyy-MM-dd HH:mm:ss")
    

    输出示例:

    2025-01-16 14:30:00
    
  3. 指定 datePatternLocale,格式化日期

    #localeDate(model.date, "MMMM dd, yyyy", "en")
    

    输出示例:

    January 16, 2025
    
  4. 使用默认的 datePattern 和系统默认 Locale 输出当前日期

    #localeDate()
    

    输出示例:

    January 16, 2025
    

说明

  • 参数说明:

    • 第一个参数(可选):日期对象(如 java.util.Datejava.time.LocalDateTime 等)。若未提供,则默认使用当前日期。
    • 第二个参数(可选):日期格式字符串(如 "MMMM dd, yyyy")。若未提供,则使用引擎配置的默认格式。
    • 第三个参数(可选):Locale,可以是 Locale 对象或语言代码字符串(如 "en""zh" 等)。若未提供,则使用系统默认 Locale
  • 类型支持:

    • 对于 java.util.Date 类型,使用 SimpleDateFormat 进行格式化。
    • 对于 Java 8 及以上版本的 Temporal 类型(如 LocalDateTimeLocalDateLocalTime),使用 DateTimeFormatter 进行格式化。
  • 异常处理:

    • 若参数类型不符合要求,会抛出相应的 TemplateException,提示使用者参数类型错误。

完整示例

假设有一个 index.html 模板文件,内容如下:




    <title>文章列表</title>


    <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>


在上述模板中,#localeDate(article.date, "MMMM dd, yyyy", "en") 将会根据指定的格式和英文 Localearticle.date 格式化为 “January 16, 2025”。

输出结果

在控制器 IndexController 中渲染模板后,浏览器中显示的日期格式如下:

January 16, 2025

总结

通过自定义 localeDate 指令,可以在 JFinal 模板中灵活地根据不同的地区(Locale)格式化日期。这不仅增强了模板的国际化支持,还提高了项目的可维护性和扩展性。按照本文的步骤实现并注册 localeDate 指令后,即可在模板中轻松使用多语言日期格式化功能。 </tiobootadminarticle>

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部