ええ?イェーイ!

Susan Sarandon
Susan Sarandonオリジナル
2024-11-17 14:51:02880ブラウズ

Java の日付パターンの「Y」文字と「y」文字の違いは何かご存知ですか?この記事では、間違った日付形式がどのようにエラーを引き起こす可能性があるかを見ていきます。また、突然のタイムトラベルからあなたを守る、Java 用の新しい V6122 診断ルールも紹介します。

YYYY? yyyy!

導入

大きな TODO ノートのほこりを払った後、特に興味深いケースを見つけました。この記事のコメント投稿者は、潜在的な問題を強調しました。

コメント
ところで、ここにアイデアがあります。Java の SimpleDateFormat は、新年のサプライズを演出できます。小文字で書かれた年は、大文字で書かれた年と同じではありません。 2021.12.27 ~ 2021.12.31 の日付の「YYYY」は 2022 年であり、一部の人が予想しているような 2021 年ではないため、今年の最後の週に突然自分が未来にいることに気づくかもしれません。

この問題の真相を見ていきましょう。

問題分析

日付の形式

SimpleDateFormat が何であるかを忘れてしまった場合は、ここで知識を磨き直すことができます。

日付を保存して表示するには、日付が特定のパターンに従う必要があることがよくあります。 SimpleDateFormat は、指定されたパターンに従って日付を簡単にフォーマットできるようにするクラスです。 SimpleDateFormat は、書式設定に加えて、文字列を解析して日付オブジェクトに変換することもできます。

Java が提供できるのはそれだけですか? SimpleDateFormat に加えて、DateTimeFormatter クラスも日付をフォーマットできます。

これら 2 つのクラスの使用例を示します。

SimpleDateFormat で日付をフォーマットする場合:

public static void main(String[] args) {
    Date date = new Date("2024/12/31");
    var dateFormatter = new SimpleDateFormat("dd-MM-yyyy");
    System.out.println(dateFormatter.format(date));
}

コンソールには次の内容が表示されます:

2024 年 12 月 31 日

ここで何が起こったのですか?

日付があり、それを特定の形式で保存/表示したいと考えています。これを実現するには、パターン文字列をコンストラクターに渡して SimpleDateFormat オブジェクトを作成します。日付はこのパターンに従って正確にフォーマットされます。 format メソッドは、フォーマットされた日付の文字列表現を返します。これはまさに私たちが望んでいたものです。

これも同じですが、DateTimeFormatter:
を使用します。

public static void main(String[] args) {
    LocalDate date = LocalDate.of(2024, 12, 31);
    var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
    System.out.println(formatter.format(date));
}

コンソールには次の内容が表示されます:

2024 年 12 月 31 日

同じ手順と結果ですが、わずかな違いがあります。DateTimeFormatter を使用すると、TemporalAccessor インターフェイスを実装するクラスによって表される日付のみをフォーマットできます。たとえば、LocalDateLocalDateTime が含まれます。 SimpleDateFormat は、Date クラスのオブジェクトのみをフォーマットします。

コメントに戻りましょう。日付パターンで「y」の代わりに「Y」を使用すると、結果は本当に変わりますか?

コードを見てみましょう:

public static void main(String[] args) {
    Date date = new Date("2024/12/31");
    var dateFormatter = new SimpleDateFormat("dd-MM-yyyy");
    System.out.println(dateFormatter.format(date));
}

コンソールには次の内容が表示されます:

2025 年 12 月 31 日

おっと。 DateTimeFormatter に関しても同じですか?

コードは次のとおりです:

public static void main(String[] args) {
    LocalDate date = LocalDate.of(2024, 12, 31);
    var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
    System.out.println(formatter.format(date));
}

コンソールには次の内容が表示されます:

2025 年 12 月 31 日

私たちは一年後の未来に旅行しました。何が起こっているかを見る時が来ました。

問題の本質

私の最初のステップは、SimpleDateFormat クラスのドキュメントを調べることでした。以下は、日付パターンが関心のあるアルファベット文字をどのように解釈するかを説明する表の一部です。

Letter Date or Time Component Presentation Examples
y Year Year 1996; 96
Y Week year Year 2009; 09

一年で一週間?説明しましょう。

週年は、その年の週番号に基づく年です。それは何を意味し、なぜ重要ですか?

特定のタスクでは、その年の週のシーケンス番号が重要です。週のシーケンス番号を決定するには、どの週を最初に考慮するかを決定する必要があります。これは、ある年から別の年への移行では、多くの場合、両方の年にまたがる数週間が生じるためです。では、今週が何年に属するかをどうやって判断できるのでしょうか? ISO-8601 標準はこれを規制しています。

この基準によれば、年の最初の週は次の条件を満たさなければなりません:

  • 週の最初の日は月曜日です;
  • 1 年の 1 週間の最小日数は 4 日です。

これから、簡単なルールを導き出すことができます。木曜日が 1 月に該当する場合、その週はその年の最初とみなされます。

実際の例でこれをさらに明確にできます。

