7.4. システム時刻


7.4.1. Overview

アプリケーションのシステム日時は、実行環境のOSのシステム時刻ではなく、開発・運用側で任意の日時に設定可能であることが望ましい。
本節では、共通ライブラリが提供しているシステム時刻を取得するためのコンポーネントについて説明する。

7.4.1.1. 共通ライブラリから提供しているコンポーネントについて

共通ライブラリでは、システム時刻を取得するためのコンポーネントを提供している。

共通ライブラリから提供しているコンポーネントは、terasoluna-gfw-commonから以下の2つの機能を提供している。

  • java.util.Dateを生成するDate Factory

  • java.time.Clockを生成するClock Factory

コンポーネントのクラス図を以下に示す。

Class Diagram of Factory

7.4.1.1.1. 提供しているインタフェース

以下に、terasoluna-gfw-commonのコンポーネントとして提供しているインタフェースについて説明する。

インタフェース

説明

org.terasoluna.gfw.common.date.
ClassicDateFactory

Javaから提供されている以下のクラスのインスタンスをシステム時刻として取得するためのインタフェース。

  • java.util.Date

  • java.sql.Timestamp

  • java.sql.Date

  • java.sql.Time

共通ライブラリでは、本インタフェースの実装クラスとして以下のクラスを提供している。

  • org.terasoluna.gfw.common.date.DefaultClassicDateFactory

org.terasoluna.gfw.common.time.
ClockFactory

Javaから提供されている以下のクラスのインスタンスをシステム時刻として取得するためのインタフェース。

  • java.time.Clock

共通ライブラリでは、本インタフェースの実装クラスとして以下のクラスを提供している。

  • org.terasoluna.gfw.common.time.DefaultClockFactory

  • org.terasoluna.gfw.common.time.ConfigurableClockFactory

  • org.terasoluna.gfw.common.time.ConfigurableAdjustClockFactory

  • org.terasoluna.gfw.common.time.JdbcClockFactory

  • org.terasoluna.gfw.common.time.JdbcAdjustClockFactory

本ガイドラインでは、本インタフェースに対応する実装クラスを使用することを推奨する。


7.4.1.1.2. ClockFactoryの実装クラス

ClockFactoryインタフェースの実装クラスをbean定義ファイルに定義し、ClockFactoryのインスタンスをJavaクラスにインジェクションして使用する。

実装クラスは使用用途に応じて、以下から選択する。

クラス名

概要

備考

org.terasoluna.gfw.common.time.
DefaultClockFactory
システム・デフォルトのClockを取得する。

org.terasoluna.gfw.common.time.
ConfigurableClockFactory
指定した固定日時から生成したClockを取得する。

org.terasoluna.gfw.common.time.
ConfigurableAdjustClockFactory
システム・デフォルトのClockに指定した差分を加算したClockを取得する。


org.terasoluna.gfw.common.time.
JdbcClockFactory
DBに登録した固定の時刻から生成したClockを取得する。
このクラスを使用するためには、日時を管理するためのテーブルが必要である。
org.terasoluna.gfw.common.time.
JdbcAdjustClockFactory
システム・デフォルトのClockにDBに登録した差分を加算したClockを取得する。
このクラスを使用するためには、差分値を管理するためのテーブルが必要である。
日付を変更する要件が無い場合はDefaultClockFactoryを使用することを推奨する。
運用対処などで日時を変更する必要がある場合は、JdbcAdjustClockFactoryを使用し、通常時は差分値を0に設定することを推奨する。

Caution

本番環境でJdbcAdjustClockFactoryを使用する場合は、通常時は参照するテーブルの差分値が0となっていることを必ず確認すること。


7.4.2. How to use

ClockFactoryインタフェースの実装クラスをbean定義ファイルに定義し、ClockFactoryのインスタンスをJavaクラスにインジェクションして使用する。ClockFactoryからClockを取得する方法については、Clockの取得を参照されたい。
実装クラスを設定するbean定義ファイルは、環境ごとに切り替えられるように、[projectName]-envに定義することを推奨する。
ClockFactoryを利用することにより、bean定義ファイルの設定を変更するだけで、ソースを変更せずに日時の変更が可能となる。
Switch ClockFactory in the profile

7.4.2.1. ClockFactoryのBean定義

以下に、ClockFactoryの実装クラスの定義方法について説明する。

7.4.2.1.1. サーバーのシステム・デフォルトClockを取得する

org.terasoluna.gfw.common.time.DefaultClockFactoryを使用する。

bean定義ファイル

  • [projectname]EnvConfig.java

    // (1)
    @Bean
    public DefaultClockFactory clockFactory() {
        return new DefaultClockFactory();
    }
    

    項番

    説明

    (1)
    DefaultClockFactoryをbean定義する。

7.4.2.1.2. 指定した固定日時から生成したClockを取得する

org.terasoluna.gfw.common.time.ConfigurableClockFactoryを使用する。
ConfigurableClockFactoryは、以下の3つのコンストラクタを提供している。

