Total Pageviews

2020/11/01

[Apache Freemarker] Custom date/time format example

Purpose
FreeMarker allows you to define your own number and date/time/datetime formats, and associate a name to them.

Here has an example to demonstrate how to convert date from 2020/07/16 to 109/07/16



How-To
1. Create custom number format class:
package com.cht.tool.filegenerator.ftl.custom;

import freemarker.core.Environment;
import freemarker.core.TemplateDateFormat;
import freemarker.core.TemplateDateFormatFactory;
import freemarker.core.TemplateFormatUtil;
import freemarker.core.TemplateValueFormatException;
import freemarker.core.UnparsableValueException;
import freemarker.template.TemplateDateModel;
import freemarker.template.TemplateModelException;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * 將西元年轉為民國年
 * <p>
 * 使用方式 ${aDate?string.@mingguo}, 會將 2020/07/16 轉為 109/07/16
 */
public class MingGuoTemplateDateFormatFactory extends TemplateDateFormatFactory {

    public static final MingGuoTemplateDateFormatFactory INSTANCE = new MingGuoTemplateDateFormatFactory();

    private MingGuoTemplateDateFormatFactory() {
        // Defined to decrease visibility
    }

    @Override
    public TemplateDateFormat get(String s, int i, Locale locale,
                                  TimeZone timeZone, boolean b, Environment environment)
            throws TemplateValueFormatException {
        TemplateFormatUtil.checkHasNoParameters(s);
        return MingGuoTemplateDateFormat.INSTANCE;
    }

    private static class MingGuoTemplateDateFormat extends TemplateDateFormat {

        public static final MingGuoTemplateDateFormat INSTANCE = new MingGuoTemplateDateFormat();

        private MingGuoTemplateDateFormat() {
            // Defined to decrease visibility
        }

        @Override
        public String formatToPlainText(TemplateDateModel templateDateModel) throws TemplateModelException {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
            Date date = new Date(TemplateFormatUtil.getNonNullDate(templateDateModel).getTime());

            String dateStr = dateFormat.format(date);
            return (Integer.parseInt(dateStr.substring(0, 4)) - 1911) + dateStr.substring(4);
        }

        @Override
        public Object parse(String s, int i) throws TemplateValueFormatException {
            try {
                return new Date(Long.parseLong(s));
            } catch (NumberFormatException e) {
                throw new UnparsableValueException("Malformed long");
            }
        }

        @Override
        public boolean isLocaleBound() {
            return false;
        }

        @Override
        public boolean isTimeZoneBound() {
            return false;
        }

        @Override
        public String getDescription() {
            return "西元年轉民國年";
        }
    }

}

2. Register to freemarker configuration class:
    Configuration cfg = new Configuration(Configuration.getVersion());
    cfg.setClassForTemplateLoading(this.getClass(), "/");
    
    // register the "mingguo" format:
    Map<String, TemplateDateFormatFactory> customDateFormats = new HashMap<>();
    customDateFormats.put("mingguo", MingGuoTemplateDateFormatFactory.INSTANCE);

    cfg.setCustomDateFormats(customDateFormats);

3. Use this format in ftl template file:
<#-- 取得現在時間 -->
<#assign aDateTime = .now>
<#-- 只保留日期 -->
<#assign aDate = aDateTime?date>
<#-- create the macro variable: -->
<#macro printDate mingguodate>
列印日期:${mingguodate}

</#macro>
<#macro footer records amount>
${"-"?right_pad(25, "-")}
總筆數:${records}
總金額:${amount}
</#macro>
<#-- call the macro: -->
<@printDate mingguodate="${aDate?string.@mingguo}" />
<#assign total = 0>
${"水果名稱"?right_pad(10)}${"單價"?right_pad(10)}${"訂購數量"?right_pad(10)}
${"-"?right_pad(25, "-")}
<#list rows as row>
<#assign total += row.price * row.quantity>
${row.name?right_pad(10)}${row.price?string.@ntd?left_pad(10)}${row.quantity?left_pad(10)}
</#list>
<#-- call the macro: -->
<@footer records="${rows?size?left_pad(10)}" amount="${total?string.@ntd?left_pad(10)}" />


Reference

No comments: