動作確認version: Java11

なぜ現在日時を変える必要があるか

JUnitレベルではなく実際にWebサーバを起動してイベントの開始・終了の制御等をテストしたいとき、サーバの現在日時を変えたくなる。しかしLinuxサーバの時間を変えるのはおすすめできない。最近のカーネルは、安易に時間を変えてしばらくするとクラッシュすることが多い。具体的にはCentOS6時代は問題なかったがCentOS7は問題がよく起こる。

単純にLinuxサーバの時間を変えるのではなくfaketimeというツールを使えば問題なく日時を変えられるが、今回はJavaだけで安全に日時を変える方法について書く。

現在日時をずらす

Clock.offsetを使用すれば、現在日時を今から n秒後, n分後, n時間後, n日後などずらして取得することができる。

static class LocalDateTimeOffset {
    public static LocalDateTime now() {
        Clock clock = Clock.offset(Clock.systemDefaultZone(), Duration.ofSeconds(60));
        return LocalDateTime.now(clock);
    }
}

public static void main(String[] args) {
    System.out.println(LocalDateTime.now());       // 2020-03-02T23:36:50.052898
    System.out.println(LocalDateTimeOffset.now()); // 2020-03-02T23:37:50.053739
}

現在日時を固定する

時間をずらすのではなく常に同じ日時をさしてほしい用途の場合にはClock.fixedが使える。

static class LocalDateTimeFixed {
    public static LocalDateTime now() {
        Clock clock = Clock.fixed(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneId.systemDefault());
        return LocalDateTime.now(clock);
    }
}

public static void main(String[] args) {
    LocalDateTime now = LocalDateTimeFixed.now();
    System.out.println(now); // 2020-01-01T10:00
}

プログラム全体で日本時間だけ扱っているとしても、Clock.fixedを使う場合はUTC時間に頭の中で変換してから指定したい日時を設定しなければならないため、ちょっとめんどくさい。タイムゾーンを気にせず指定日時を設定するには工夫がいる。

タイムゾーンを気にせず現在日時を固定する

LocalDateTimeにはもともとタイムゾーンの概念がないので、InstantにパースさせるのではなくLocalDateTimeにパースさせればタイムゾーンを気にしないで現在日時が固定できる。

static class LocalDateTimeFixed {
    public static LocalDateTime now() {
        LocalDateTime localDateTime = LocalDateTime.parse("2020-01-01T10:00:00");
        Instant instant = localDateTime.toInstant(OffsetDateTime.now().getOffset());
        Clock clock = Clock.fixed(instant, ZoneId.systemDefault());
        return LocalDateTime.now(clock);
    }
}

public static void main(String[] args) {
    LocalDateTime now = LocalDateTimeFixed.now();
    System.out.println(now); // 2020-01-01T10:00
}