7.4.2.1.2.1. 日付フォーマットの設定なし

bean定義ファイル

  • [projectname]EnvConfig.java

    // (1)
    @Bean
    public ConfigurableClockFactory clockFactory() {
        ConfigurableClockFactory factory = new ConfigurableClockFactory("2012-09-11T02:25:15"); // (2)
        return factory;
    }
    

    項番

    説明

    (1)
    ConfigurableClockFactoryをbean定義する。
    (2)
    localDateTimeStringプロパティに、固定日時を設定する。
    日付フォーマットを設定しない場合、日付フォーマットはISO_LOCAL_DATE_TIMEが適用される。
    そのため、ISO_LOCAL_DATE_TIMEに合わせ”2012-09-11T02:25:15“を固定日時として設定している。

7.4.2.1.2.2. 日付フォーマットの設定あり

bean定義ファイル

  • [projectname]EnvConfig.java

    // (1)
    @Bean
    public ConfigurableClockFactory clockFactory() {
        ConfigurableClockFactory factory = new ConfigurableClockFactory("2012/09/11 02:25:15", "uuuu/MM/dd HH:mm:ss"); // (2)(3)
        return factory;
    }
    

    項番

    説明

    (1)
    ConfigurableClockFactoryをbean定義する。
    (2)
    localDateTimeStringプロパティに、固定日時を設定する。
    この例では日付フォーマットを”uuuu/MM/dd HH:mm:ss“として定義しているため、指定した日付フォーマットに合わせ”2012/09/11 02:25:15“を固定日時として設定している。
    (3)
    patternプロパティに、日付フォーマットを設定する。

7.4.2.1.2.3. Styleでの指定

bean定義ファイル

  • [projectname]EnvConfig.java

    // (1)
    @Bean
    public ConfigurableClockFactory clockFactory() {
        ConfigurableClockFactory factory = new ConfigurableClockFactory("2012/09/11 02:25:15", FormatStyle.MEDIUM, FormatStyle.MEDIUM); // (2)(3)
        return factory;
    }
    

    項番

    説明

    (1)
    ConfigurableClockFactoryをbean定義する。
    (2)
    localDateTimeStringプロパティに、固定日時を設定する。
    この例では日付および時間のStyleをFormatStyle.MEDIUMとして定義しているため、指定した日付スタイルに合わせ”2012/09/11 02:25:15“を固定日時として設定している。
    (3)
    dateStyleプロパティに日付のStyle、timeStyleプロパティに時間のStyleを設定する。
    入力可能な値はFormatStyleを参照されたい。

7.4.2.1.3. システム・デフォルトのClockに対し差分を追加したClockを取得する

org.terasoluna.gfw.common.time.ConfigurableAdjustClockFactoryを使用する。

bean定義ファイル

  • [projectname]EnvConfig.java

    // (1)
    @Bean
    public ConfigurableAdjustClockFactory clockFactory() {
        ConfigurableAdjustClockFactory factory = new ConfigurableAdjustClockFactory(1, ChronoUnit.DAYS); // (2)(3)
        return factory;
    }
    

    項番

    説明

    (1)
    ConfigurableAdjustClockFactoryをbean定義する。
    (2)
    adjustedValueプロパティに、差分値を設定する。日付時間単位は(3)で決定する。
    (3)
    adjustedValueUnitプロパティに、日付時間単位を設定する。
    この例ではDAYSを設定しているため、Factoryで生成されるClockはシステムのデフォルトClockに1日加算した日時となる。
    設定できる日付時間単位についてはChronoUnitを参照されたい。

    Note

    adjustedValueUnitプロパティには推定期間を設定することはできない。(例えば、MONTHSYEARSなどは設定できない。)

    推定期間を設定した場合、以下の様な例外が出力される。

    java.time.temporal.UnsupportedTemporalTypeException: Unit must not have an estimated duration
    

    推定期間かどうかはChronoUnitのJavaDocを参照されたい。


7.4.2.1.4. DBに登録した固定の時刻から生成したClockを取得する

org.terasoluna.gfw.common.time.JdbcClockFactoryを使用する。

bean定義ファイル

  • [projectname]EnvConfig.java

    // (1)
    @Bean
    public JdbcClockFactory clockFactory(
            @Qualifier("dataSource") DataSource dataSource) {
        JdbcClockFactory factory = new JdbcClockFactory(dataSource, "SELECT now FROM system_date"); //(2)(3)
        return factory;
    }
    

    項番

    説明

    (1)
    JdbcClockFactoryをbean定義する。
    (2)
    固定時刻を管理するためのテーブルが存在するデータソース(javax.sql.DataSource)を指定する。
    (3)
    固定時刻を取得するためのSQLを設定する。

テーブル設定例

以下のようにテーブルを作成し、レコードを追加する必要がある。

