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

2020/10/31

[Travel] 基隆情人湖

基隆情人湖 

基隆情人湖

基隆情人湖

基隆情人湖

基隆情人湖

基隆情人湖

2020/10/10

[Apache Freemarker] custom number 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 amount from 2380 to NT$2,380.


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

import freemarker.core.Environment;
import freemarker.core.TemplateFormatUtil;
import freemarker.core.TemplateNumberFormat;
import freemarker.core.TemplateNumberFormatFactory;
import freemarker.core.TemplateValueFormatException;
import freemarker.core.UnformattableValueException;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import freemarker.template.utility.NumberUtil;

import java.text.DecimalFormat;
import java.util.Locale;

/**
 * 將金額加上千分號,並註明新台幣 NT
 * <p>
 * 使用方式 ${total?string.@ntd},2380 會被轉換為 NT$2,380
 */
public class NTDTemplateNumberFormatFactory extends TemplateNumberFormatFactory {

    public static final NTDTemplateNumberFormatFactory INSTANCE = new NTDTemplateNumberFormatFactory();
    private static DecimalFormat ntdFormat = new DecimalFormat("NT$#,###");

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

    @Override
    public TemplateNumberFormat get(String s, Locale locale, Environment environment)
            throws TemplateValueFormatException {
        TemplateFormatUtil.checkHasNoParameters(s);
        return NTDTemplateStringFormat.INSTANCE;
    }

    private static class NTDTemplateStringFormat extends TemplateNumberFormat {

        private static final NTDTemplateStringFormat INSTANCE = new NTDTemplateStringFormat();

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

        /**
         * 2380 會被轉換為 NT$2,380 的邏輯寫在這裡
         *
         * @param numberModel
         * @return
         * @throws TemplateValueFormatException
         * @throws TemplateModelException
         */
        @Override
        public String formatToPlainText(TemplateNumberModel numberModel)
                throws TemplateValueFormatException, TemplateModelException {
            Number number = TemplateFormatUtil.getNonNullNumber(numberModel);
            try {
                return ntdFormat.format(NumberUtil.toIntExact(number));
            } catch (RuntimeException e) {
                throw new UnformattableValueException(number + " - 金額轉換失敗", e);
            }
        }

