1. 序章

このチュートリアルでは、SimpleDateFormatクラスの詳細なツアーに参加します。

単純なインスタンス化とフォーマットスタイル、およびクラスが処理ロケールとタイムゾーンに対して公開する便利なメソッドを見ていきます。

2. 単純なインスタンス化

まず、新しいSimpleDateFormatオブジェクトをインスタンス化する方法を見てみましょう。

4つの可能なコンストラクターがありますが、名前に合わせて、物事を単純に保ちましょう。 開始する必要があるのは、必要な日付パターンの文字列表現だけです。

次のように、ダッシュで区切られた日付パターンから始めましょう。

"dd-MM-yyyy"

これにより、現在の日付、現在の月、そして最後に現在の年から始まる日付が正しくフォーマットされます。 簡単な単体テストで新しいフォーマッターをテストできます。 新しいSimpleDateFormatオブジェクトをインスタンス化し、既知の日付を渡します。

SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
assertEquals("24-05-1977", formatter.format(new Date(233345223232L)));

上記のコードでは、フォーマッターはミリ秒を l ongとして人間が読める日付(1977年5月24日)に変換します。

2.1. ファクトリメソッド

SimpleDateFormat は、日付フォーマッターをすばやく作成するための便利なクラスですが、DateFormatクラスのファクトリメソッドを使用することをお勧めします getDateFormat() getDateTimeFormat() getTimeFormat()

上記の例は、これらのファクトリメソッドを使用する場合は少し異なります。

DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT);
assertEquals("5/24/77", formatter.format(new Date(233345223232L)));

上記からわかるように、フォーマットオプションの数は、DateFormatクラスフィールドによって事前に決定されています。 これにより、フォーマットで使用可能なオプションが大幅に制限されます。そのため、この記事ではSimpleDateFormatを使用します。

2.2. スレッドセーフ

SimpleDateFormatJavaDocは明示的に次のように述べています。

日付形式は同期されません。 スレッドごとに個別のフォーマットインスタンスを作成することをお勧めします。 複数のスレッドが同時にフォーマットにアクセスする場合は、外部で同期する必要があります。

したがって、SimpleDateFormatインスタンスはスレッドセーフではありません。並行環境では慎重に使用する必要があります。

この問題を解決するための最良のアプローチは、ThreadLocalと組み合わせて使用することです。 このように、各スレッドは独自のSimpleDateFormat インスタンスで終了し、共有がないため、プログラムはスレッドセーフになります:

private final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal
  .withInitial(() -> new SimpleDateFormat("dd-MM-yyyy"));

withInitial メソッドの引数は、SimpleDateFormatインスタンスのサプライヤーです。 ThreadLocal がインスタンスを作成する必要があるたびに、このサプライヤーを使用します。

次に、ThreadLocalインスタンスを介してフォーマッターを使用できます。

formatter.get().format(date)

ThreadLocal.get()メソッドは、最初に現在のスレッドの SimpleDateFormat を初期化し、次にそのインスタンスを再利用します。

各インスタンスの使用を1つの特定のスレッドに制限するため、この手法をスレッド制限と呼びます。

同じ問題に取り組むには、他に2つのアプローチがあります。

  • 同期されたブロックまたはReentrantLockを使用する
  • SimpleDateFormatの使い捨てインスタンスをオンデマンドで作成する

これらのアプローチはどちらもお勧めできません。前者は競合が多いときにパフォーマンスに大きな打撃を与え、後者は多くのオブジェクトを作成し、ガベージコレクションに圧力をかけます。

Java 8以降、新しいDateTimeFormatterクラスが導入されました。 新しいDateTimeFormatterクラスは不変でスレッドセーフです。Java8以降を使用している場合は、新しいDateTimeFormatterクラスを使用することをお勧めします。

3. 日付の解析

SimpleDateFormatおよびDateFormatを使用すると、日付をフォーマットできるだけでなく、操作を逆にすることもできます。 parse メソッドを使用すると、日付の文字列表現を入力して、同等の日付オブジェクトを返すことができます。

SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
Date myDate = new Date(233276400000L);
Date parsedDate = formatter.parse("24-05-1977");
assertEquals(myDate.getTime(), parsedDate.getTime());

ここで重要なのは、コンストラクターで提供されるパターンは、parseメソッドを使用して解析された日付と同じ形式である必要があるということです。

4. 日時パターン

SimpleDateFormat は、日付をフォーマットするときにさまざまなオプションを提供します。 完全なリストはJavaDocsで入手できますが、より一般的に使用されるオプションのいくつかを調べてみましょう。

手紙 日付コンポーネント
M 12; 12月
y 94
d 23; 月曜日
H 時間 03
m 57

日付コンポーネントによって返される出力は、文字列内で使用される文字数にも大きく依存します。 たとえば、6月を考えてみましょう。 日付文字列を次のように定義すると、次のようになります。

"MM"

次に、結果は番号コード–06として表示されます。 ただし、日付文字列に別のMを追加すると、次のようになります。

"MMM"

次に、結果のフォーマットされた日付は、単語Junとして表示されます。

5. ロケールの適用

SimpleDateFormat クラスもは、コンストラクターが呼び出されたときに設定されるさまざまなロケールをサポートします。

日付をフランス語でフォーマットして、これを実践してみましょう。 Locale.FRANCE をコンストラクターに提供しながら、SimpleDateFormatオブジェクトをインスタンス化します。

SimpleDateFormat franceDateFormatter = new SimpleDateFormat("EEEEE dd-MMMMMMM-yyyy", Locale.FRANCE);
Date myWednesday = new Date(1539341312904L);
assertTrue(franceDateFormatter.format(myWednesday).startsWith("vendredi"));

特定の日付、水曜日の午後を指定することにより、franceDateFormatterが日付を正しくフォーマットしたことを表明できます。 新しい日付は正しくVendrediで始まります-水曜日のフランス語!

コンストラクターのロケールバージョンには少し注意が必要ですが、多くのロケールがサポートされていますが、完全なカバレッジは保証されていません。 ロケールを確実にカバーするために、DateFormatクラスのファクトリメソッドを使用することをお勧めします。

6. タイムゾーンの変更

SimpleDateFormatDateFormatクラスを拡張するため、setTimeZoneメソッドを使用してタイムゾーンを操作することもできます。 これを実際に見てみましょう:

Date now = new Date();

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE dd-MMM-yy HH:mm:ssZ");

simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Europe/London"));
logger.info(simpleDateFormat.format(now));

simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
logger.info(simpleDateFormat.format(now));

上記の例では、同じDateを同じSimpleDateFormatオブジェクトの2つの異なるタイムゾーンに提供しています。 また、タイムゾーンの違いを示すために、パターン文字列の最後に‘Z’文字を追加しました。 次に、形式のメソッドからの出力がユーザー用にログに記録されます。

実行を押すと、2つのタイムゾーンに関連する現在の時刻を確認できます。

INFO: Friday 12-Oct-18 12:46:14+0100
INFO: Friday 12-Oct-18 07:46:14-0400

7. 概要

このチュートリアルでは、SimpleDateFormatの複雑さについて深く掘り下げました。

SimpleDateFormat インスタンス化する方法と、パターン文字列が日付のフォーマットにどのように影響するかを見てきました。

出力文字列のロケールを変更してから、最終的にタイムゾーンを使用してを試してみました。

いつものように、完全なソースコードはGitHubにあります。