2024/11/28

AWS Step FunctionsのPassステートだけで入力された日時のタイムゾーンを変更する

AWS Step Functionsは各種ステートと入出力をうまくつかうことで、簡単なデータ変換であればLambdaを使わずに実装できます。2024/11/22にリリースされた変数機能とJSONata形式のサポートにより、その自由度の幅はさらに広がり、また実装がしやすくなりました。

JSONata形式のサポートに関するリリースでは正規表現を使った変換や条件分岐について触れられていましたが、この記事では日付のタイムゾーンの変換を実装します。

実装方法

以下の入力がステートの入力として渡されることを考えます。

{
  "version": "0",
  "id": "5267474e-f44e-429c-bc4e-8cfabexample",
  "detail-type": "Scheduled Event",
  "source": "aws.scheduler",
  "account": "123456789012",
  "time": "2024-11-27T16:55:16Z",
  "region": "ap-northeast-1",
  "resources": [
    "arn:aws:scheduler:ap-northeast-1:123456789012:schedule/default/test"
  ],
  "detail": "{}"
}

この中の time をGMTからJSTに変換するPassステートの定義は以下のとおりです。JSONata形式を利用する場合、Passステートからの出力はParametersやResultではなくOutputを使用します。

"ConvertTimezoneState": {
  "Type": "Pass",
  "Output": {
    "time": "{% $toMillis($states.input.time) ~> $fromMillis('[Y0001]-[M01]-[D01]T[H01]:[m]:[s][Z]', '+0900') %}"
  },
  "Next": "NextState"
}

実際にStep Functionsとして実行した様子が以下の画像です。

Step Functionsのスクリーンショット

タイムゾーン変換後の日時が出力されました。

説明

JSONata形式で書かれた部分を改めて見てみましょう。

$toMillis($states.input.time) ~> $fromMillis('[Y0001]-[M01]-[D01]T[H01]:[m]:[s][Z]', '+0900')

入力された日時形式の文字列を一度Unix時間に変換した後、形式とタイムゾーンを指定して出力しています。

日時操作関数

関数 $toMillis(timestamp, [picture]) は、timestamp に渡された文字列をUnix時間(ミリ秒)に変換します。オプションとして第2引数に入力する文字列の日時フォーマットを指定できます(省略した場合はISO 8601形式として解釈される)。今回はこの関数に、ステートの入力から time プロパティを渡しています($states.input.time)。

関数 $fromMillis(number [, picture [, timezone]]) はその逆で、 number に渡されたUnix時間(ミリ秒)を日付形式の文字列に変換します。オプションとして第2引数に出力時のフォーマットを、第3引数にタイムゾーンを指定できます(±HHMM 形式)。

上記2つの関数はドキュメントDate/Time functionsに記載されています。日付の入出力のフォーマットはXPath/XQuery function fn:format-dateTimeで定義されているものが使用できます。

チェーン演算子

これら2つの関数を繋いでいる ~>チェーン演算子で、「1つ目の関数の出力」を「2つ目の関数の第1引数」に渡すという処理を簡潔に記述することができます。つまり、上記のJSONata部分は以下の記述と同等です。

$fromMillis($toMillis($states.input.time), '[Y0001]-[M01]-[D01]T[H01]:[m]:[s][Z]', '+0900')

関数を繋げてデータの変換を記述する際に便利です。

バリエーション

結果を変数へ格納する

出力ではなく変数へ格納する場合は、OutputではなくAssignを使用します。変数を使うことで見通しが良くなるので、積極的に活用しましょう。

"ConvertTimezoneStateAndAssign": {
  "Type": "Pass",
  "Assign": {
    "time": "{% $toMillis($states.input.time) ~> $fromMillis('[Y0001]-[M01]-[D01]T[H01]:[m]:[s][Z]', '+0900') %}"
  },
  "Next": "NextState"
}

なお、上記の例ではOutputを指定していませんが、この場合は入力(GMTの日時)がそのまま出力されます。取り違えないようにご注意ください。

日付部分だけ使う

Athenaのクエリで日付が必要な場合など、日付部分だけ使用する場合は出力の形式を調整します。

$toMillis($states.input.time) ~> $fromMillis('[Y0001]-[M01]-[D01]', '+0900')

使い所

EventBridge Schedulerで何もInputを指定せずにStep Functionsのステートマシンを実行した場合、先に挙げた形式の入力がステートマシンに渡されます。

Inputを指定する場合でも、実行時間としてコンテキスト属性<aws.scheduler.scheduled-time>)を使用するケースは多いと思います。この記述方法でもタイムゾーンは指定できず、GMTが使用されます。

ローカルタイムゾーンを実行時間を処理の中で利用する場合、これまではLambdaでタイムゾーン変換することが多かったと思いますが、今後はPassステートを噛ませたり、Argumentsでデータ変換をすることで、その実装をスキップすることができます。

まとめ

JSONataを利用してタイムゾーンを変換する方法をご紹介しました。変数機能を含め、Step Functionsの実装がよりわかりやすく、より豊かになる嬉しいアップデートでした。ぜひご活用ください。

Share with Hatena Bookmark

関連記事