Java/Kotlin
PR

【Kotlin】RFC3339 UTC「Zulu」形式とエポックミリ秒の相互変換|Google Play Billing 対応

saratogax
記事内に商品プロモーションを含む場合があります

GooglePlayBilling でサブスクリプションの情報を取得する API のバージョンが変わりました。

これについては、以前紹介した通りです。

あわせて読みたい
【Kotlin】RFC3339 UTC「Zulu」形式とエポックミリ秒の相互変換|Google Play Billing 対応
【Kotlin】RFC3339 UTC「Zulu」形式とエポックミリ秒の相互変換|Google Play Billing 対応

ここで気になったのが日時の表記。

「RFC3339 UTC “Zulu” format」というのが気になりますね。

今回は、この日付のフォーマット作成やエポックミリ秒への変換を試していきます。

日付フォーマットの仕様

GooglePlayBilling の API の仕様を確認すると、以下の説明が記載されています。

A timestamp in RFC3339 UTC “Zulu” format, with nanosecond resolution and up to nine fractional digits. Examples: “2014-10-02T15:01:23Z” and “2014-10-02T15:01:23.045123456Z”.

purchases.subscriptionsv2

これまで通りエポックミリ秒でもいいのですが、レスポンスの中身を見ただけで日時がわかるというメリットはあります。

エポックミリ秒だと、日付フォーマットに変換しないとわからないですからね。

あわせて読みたい
エポックミリ秒・エポック秒⇔日時 自動変換ツール|13桁/10桁の違い・JavaScript・Excel対応
エポックミリ秒・エポック秒⇔日時 自動変換ツール|13桁/10桁の違い・JavaScript・Excel対応

現在日時のZuluフォーマット

Kotlin で現在日時を Zulu 形式にするなら、常に UTC を指す java.time.Instant を使うのが確実です。

Instant.now().toString() は、末尾に Z が付いた RFC3339(ISO 8601)形式の文字列をそのまま返してくれます。

import java.time.Instant

fun main() {
    val zulu = Instant.now().toString()
    println(zulu)
}

このコードを日本時間(JST)の 2022 年 11 月 16 日 19 時頃に実行すると、UTC では 9 時間前の 10 時台になるため、結果は以下のようになります(ミリ秒以下の桁数は実行環境によって前後します)。

2022-11-16T10:08:20.669Z

【注意】LocalDateTime に 'Z' を付けても UTC にはなりません

古い記事やサンプルでよく見かけるのが、次のように LocalDateTime.now() をフォーマットして、末尾に 'Z' を付ける書き方です。

// アンチパターン:ローカル時刻に 'Z' を付けているだけ
val zulu = LocalDateTime.now().format(
    DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
)

この 'Z' はパターン内の単なる文字リテラルなので、実際にはローカル時刻(例:JST)の末尾に Z を貼り付けているだけです。

つまり、本当は UTC ではないのに「UTC(Z)」を名乗る、9 時間ずれた文字列になってしまいます。

ミリ秒 3 桁固定など書式を細かく指定したい場合は、フォーマッタに UTC を指定して Instant を渡せば、表示の Z と実際のタイムゾーンが一致します。

import java.time.Instant
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter

fun main() {
    val fmt = DateTimeFormatter
        .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
        .withZone(ZoneOffset.UTC)
    println(fmt.format(Instant.now()))
}

Zuluフォーマットからエポックミリ秒への変換

では、GooglePlayBilling からレスポンスされた日付項目を、エポックミリ秒に変換する方法を考えてみます。

ここでは上記で使用した現在日時をサンプルとします。

Instant.parse() は末尾 Z 付きの文字列を UTC として正しく解釈し、toEpochMilli() でエポックミリ秒に変換できます。

import java.time.Instant

fun main() {
    val zulu = Instant.now().toString()
    val em = Instant.parse(zulu).toEpochMilli()
    println(em)
}

結果は以下の通りです。

1668593300669

まとめ

RFC3339 UTC 「Zulu」 形式の日付フォーマットの変換について紹介してきました。

相互変換ができるようにしておけば、日付の扱いで困ることもありませんね。

自分のシステムだけであれば日付フォーマットは統一しておけますが、外部システムが絡むと厄介。

これが正解かと言われると未知数なので、Java の新しい日付クラスの情報も追いかけておきたいところです。

ABOUT ME
saratoga
saratoga
フリーランスエンジニア
仕事にも趣味にも IT を駆使するフリーランスエンジニア。技術的な TIPS や日々の生活の中で深堀りしてみたくなったことを備忘録として残していきます。
記事URLをコピーしました