CREATE TABLE system_date(now timestamp NOT NULL);
INSERT INTO system_date(now) VALUES (to_timestamp('2013/01/01 01:01:01.000', 'yyyy/MM/dd HH24:mi:ss.ms'));

レコード番号

now

1

2013/01/01 01:01:01.000


7.4.2.1.5. システム・デフォルトのClockに対しDBから取得した差分を追加したClockを取得する

org.terasoluna.gfw.common.time.JdbcAdjustClockFactoryを使用する。

bean定義ファイル

  • [projectname]EnvConfig.java

    // (1)
    @Bean
    public JdbcAdjustClockFactory clockFactory(
            @Qualifier("dataSource") DataSource dataSource) {
        JdbcAdjustClockFactory factory = new JdbcAdjustClockFactory(dataSource, "SELECT diff FROM operation_date", ChronoUnit.SECONDS); // (2)(3)(4)
        return factory;
    }
    

    項番

    説明

    (1)
    JdbcAdjustClockFactoryをbean定義する。
    (2)
    差分値を管理するためのテーブルが存在するデータソース(javax.sql.DataSource)を指定する。
    (3)
    差分値を取得するためのSQLを設定する。
    (4)
    日付時間単位を設定する。
    この例ではSECONDSを設定しているため、Factoryで生成されるClockはシステムのデフォルトClockにadjustedValueQuery秒加算した日時となる。
    設定できる日付時間単位についてはChronoUnitを参照されたい。

    Note

    adjustedValueUnitプロパティには推定期間を設定することはできない。(例えば、MONTHSYEARSなどは設定できない。)

    推定期間を設定した場合、以下の様な例外が出力される。

    java.time.temporal.UnsupportedTemporalTypeException: Unit must not have an estimated duration
    

    推定期間かどうかはChronoUnitのJavaDocを参照されたい。


テーブル設定例

以下のようにテーブルを作成し、レコードを追加する必要がある。

CREATE TABLE operation_date(diff bigint NOT NULL);
INSERT INTO operation_date(diff) VALUES (-86400);

レコード番号

diff

1

-86400

ここでは差分値を設定しているだけで、日付時間単位はBeanのpropertyで設定されている。
上記例ではSECONDSを設定しているため、-86400秒=1日前の値となる。

Note

上記のSQLはPostgreSQL用である。Oracleの場合はBIGINTの代わりにNUMBER(19)を使用すればよい。


7.4.2.2. Clockの取得

ClockFactoryは、時刻を固定するfixedメソッドと、時刻を刻むtickメソッドを提供している。
以下に、ClockFactoryを使用してシステム日時を取得する例を示す。
@Inject
ClockFactory clockFactory;  // (2)

public void clockSample() {

    Clock fixedClock1 = clockFactory.fixed();  // (3)
    Clock fixedClock2 = clockFactory.fixed(ZoneId.of("Asia/Tokyo"));  // (4)
    Clock tickClock1 = clockFactory.tick();  // (5)
    Clock tickClock2 = clockFactory.tick(ZoneOffset.UTC);  // (6)

    LocalDateTime fixedLocalDateTime1 = LocalDateTime.now(fixedClock1); // (7)
    LocalDateTime fixedLocalDateTime2 = LocalDateTime.now(fixedClock2); // (8)
    LocalDateTime tickLocalDateTime1 = LocalDateTime.now(tickClock1); // (9)
    LocalDateTime tickLocalDateTime2 = LocalDateTime.now(tickClock2); // (10)

    // omitted
}

項番

説明

(2)
ClockFactoryを利用するクラスにインジェクションする。
(3)
fixedメソッドを呼び出した瞬間の日時で固定したClockを取得する。
タイムゾーンはシステムのデフォルト・タイムゾーンが使用される。
(4)
fixedメソッドを呼び出した瞬間の日時で固定したClockを取得する。
タイムゾーンは指定したタイムゾーンが使用される。
この例ではJSTを指定している。
(5)
tickメソッドを呼び出した瞬間の日時から時を刻むClockを取得する。
タイムゾーンはシステムのデフォルト・タイムゾーンが使用される。
(6)
tickメソッドを呼び出した瞬間の日時から時を刻むClockを取得する。
タイムゾーンは指定したタイムゾーンが使用される。
この例ではUTCを指定している。
(7)
取得したClockを引数に指定しシステム日時を取得する。
Clock内のタイムスタンプが固定されているため、now(clock)メソッドは毎回同じ日時で返却する。
(8)
取得したClockを引数に指定しシステム日時を取得する。
Clock内のタイムスタンプが固定されているため、now(clock)メソッドは毎回同じ日時で返却する。
(9)
取得したClockを引数に指定しシステム日時を取得する。
Clock内のタイムスタンプは固定されていないため、now(clock)メソッドは呼び出される度に違う日時で返却する。
(10)
取得したClockを引数に指定しシステム日時を取得する。
Clock内のタイムスタンプは固定されていないため、now(clock)メソッドは呼び出される度に違う日時で返却する。