Javaで夏時間を処理する

Javaでの夏時間の処理

1. 概要

Daylight Saving Time(DST)は、自然光をさらに1時間活用するために、夏の間に時計を進める方法です(暖房、照明、気分の向上など)。

これはseveral countriesによって使用され、日付とタイムスタンプを操作するときに考慮する必要があります。

このチュートリアルでは、さまざまな場所に応じてJavaでDSTを正しく処理する方法を説明します。

2. JREおよびDSTの可変性

まず、世界中のDSTゾーンがchange very oftenであり、それを調整する中央機関がないことを理解することが非常に重要です。

国、場合によっては都市でさえ、それを適用または取り消すかどうか、およびその方法を決定できます。

それが発生するたびに、変更はIANA Time Zone Databaseに記録され、更新は将来のrelease of the JREでロールアウトされます。

Java SE download pageで利用可能なJava Time Zone Updater Toolと呼ばれる公式のOracleツールを介したIn case it’s not possible to wait, we can force the modified Time Zone data containing the new DST settings into the JRE

3. 間違った方法:3文字のタイムゾーンID

JDK 1.1の時代、APIは3文字のタイムゾーンIDを許可していましたが、これによりいくつかの問題が発生しました。

まず、これは同じ3文字のIDが複数のタイムゾーンを参照できるためです。 たとえば、CSTは米国の場合があります。 「中部標準時」だけでなく、「中国標準時」もあります。 Javaプラットフォームは、そのうちの1つしか認識できませんでした。

別の問題は、標準タイムゾーンが夏時間をアカウントに取り入れないことです。 複数のエリア/地域/都市が同じ標準タイムゾーン内にローカルDSTを持つことができるため、標準時間はそれを監視しません。

下位互換性のため、it’s still possible to instantiate a java.util.Timezone with a three-letter ID. However, this method is deprecated and shouldn’t be used anymore.

4. 正しい方法:TZDBタイムゾーンID

JavaでDSTを処理する正しい方法は、特定のTZDBタイムゾーンIDを使用してTimezoneをインスタンス化することです。 “Europe/Rome”.

次に、これをjava.util.Calendarなどの時間固有のクラスと組み合わせて使用​​して、TimeZone’sの生のオフセット(GMTタイムゾーンへの)の適切な構成と自動DSTシフト調整を取得します。

正しいTimeZone:を使用すると、GMT+1からGMT+2へのシフト(2018年3月25日の午前2:00にイタリアで発生)が自動的に処理される方法を見てみましょう。

TimeZone tz = TimeZone.getTimeZone("Europe/Rome");
TimeZone.setDefault(tz);
Calendar cal = Calendar.getInstance(tz, Locale.ITALIAN);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ITALIAN);
Date dateBeforeDST = df.parse("2018-03-25 01:55");
cal.setTime(dateBeforeDST);

assertThat(cal.get(Calendar.ZONE_OFFSET)).isEqualTo(3600000);
assertThat(cal.get(Calendar.DST_OFFSET)).isEqualTo(0);

ご覧のとおり、ZONE_OFFSETは60分です(イタリアはGMT+1であるため)が、その時点ではDST_OFFSETは0です。

Calendarに10分を追加しましょう:

cal.add(Calendar.MINUTE, 10);

現在、DST_OFFSETも60分になり、国は現地時間をCET(中央ヨーロッパ時間)からCEST(中央ヨーロッパ夏時間)であるGMT+2に移行しました。 :

Date dateAfterDST = cal.getTime();

assertThat(cal.get(Calendar.DST_OFFSET))
  .isEqualTo(3600000);
assertThat(dateAfterDST)
  .isEqualTo(df.parse("2018-03-25 03:05"));

コンソールに2つの日付を表示すると、タイムゾーンも変更されます。

Before DST (00:55 UTC - 01:55 GMT+1) = Sun Mar 25 01:55:00 CET 2018
After DST (01:05 UTC - 03:05 GMT+2) = Sun Mar 25 03:05:00 CEST 2018

最後のテストとして、2つのDates、1:55と3:05の間の距離を測定できます。

Long deltaBetweenDatesInMillis = dateAfterDST.getTime() - dateBeforeDST.getTime();
Long tenMinutesInMillis = (1000L * 60 * 10);

assertThat(deltaBetweenDatesInMillis)
  .isEqualTo(tenMinutesInMillis);

予想どおり、距離は70分ではなく10分です。

TimeZoneLocaleを正しく使用することで、Dateを操作するときに発生する可能性のある一般的な落とし穴に陥らないようにする方法を見てきました。

5. 最良の方法:Java8日付/時刻API

これらのスレッドセーフではなく、常にユーザーフレンドリーなjava.utilクラスの操作は、特に互換性の懸念により適切にリファクタリングできなかったため、常に困難でした。

このため、Java 8ではまったく新しいパッケージjava.timeとまったく新しいAPIセットDate/Time API.が導入されました。これはISO中心で、完全にスレッドセーフであり、有名なライブラリJoda-に大きく影響を受けています。時間。

java.util.Datejava.time.LocalDateTimeの後継から始めて、この新しいクラスを詳しく見てみましょう。

LocalDateTime localDateTimeBeforeDST = LocalDateTime
  .of(2018, 3, 25, 1, 55);

assertThat(localDateTimeBeforeDST.toString())
  .isEqualTo("2018-03-25T01:55");

LocalDateTimeISO8601プロファイルにどのように準拠しているかを観察できます。これは、標準で広く採用されている日時表記です。

ZonesOffsetsを完全に認識していませんが、それがa fully DST-aware java.time.ZonedDateTimeに変換する必要がある理由です。

ZoneId italianZoneId = ZoneId.of("Europe/Rome");
ZonedDateTime zonedDateTimeBeforeDST = localDateTimeBeforeDST
  .atZone(italianZoneId);

assertThat(zonedDateTimeBeforeDST.toString())
  .isEqualTo("2018-03-25T01:55+01:00[Europe/Rome]");

ご覧のとおり、日付には2つの基本的な末尾の情報が組み込まれています。+01:00ZoneOffsetであり、[Europe/Rome]ZoneIdです。

前の例のように、10分を追加してDSTをトリガーしましょう。

ZonedDateTime zonedDateTimeAfterDST = zonedDateTimeBeforeDST
  .plus(10, ChronoUnit.MINUTES);

assertThat(zonedDateTimeAfterDST.toString())
  .isEqualTo("2018-03-25T03:05+02:00[Europe/Rome]");

繰り返しますが、時間とゾーンオフセットの両方が前方に移動し、同じ距離を維持していることがわかります。

Long deltaBetweenDatesInMinutes = ChronoUnit.MINUTES
  .between(zonedDateTimeBeforeDST,zonedDateTimeAfterDST);
assertThat(deltaBetweenDatesInMinutes)
  .isEqualTo(10);

6. 結論

夏時間とは何か、そしてJavaコアAPIのさまざまなバージョンでのいくつかの実用的な例を通してそれを処理する方法を見てきました。

Java 8以降で作業する場合、使いやすさとその標準のスレッドセーフな性質のおかげで、新しいjava.timeパッケージの使用が推奨されます。

いつものように、完全なソースコードはover on Githubで利用できます。