        @Override
        public boolean isLocaleBound() {
            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 "ntd" format:
    Map<String, TemplateNumberFormatFactory> customNumberFormats = new HashMap<>();
    customNumberFormats.put("ntd", NTDTemplateNumberFormatFactory.INSTANCE);
    
    cfg.setCustomNumberFormats(customNumberFormats);

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

2020/10/09

[Apache Freemarker] macro variable example

Purpose
Macro variable stores a template fragment that can be used as user-defined directive. The variable also stores the name of allowed parameters to the user-defined directive. You must give value for all of those parameters when you use the variable as directive, except for parameters that has a default value. The default value will be used if and only if you don't give value for the parameter when you call the macro.

How-To
Here has two examples:
<#-- 取得現在時間 -->
<#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)}" />

2020/10/08

[閱讀筆記] Misbehaving: The Making of Behavioral Economics (不當行為, 4/4)

  1. 價格經常是錯的,而且有時候還錯得很離譜。除此之外,當價格背離基本價值甚遠,資源的配置錯誤也會變得更嚴重。例如在美國,當住宅價格發生國家性上漲時,有些區域不但漲得特別快,房價租金比甚至飆破歷史紀錄,有些區域甚至樂觀地預期未來房價會繼續上漲,沒想到回歸均值 (regression to the mean) 這回事。

  2. 決策心理學有五項發現:

決策心理學有五項發現

說明

① 人們常過度自信

人們對於自己的判斷能力的評價,高過其實際能力

② 人們常會做出過於極端的預測

人常會在當下做出一廂情願地做出極端預測

③ 贏家的詛咒

當許多投標人為同一個標的競爭時,贏得拍賣的往往是最看重標的物的那個人

④ 錯誤共識效應

一般人傾向於認為其他人與他們偏好相同,認為其他對手會跟他覬覦相同目標

⑤ 現時偏好

任何人都想要現在就拔得頭籌


  1. 代理人問題,更正確的名稱是「笨蛋委託人問題」,因為老闆想要馬上就看出成績,下屬就必須做出短視的決策。在許多情況,而且不限於體育活動領域,老闆都至少要和僱員負起同樣責任,總教練之所以交易順位更高的選秀權,是因為老闆希望現在就看到比賽獲勝。

  2. 遵循傳統智慧能讓你免於被炒魷魚,聰明的老闆會敦促部署遵循能夠在最大程度提升獲勝機率的策略,並且告訴他們沒人會因此丟掉工作,但是這樣的老闆不多。

  3. 研究發現,有兩種情境會降低常人的風險規避清向,甚至變得主動尋求風險。第一種情境是當他們手風正順,玩的是「莊家的錢」;另一種情境則是當他們落居下方,確有機會扳回一城時。「一擲千金」的參賽者表現出同樣的傾向,而且是當他們面對損失鉅額金錢的高風險時。

  1. 當人們有機會翻本時,傾向於在面對損失時押注冒險。

  2. 人們無法存到足夠的錢退休的三大障礙

三大障礙

說明

惰性

  • 加入退休金計畫後,人就會懶得變更。

  • 讓民眾一開始就選擇儲蓄率較高的退休新計畫,並設定他們自動加入該計畫,讓惰性從阻力變成助力

損失規避心態

  • 人們都痛恨損失,討厭自己的薪水數字下降。

  • 損失規避是以名目數字來估計,不考慮通膨調整,所以如果可以想出某種避免讓員工感覺自己的薪水數字被削減,就不會對儲蓄心生抗拒。

自我控制

  • 人們都傾向選擇當下,但是當我們想到的是未來而非當下時,就會更有自制力。


  1. 「家長式領導」或稱為「推力」,無法解決所有問題,某些禁令與命令無可避免,社會必然需要規則和法條才能存在。我們要求孩子們上學、行車須遵守交通規則等。各國可自行決定駕駛應該左駕或右駕,不過當英國人走訪美國時就不可以右駕;即使是徹頭徹尾的自由主義者也同意,你不該因為討厭鄰居就把他殺死。

  2. 「推力」(nudge) 可以減少錯誤,例如 Amsterdam 的 Schiphol Airport,為了讓男性在使用機場廁所的小便斗時,更注意自己射出的方向,便在小便斗增加佳瑩的蝕刻圖像,根據機場管理者回報,這些家蠅圖減少 80% 的尿意就飛濺。小便斗的蒼蠅變成推力的完美典範。推力是吸引我們注意力和影響行為環境的某些小功能

  3. 又如英國政府,為了讓納稅人儘早繳清欠稅,減少走上代價高昂的法律訴訟,也善用了推力,其寄信給欠稅的公民,多數人最後乖乖繳稅,讓高達 900 萬英鎊的稅款較往常提前 23 天入帳,信件的內容如下:

英國絕大多數人按時繳納稅款。

在您的居住地,絕大多數皆按時繳納稅款。

你目前是極少數沒有按時繳稅的人。


  1. 如果你想鼓勵某人做某事,就得把它弄得容易一點。改變人們行為的第一步稱為「解凍」。而解凍的其中一種方法就是移除阻止他們改變的障礙

  2. 所謂推力不一定要很有創造力、很精密,或是得巧妙隱身。簡訊的形式很簡單,而且直接提醒也可以非常有效。舉個來自健康領域的例子,為了提醒瘧疾病患在整個療程中按時服藥,運用提醒服用瘧疾藥物的簡訊就非常有效。有效的簡訊通常都很短,畢竟提醒本身才是最重要的資訊,而非其他額外資訊

  3. 就是因為市場會犯錯,金融市場才會變成最有機會賺錢的地方,才會有不少研究者的聰明才智都投注在鑽研可望帶來利潤的投資策略。

  4. 一個理論若是建立在理性經濟人行為之上,就不可能有實證的基礎,因為理性經濟人根本不存在於這個世界。

  5. 一個亟待行為學分析的重要整體經濟政策,就是如何打造能夠提振經濟的減稅方案。這時政策制定者需考慮到一個原本認為無關的因素,退稅應該一次給足或在一年內分多次給?若目標是刺激消費支出,我的建議是分成多次給。一次給足的退稅可能會變成存款,或被用於償還貸款

  6. 另一個亟需透過行為學檢視分析的宏觀問題是,鼓勵民眾創業的最佳方式究竟為何(尤其是鼓勵那些成功機率較高的)?右派經濟學家傾向降低高收入者的邊際稅率,左派經濟學家傾向於推動特定產業的補助,或放寬中小企業的貸款資格。但是,國家該做的是,為失敗者提供較柔軟的緩衝墊,讓追求成功者不必冒著付出沈痛代價的失敗風險。

  1. 許多創業者一開始就對成功機率懷抱不切實際的期望,絕大多數創業者相信自己的成功機率遠超過平均,⅓ 左右的創業者相信他們鐵定成功。

  2. 在教育領域,如何讓孩子們學到最多東西?依據研究結果指出,獎勵學生的輸入(例如,做功課)而非產出(例如,成績)是有效果的。因為最需要幫助的學生其實並不知道如何提升自己的程度,獎勵他們投入的教育者相信能夠發揮效益的事物,自然是合理的做法。

  1. 獎勵金給予的方式,會對教師們造成莫大影響,若規定未達目標必須返還獎金,在學年一開始就收到獎勵金的教師,其學生表現的進步幅度明晰高於那些目標相同,但學期末才能事情況得到獎勵金的教師。這項發現有個要特別注意的地方,就是收回獎勵金會讓教師們非常不高興,原因之一是職場上幾乎沒見過「負數」的獎勵金,把錢收回去可能會被認為「不公平」

  1. 快大考前,用低成本的簡訊提醒孩子的家長,相較於另一半完全沒有收到任何通知的家長,有收到簡訊的家長的孩子,提升數學測驗成績的程度相當於額外多上一個月的課,而且程度位於最後 ¼ 的孩子學生受益最多,這就是推力造成的效果

  1. 如何在日常生活中運用行為經濟學:

步驟

說明

① 細心觀察

  • 行為經濟學始於簡單的觀察。

  • 當傳統智慧犯錯,推翻它的第一步就是睜大雙眼看看週遭的世界,看清它的真實樣貌,而非其他人一廂情願認為世界該是什麼模樣。

② 搜集數據

  • 如馬克・吐溫所言:「讓你陷入困境的並非是你所不知道的事物,而是你自以為知道的錯誤判斷。」

  • 一般人之所以過度自信,是因為他們向來懶得費新紀錄自己過去的錯誤預測,可怕的認知偏誤更是助紂為虐。抵禦過度自信的唯一方法,就是有系統地搜集數據,特別是那些能夠證明自己錯誤的數據

③ 勇於表達

  • 1977 年,超過 500 人因在跑道上飛機相撞意外中喪命,就是因為荷蘭航空的副駕駛不敢質疑機長,也就是「老闆的權威」。

  • 有些時候即便對象是你的老闆,你依然必須挺身而出,對迫在眉睫災難發出警告。

  • 優秀的領導人能創造一個環境,讓員工覺得依證據來做決定必能獲得獎勵,無論最後的結果為何。理想的組織可鼓勵每個人細心觀察、搜集證據、毫無保留說出看法


2020/10/07

[閱讀筆記] Misbehaving: The Making of Behavioral Economics (不當行為, 3/4)

  1. 公平的觀念涉及「稟賦效應」。買賣雙方將他們已習慣的交易條件視為理所當然,任何條件上的退讓都是一種虧損。在買方針對以為都免費贈送的東西開始收費,或將之納為售價時,這種對於慣常交易條件擁有所有權的感覺,會變得格外真確。例如,原本只能站著吃的阿宗麵線,開始提供額外收費的座位,即便座位不一定舒適好坐,民眾確實認同公司有權獲取合理利潤,屬於民眾能接受的公平範圍。

  2. 以下兩種狀況,員工購買力是一樣的,但是得到的反應不同,只因「名目薪資降低被視為損失」,所以是不公平的,可是沒有跟上通膨卻是可接受的,因為名目薪資有增加。這就是為什麼有些經濟學家覺得央行應該在金融危機爆發之後,提高對通膨的容忍,即使只有 3% 的通膨率,都能讓企業有效降低實質薪資至足以加速就業復甦的速度。

  1. 對於需要長期經營常客的企業來說,公平形象具有特別高的價值,因為假如被顧客認定做法不公,將比其他公司面臨更多損失。任何率先採取違反公平原則的大公司,都得冒著競爭者不追隨其後的風險,若競爭者都追隨,最後顧客只能因為別無選擇而被迫接受。

  1. 經營任何行業都絕對不能忘記,無論市場需求有多高,都不能向顧客收取超乎商品或服務真實價值的費用,即使顧客願意多付些錢。例如,有人願意花 $2000 美元到 ABC 餐廳用餐,顧客離開後可能會覺得,食物挺不錯的,但不值得 $2000 美元,更重要的是,這樣的顧客不但不會在度上門,還可能向其他潛在顧客抱怨這次的用餐經驗。又如 Super Bowel 門票的搶手程度,確實能合理化高漲票價,但聯盟仍刻意維持合理票價,目的在培養「與球迷和商業夥伴間的持續關係」。

  2. 稟賦效應的實驗顯示,一般人傾向於保留已擁有的東西,部分原因是出自「損失規避」心態。一旦我擁有過那只馬克杯,我認為它是屬於我的了,放棄那只杯子將會是一種損失。

  3. 從物理學來說,靜止的物體會一直保持不動,除非環境出現變化,人們的行為舉止也一樣,他們會緊抱已擁有的東西,直到出現轉換的好理由,或甚至就算好理由出現也不想改變,這就是「維持現狀的偏誤」。

  1. 人是理性的嗎?看看投資人偏好發股利或發股票就可窺一二,上市公司也順應投資人偏好,大多選擇發放現金股利 (以下稅率以美國為例):

  1. 長久以來,基金會和捐贈基金都是以相同方式在運作,也就是「本金不動」,只花「收入」不份。所以他們傾向於持有發放大筆鼓勵的債券與股票,但是後來這些組織逐漸發現這項作法相當不智,於是調整成比較合理個堆定,例如將一定比例 (ex. 5%) 的三年移動平均捐贈資產價值投注於具有長期潛力的投資項目,而不再只看能拿到多少現金股利。這項證照調整讓捐贈基金得以投資於新的資產類別,譬如創投基金這類經常得等待多年才開始產生回報的標的。

  2. 在組織機構中,損失規避的自然傾向會因為獎懲制度而變本加厲。許多公司給創造巨大收益的人微薄獎賞,對於犯錯的人則予以開除。在這種環境條件下,就算是一開始並不特別害怕風險且願意把握任何賺錢契機的經理人,也會變成高度損失規避。如此一來,組織架構非但沒能解決問題,反而會讓事態變得更惡化。這樣的失敗通常會被描述為「責任歸咎於代理人」,他們未能做出讓公司利益最大化的決策,僅根據自己的利益行事,可是許多時候造成此狀況的罪魁禍首是老闆,而非員工

  3. 為了讓主管們願意承擔風險,公司有必要創造一個主管若作出經事前評估,可望帶來最大價值的決定時,便能獲得獎勵的環境,也就是說,根據決策當下所能得到的訊息而做的決定,即便結果是虧錢。然而後見之明的偏誤,使這樣的政策執行起來並不容易,老闆可能不記得他自己當初也覺得那是個好主意。

  4. 傻瓜委託人問題:許多時候,代理人做出不智決定時,行為不當者往往是委託人,而非代理人。行為不當的一方,未能創造出讓員工覺得可以承擔風險、不必擔心失敗後會被懲罰的環境

  5. 一般人越常檢視他們的投資組合,就越不願意承擔風險,因為你越常檢視,就越會看到損失,往往也變得更為小心謹慎。

  6. 股票不會只漲不跌,大家近年才親眼目睹股市狂跌 50%,這就是為什麼我認為投資人應隨著年紀增長,逐漸調降投資組合中股票比率

  7. 凱因斯在《就業、利息和貨幣通論》這本書提到:「現有投資獲利的每日波動,顯然是無關緊要的短暫現象,卻對市場產生過度、甚至荒謬的影響。例如冰淇淋公司的股價在夏季月份隨著銷售量而攀高,實在令人感到意外,因為在一個效率市場中,股價反映的是公司長期價值,照理說夏暑或寒冬都不該反映在股價估值上,像這種可預期的季節股價變化模式,在效率市場假說中市絕對不可以出現的。」

  8. 買進一檔市場還看不起眼的股票,其實是沒有問題的,只要其他投資人,之後能夠轉而認同你的看法即可,而且時機宜早不宜遲。

  9. 由於忽略或偏見而造成的股價低估,有可能持續很長的時間。同樣的道理也可套用於投資人一頭熱,或人為刺激造成的股價大漲。1990 年代的科技泡沫期間,當時的價值投資表現奇差無比,因為最昂貴的股票,也就是那些熱門的網路概念股漲個不停,把其他無趣的價值型股票遠遠甩在後面。

  10. 假設你建立了由一批高風險股票構成的投資組合,而且價格波動頻繁,但是如果每支成分股的價格變化互不影響,因為這些變化會互相抵銷,這個投資組合本身其實風險不大。然而,倘若不同股票的回報率會互相影響,表示他們傾向於一起上漲或下跌,這樣就是個相當冒險的投資組合,持有一籃子股票帶來分散風險的好處就會消失。

  11. 我們不應期望靠著精準掌握市場時機來大賺股市財,偵測泡沫的出現要比判斷泡沫何時破滅容易得多,想靠掌握市場時機來賺錢的投資者,成功機會微乎其微

  12. 散戶投資者比機構投資者更可能持有小公司股票,機構投資者之所以避開小公司股票,是因為小公司股票的交易量不足以提供大型投資者所需要的流動性

  13. 市場有時反應過度,有時反應不足,絕大多數採取積極策略的經理人都沒能打敗市場