例から、2024 年 12 月 31 日という日付を取り上げてみましょう。以下は、日付 (2024 年 12 月から 2025 年 1 月) を含む週のカレンダーの抜粋です:

Mo Tu We Th Fr Sa Su
30 31 1 2 3 4 5

今週は上記の条件を満たすため (1 月が 5 日ある)、2025 年の最初の週とみなされます。したがって、「Y」指定子を使用すると、2025 年が取得されます。

ご想像のとおり、DateTimeFormatter の場合もまったく同じです。

私たちは未来だけでなく過去にも旅行できることに注意することが重要です。

別の日付、2027 年 1 月 1 日を考えてみましょう。

2027 年の第 1 週には 1 月 1 日が含まれますか?もう一度、カレンダーを見てみましょう。

以下は、日付 (2026 年 12 月から 2027 年 1 月) を含む週のカレンダーの抜粋です。

Mo Tu We Th Fr Sa Su
28 29 30 31 1 2 3

今週は 1 月の日が 3 日しかないため (要件によると最初の週は 4 日になるはずです)、2026 年の最後の週としてカウントされます。つまり、「Y」を使用してフォーマットされた日付です。キャラクターは、2026 年を見せてくれます。

これが証拠です。コードを見てください:

public static void main(String[] args) {
    Date date = new Date("2024/12/31");
    var dateFormatter = new SimpleDateFormat("dd-MM-yyyy");
    System.out.println(dateFormatter.format(date));
}

コンソールには次の内容が表示されます:

2026 年 1 月 1 日

さて、問題を詳しく分析しました。また、PVS-Studio Java アナライザーに専用の診断ルールを追加するのは非常に興味深いことであることがわかりました。

実際のプロジェクトでのエラー

診断ルールが記述されたので、それをテストします。

アナライザーの開発中、テスト段階の 1 つに回帰テストの実行が含まれます。このプロセスに関する記事があります。つまり、新しい診断ルールを追加すると、オープンソース プロジェクトの大規模なプールが分析され、新しいレポートと参照レポートが比較されます。

この診断ルールの場合、アナライザーはいくつかのプロジェクトに対して新しい警告を発行しました。それらを見てみましょう。

弾む城

このプロジェクトでは、診断ルールが指すコード フラグメントは同一であるため、そのうちの 1 つだけを示します。

コードを見てみましょう:

public static void main(String[] args) {
    LocalDate date = LocalDate.of(2024, 12, 31);
    var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
    System.out.println(formatter.format(date));
}

PVS-Studio の警告:

V6122 'Y' (週年) パターンの使用が検出されました: おそらく 'y' (年) を使用することが意図されていました。 SkeinParameters.java 246

まず、GitHub を見てみました。それが本当にバグで、開発者がすでにそれを発見して修正をコミットしていたとしたらどうなるでしょうか?まさにそれが起こったのです。ここにコミットへのリンクがありますので、確認してください。パターン内のすべての「Y」(週年) 文字は「y」(年) に置き換えられました。

なぜコミットが比較的昔に行われたのか不思議に思うかもしれません。説明しましょう。私たちの回帰テストは、特定のオープンソース プロジェクトの品質を継続的に管理することを目的としたものではありません。タスクは、新しい診断ルールが追加されたときにレポートがどのように変化するかを確認することです。アナライザーに問題があることを示すため、古い警告が消えたり、新しいエラーが表示されたりすることはありません。したがって、チェックされたコードは同じである必要があります。

オープングロク

次に、診断ルールがトリガーされた 2 番目のプロジェクトを見てみましょう。

PVS-Studio の警告:

V6122 'Y' (週年) パターンの使用が検出されました: おそらく 'y' (年) を使用することが意図されていました。リポジトリ情報.java 77

コード:

public static void main(String[] args) {
    Date date = new Date("2024/12/31");
    var dateFormatter = new SimpleDateFormat("dd-MM-YYYY");
    System.out.println(dateFormatter.format(date));
}

前のプロジェクトと同様に、何が起こっているのかを確認するために急いでコミットにアクセスしました。まず、リファクタリングの結果、フィールドが派生クラス Repository に移動したことは注目に値します (コミットへのリンクはここにあります)。さらに検索すると、修正を含むコミットが見つかりました。日付パターンの「Y」文字は「y」に置き換えられました:

public static void main(String[] args) {
    LocalDate date = LocalDate.of(2024, 12, 31);
    var formatter = DateTimeFormatter.ofPattern("dd-MM-YYYY");
    System.out.println(formatter.format(date));
}

つまり、ここでもエラーでした。

結論

PVS-Studio では、コミュニティからのフィードバックを歓迎します。 Habr ユーザーからのコメントに対処した後、いくつかの有用な診断ルールを追加して Java アナライザーを強化しました。共有したいご意見がございましたら、この記事のコメント欄でお待ちしております。

ところで、診断ルールは 10 月の 7.33 リリースで導入されました。したがって、当社のアナライザーを試してみたい場合は、このリンクを使用してください。

それだけです。ここで話を終わらせましょう。一年前(または逆)の突然の旅行に驚かないことを願っています。

以上がええ?イェーイ!の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。