Step Functions で JSONata だけを使って日付を和暦に変換してみた
はじめに
最近 Step Functions を使う機会があり、せっかくなので去年対応した JSONata を使っていました。
JSONata のドキュメントに目を通していると多機能なことを知り、「色々できるなぁ」と思っている中でゴールデンウィークに突入したので、暇つぶしとして和暦変換をやってみました。
できたもの
入力で年月日を受け取り、和暦を出力する Pass ワークフローだけのステートマシンです。
Lambda を使わず JSONata だけで和暦に変換できました。
Step Functions 実行時の入力に以下のように年月日を記述した JSON を使用します。
ここではクラスメソッドの創業日である 2004年7月7日で試してみました。
{
"year": 2004,
"month": 7,
"day": 7
}
出力では wareki
に JSONata の結果を入れるようにしたため以下のようになります。
wareki.wareki
に和暦の平成16年、wareki.warekiAbbr
に略称の H16 が入っています。
{
"wareki": {
"wareki": "平成16年",
"warekiAbbr": "H16",
"year": 2004,
"month": 7,
"day": 7
}
}
このステートマシンの ASL はこちら。
出力の JSON で wareki
に対し JSONata のコードの結果を入れるようにしています。
{
"Comment": "A description of my state machine",
"StartAt": "Pass",
"States": {
"Pass": {
"Type": "Pass",
"Output": {
"wareki": "{% (\n $toWareki := function($year, $month, $day){(\n $eras := [\n {\"name\": \"令和\", \"abbr\": \"R\", \"start\": {\"year\": 2019, \"month\": 5, \"day\": 1}},\n {\"name\": \"平成\", \"abbr\": \"H\", \"start\": {\"year\": 1989, \"month\": 1, \"day\": 8}},\n {\"name\": \"昭和\", \"abbr\": \"S\", \"start\": {\"year\": 1926, \"month\": 12, \"day\": 25}},\n {\"name\": \"大正\", \"abbr\": \"T\", \"start\": {\"year\": 1912, \"month\": 7, \"day\": 30}},\n {\"name\": \"明治\", \"abbr\": \"M\", \"start\": {\"year\": 1868, \"month\": 1, \"day\": 25}}\n ];\n\n $compareDate := function($y1, $m1, $d1, $y2, $m2, $d2) {\n $y1 > $y2 ? 1 : \n $y1 < $y2 ? -1 :\n $m1 > $m2 ? 1 :\n $m1 < $m2 ? -1 :\n $d1 > $d2 ? 1 :\n $d1 < $d2 ? -1 : 0\n };\n\n $era := $eras[\n $compareDate($year, $month, $day, start.year, start.month, start.day) >= 0\n ][0];\n\n $era = null ? {\n \"wareki\": \"不明\",\n \"warekiAbbr\": \"?\",\n \"year\": $year\n } : (\n /* 元号年の計算 */\n $eraYear := $year - $era.start.year + 1;\n \n /* 元号初年(元年)の処理 */\n $displayYear := $eraYear = 1 ? \"元\" : $eraYear;\n \n /* 結果の生成 */\n {\n \"wareki\": $era.name & $displayYear & \"年\",\n \"warekiAbbr\": $era.abbr & $displayYear,\n \"year\": $year,\n \"month\": $month,\n \"day\": $day\n }\n )\n )};\n\n $toWareki($states.input.year, $states.input.month, $states.input.day);\n)\n %}"
},
"End": true
}
},
"QueryLanguage": "JSONata"
}
JSONata のコード自体は以下の通りです。
(
$toWareki := function($year, $month, $day){(
$eras := [
{"name": "令和", "abbr": "R", "start": {"year": 2019, "month": 5, "day": 1}},
{"name": "平成", "abbr": "H", "start": {"year": 1989, "month": 1, "day": 8}},
{"name": "昭和", "abbr": "S", "start": {"year": 1926, "month": 12, "day": 25}},
{"name": "大正", "abbr": "T", "start": {"year": 1912, "month": 7, "day": 30}},
{"name": "明治", "abbr": "M", "start": {"year": 1868, "month": 1, "day": 25}}
];
$compareDate := function($y1, $m1, $d1, $y2, $m2, $d2) {
$y1 > $y2 ? 1 :
$y1 < $y2 ? -1 :
$m1 > $m2 ? 1 :
$m1 < $m2 ? -1 :
$d1 > $d2 ? 1 :
$d1 < $d2 ? -1 : 0
};
$era := $eras[
$compareDate($year, $month, $day, start.year, start.month, start.day) >= 0
][0];
$era = null ? {
"wareki": "不明",
"warekiAbbr": "?",
"year": $year
} : (
/* 元号年の計算 */
$eraYear := $year - $era.start.year + 1;
/* 元号初年(元年)の処理 */
$displayYear := $eraYear = 1 ? "元" : $eraYear;
/* 結果の生成 */
{
"wareki": $era.name & $displayYear & "年",
"warekiAbbr": $era.abbr & $displayYear,
"year": $year,
"month": $month,
"day": $day
}
)
)};
$toWareki($states.input.year, $states.input.month, $states.input.day);
)
マネジメントコンソール上で JSONata を記述時に使用できる編集モーダルで、改行込みで入力したため、ASL では改行コードが含まれたワンライナーで表されています。
さいごに
JSONata だけで和暦変換を行なってみました。
JSONata には文字列操作や日時や日付を処理する関数が用意されている以外にも高階関数や、チューリング完全な関数型プログラミングができるらしいので、Step Functions を触る方は JSONata のドキュメントを読んで Lambda レスなステートマシンを目指してみてはいかがでしょうか。