Think Twice
IT技術メモ | Javaのメモ
Created: 2023-05-17 / Updated: 2023-05-17

JavaでRFC3339の日付フォーマットを扱う


ちょっとだけJavaでRFC3339の日付フォーマットを使う機会があったのでメモしておきます。

目次


概要

基本的な流れは以下となります。

文字列→日付型 (parse)

日付の文字列から、日付型(OffsetDateTime)に変換します。

Copy
OffsetDateTime.parse(<日付文字列>, <日付フォーマット>); // => 戻りはOffsetDateTimeクラスのインスタンス

日付フォーマット型→文字列 (format)

日付型(OffsetDateTime)から、日付の文字列変換します。

Copy
<日付フォーマット型>.format(<日付フォーマット>); // => 戻りはString

時差変換

ある日付(日付型)のタイムゾーンを変換します。

Copy
<日付フォーマット型>.withOffsetSameInstant(<変換先のタイムゾーンオフセット>); // => 戻りは時差変換されたOffsetDateTimeクラスのインスタンス

ポイント

詳しくはサンプルコードを御覧ください。

サンプルコード

ソース構成

Copy
.
└── src/
    └── main/
        └── java/
            └── com/
                └── example/
                    └── app/
                        ├── Main.java
                        └── Rfc3339DateTimeConverter.java

ソース

Main.java
Copy
package com.example.app;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class Main {
    private Rfc3339DateTimeConverter converter;

    Main() {
        this.converter = new Rfc3339DateTimeConverter();
    }

    private void test(String dateString) {
        OffsetDateTime dateTime = this.converter.parse(dateString);
        String resultString = this.converter.format(dateTime, "yyyy/MM/dd HH:mm:ss.SSSSSSS");

        // UTCに変換
        OffsetDateTime utcDateTime = this.converter.changeTimeZoneOffset(dateTime, ZoneOffset.UTC);
        String resultStringUTC = this.converter.format(utcDateTime, "yyyy/MM/dd HH:mm:ss.SSSSSSS");

        // 日本時間(JST)に変換
        OffsetDateTime jstDateTime = this.converter.changeTimeZoneOffset(dateTime, ZoneOffset.ofHours(9));
        String resultStringJST = this.converter.format(jstDateTime, "yyyy/MM/dd HH:mm:ss.SSSSSSS");

        System.out.println("元:" + dateString + " => 無加工:" + resultString + " (UTCに変換:" + resultStringUTC + ") (日本時間に変換:" + resultStringJST + ")");
    }

    public static void main(String[] args) {
        Main main = new Main();

        System.out.println("1. 標準");
        main.test("2023-05-17T00:12:34Z");
        main.test("2023-05-17T00:12:34.100Z");

        System.out.println("2. 末尾の0省略");
        main.test("2023-05-17T00:12:34.1Z");
        main.test("2023-05-17T00:12:34.12Z");
        main.test("2023-05-17T00:12:34.123Z");
        main.test("2023-05-17T00:12:34.1234Z");
        main.test("2023-05-17T00:12:34.12345Z");
        main.test("2023-05-17T00:12:34.123456Z");
        main.test("2023-05-17T00:12:34.1234567Z");
        main.test("2023-05-17T00:12:34.1000000Z");

        System.out.println("3. タイムゾーンオフセットを数値で表す");
        main.test("2023-05-17T00:12:34+09:00");
        main.test("2023-05-17T00:12:34.100+09:00");

        System.out.println("4. 末尾の0省略");
        main.test("2023-05-17T00:12:34.1+09:00");
        main.test("2023-05-17T00:12:34.12+09:00");
        main.test("2023-05-17T00:12:34.123+09:00");
        main.test("2023-05-17T00:12:34.1234+09:00");
        main.test("2023-05-17T00:12:34.12345+09:00");
        main.test("2023-05-17T00:12:34.123456+09:00");
        main.test("2023-05-17T00:12:34.1234567+09:00");
        main.test("2023-05-17T00:12:34.1000000+09:00");

        System.out.println("5. タイムゾーンずらし");
        main.test("2023-04-10T22:00:00Z");
        main.test("2023-04-10T23:00:00Z");
        main.test("2023-05-17T00:00:00Z");
        main.test("2023-05-17T01:00:00Z");
        main.test("2023-05-17T02:00:00Z");
        main.test("2023-05-17T03:00:00Z");
        main.test("2023-05-17T04:00:00Z");
        main.test("2023-05-17T05:00:00Z");
        main.test("2023-05-17T06:00:00Z");
        main.test("2023-05-17T07:00:00Z");
        main.test("2023-05-17T08:00:00Z");
        main.test("2023-05-17T09:00:00Z");

        // 以下エラーパターン
        // main.test("2023-05-17T00:12:34"); // タイムゾーンオフセット指定がないとエラー
        // main.test("2023-05-17T00:12:34+0900"); // +09:00 のようにコロンが入らないとエラー
    }

}
Rfc3339DateTimeConverter.java
Copy
package com.example.app;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

public class Rfc3339DateTimeConverter {

    private DateTimeFormatter formatter;

    public Rfc3339DateTimeConverter() {
        this.formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
    }

    /**
     * RFC3339形式の文字列パースして、OffsetDateTime型を返却
     * @param dateString
     * @return
     */
    public OffsetDateTime parse(String dateString) {
        OffsetDateTime dateTime = OffsetDateTime.parse(dateString, this.formatter);
        return dateTime;
    }

    /**
     * OffsetDateTime型を指定フォーマットの文字列に変換
     * @param dateTime
     * @param outPattern
     * @return
     */
    public String format(OffsetDateTime dateTime, String outPattern) {
        DateTimeFormatter outFormatter = DateTimeFormatter.ofPattern(outPattern);
        return dateTime.format(outFormatter);
    }

    /**
     * タイムゾーンオフセットを変更
     * @param toTimeZoneOffset
     * @return
     */
    public OffsetDateTime changeTimeZoneOffset(OffsetDateTime dateTime, ZoneOffset toTimeZoneOffset) {
        OffsetDateTime newDateTime = dateTime.withOffsetSameInstant(toTimeZoneOffset);
        return newDateTime;
    }

}

実行結果

Copy
1. 標準
元:2023-05-17T00:12:34Z => 無加工:2023/05/17 00:12:34.0000000 (UTCに変換:2023/05/17 00:12:34.0000000) (日本時間に変換:2023/05/17 09:12:34.0000000)
元:2023-05-17T00:12:34.100Z => 無加工:2023/05/17 00:12:34.1000000 (UTCに変換:2023/05/17 00:12:34.1000000) (日本時間に変換:2023/05/17 09:12:34.1000000)
2. 末尾の0省略
元:2023-05-17T00:12:34.1Z => 無加工:2023/05/17 00:12:34.1000000 (UTCに変換:2023/05/17 00:12:34.1000000) (日本時間に変換:2023/05/17 09:12:34.1000000)
元:2023-05-17T00:12:34.12Z => 無加工:2023/05/17 00:12:34.1200000 (UTCに変換:2023/05/17 00:12:34.1200000) (日本時間に変換:2023/05/17 09:12:34.1200000)
元:2023-05-17T00:12:34.123Z => 無加工:2023/05/17 00:12:34.1230000 (UTCに変換:2023/05/17 00:12:34.1230000) (日本時間に変換:2023/05/17 09:12:34.1230000)
元:2023-05-17T00:12:34.1234Z => 無加工:2023/05/17 00:12:34.1234000 (UTCに変換:2023/05/17 00:12:34.1234000) (日本時間に変換:2023/05/17 09:12:34.1234000)
元:2023-05-17T00:12:34.12345Z => 無加工:2023/05/17 00:12:34.1234500 (UTCに変換:2023/05/17 00:12:34.1234500) (日本時間に変換:2023/05/17 09:12:34.1234500)
元:2023-05-17T00:12:34.123456Z => 無加工:2023/05/17 00:12:34.1234560 (UTCに変換:2023/05/17 00:12:34.1234560) (日本時間に変換:2023/05/17 09:12:34.1234560)
元:2023-05-17T00:12:34.1234567Z => 無加工:2023/05/17 00:12:34.1234567 (UTCに変換:2023/05/17 00:12:34.1234567) (日本時間に変換:2023/05/17 09:12:34.1234567)
元:2023-05-17T00:12:34.1000000Z => 無加工:2023/05/17 00:12:34.1000000 (UTCに変換:2023/05/17 00:12:34.1000000) (日本時間に変換:2023/05/17 09:12:34.1000000)
3. タイムゾーンオフセットを数値で表す
元:2023-05-17T00:12:34+09:00 => 無加工:2023/05/17 00:12:34.0000000 (UTCに変換:2023/05/16 15:12:34.0000000) (日本時間に変換:2023/05/17 00:12:34.0000000)
元:2023-05-17T00:12:34.100+09:00 => 無加工:2023/05/17 00:12:34.1000000 (UTCに変換:2023/05/16 15:12:34.1000000) (日本時間に変換:2023/05/17 00:12:34.1000000)
4. 末尾の0省略
元:2023-05-17T00:12:34.1+09:00 => 無加工:2023/05/17 00:12:34.1000000 (UTCに変換:2023/05/16 15:12:34.1000000) (日本時間に変換:2023/05/17 00:12:34.1000000)
元:2023-05-17T00:12:34.12+09:00 => 無加工:2023/05/17 00:12:34.1200000 (UTCに変換:2023/05/16 15:12:34.1200000) (日本時間に変換:2023/05/17 00:12:34.1200000)
元:2023-05-17T00:12:34.123+09:00 => 無加工:2023/05/17 00:12:34.1230000 (UTCに変換:2023/05/16 15:12:34.1230000) (日本時間に変換:2023/05/17 00:12:34.1230000)
元:2023-05-17T00:12:34.1234+09:00 => 無加工:2023/05/17 00:12:34.1234000 (UTCに変換:2023/05/16 15:12:34.1234000) (日本時間に変換:2023/05/17 00:12:34.1234000)
元:2023-05-17T00:12:34.12345+09:00 => 無加工:2023/05/17 00:12:34.1234500 (UTCに変換:2023/05/16 15:12:34.1234500) (日本時間に変換:2023/05/17 00:12:34.1234500)
元:2023-05-17T00:12:34.123456+09:00 => 無加工:2023/05/17 00:12:34.1234560 (UTCに変換:2023/05/16 15:12:34.1234560) (日本時間に変換:2023/05/17 00:12:34.1234560)
元:2023-05-17T00:12:34.1234567+09:00 => 無加工:2023/05/17 00:12:34.1234567 (UTCに変換:2023/05/16 15:12:34.1234567) (日本時間に変換:2023/05/17 00:12:34.1234567)
元:2023-05-17T00:12:34.1000000+09:00 => 無加工:2023/05/17 00:12:34.1000000 (UTCに変換:2023/05/16 15:12:34.1000000) (日本時間に変換:2023/05/17 00:12:34.1000000)
5. タイムゾーンずらし
元:2023-04-10T22:00:00Z => 無加工:2023/04/10 22:00:00.0000000 (UTCに変換:2023/04/10 22:00:00.0000000) (日本時間に変換:2023/04/11 07:00:00.0000000)
元:2023-04-10T23:00:00Z => 無加工:2023/04/10 23:00:00.0000000 (UTCに変換:2023/04/10 23:00:00.0000000) (日本時間に変換:2023/04/11 08:00:00.0000000)
元:2023-05-17T00:00:00Z => 無加工:2023/05/17 00:00:00.0000000 (UTCに変換:2023/05/17 00:00:00.0000000) (日本時間に変換:2023/05/17 09:00:00.0000000)
元:2023-05-17T01:00:00Z => 無加工:2023/05/17 01:00:00.0000000 (UTCに変換:2023/05/17 01:00:00.0000000) (日本時間に変換:2023/05/17 10:00:00.0000000)
元:2023-05-17T02:00:00Z => 無加工:2023/05/17 02:00:00.0000000 (UTCに変換:2023/05/17 02:00:00.0000000) (日本時間に変換:2023/05/17 11:00:00.0000000)
元:2023-05-17T03:00:00Z => 無加工:2023/05/17 03:00:00.0000000 (UTCに変換:2023/05/17 03:00:00.0000000) (日本時間に変換:2023/05/17 12:00:00.0000000)
元:2023-05-17T04:00:00Z => 無加工:2023/05/17 04:00:00.0000000 (UTCに変換:2023/05/17 04:00:00.0000000) (日本時間に変換:2023/05/17 13:00:00.0000000)
元:2023-05-17T05:00:00Z => 無加工:2023/05/17 05:00:00.0000000 (UTCに変換:2023/05/17 05:00:00.0000000) (日本時間に変換:2023/05/17 14:00:00.0000000)
元:2023-05-17T06:00:00Z => 無加工:2023/05/17 06:00:00.0000000 (UTCに変換:2023/05/17 06:00:00.0000000) (日本時間に変換:2023/05/17 15:00:00.0000000)
元:2023-05-17T07:00:00Z => 無加工:2023/05/17 07:00:00.0000000 (UTCに変換:2023/05/17 07:00:00.0000000) (日本時間に変換:2023/05/17 16:00:00.0000000)
元:2023-05-17T08:00:00Z => 無加工:2023/05/17 08:00:00.0000000 (UTCに変換:2023/05/17 08:00:00.0000000) (日本時間に変換:2023/05/17 17:00:00.0000000)
元:2023-05-17T09:00:00Z => 無加工:2023/05/17 09:00:00.0000000 (UTCに変換:2023/05/17 09:00:00.0000000) (日本時間に変換:2023/05/17 18:00:00.0000000)

補足(OKの形式、エラーになる形式)

OKの形式

Copy
"2023-05-17T00:12:34Z"           // OK(Z=UTC, 秒以下なし)
"2023-05-17T00:12:34.100Z"       // OK(ミリ秒まで指定)
"2023-05-17T00:12:34.1Z"         // OK(1/10秒まで指定)
"2023-05-17T00:12:34.1234567Z"   // OK(秒以下7桁もOK)
"2023-05-17T00:12:34+09:00"      // OK(Zではなく、タイムゾーンで指定)
"2023-05-17T00:12:34.100+09:00"  // OK(Zではなく、タイムゾーンで指定、ミリ秒まで指定)

エラーの形式

Copy
"2023-05-17T00:12:34"            // NG(タイムゾーンオフセット指定がないとエラー)
"2023-05-17T00:12:34+0900"       // NG(+09:00のようにコロンが入らないとエラー)

参考

参照

参考サイト

Special thanks


  1. 日付フォーマットはSimpleDateFormatと同様のものが使えるものと思われます。 ↩︎