[Java] 2020年12月28日が2021年12月28日ってフォーマットされるんだけど？

# きっかけ
自分が携わるWEBシステムのある画面で2020年12月28日に作業していると、他メンバーから「"2021年12月28日"と1年先で表示されている画面があるんだけど？」と言われたこと

先に結論を言うと 日付のフォーマット形式で
```"yyyy/MM/dd"``` ではなく、**```"YYYY/MM/dd"```**で記述していたことが原因だった。

# 検証コード
```java:検証コード
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
  public static void main(String[] args) throws Exception {
    final SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyy-MM-dd");
    final SimpleDateFormat YYYYMMdd = new SimpleDateFormat("YYYY-MM-dd");
    
    Date date = yyyyMMdd.parse("2020-12-28");
    
    System.out.println(yyyyMMdd.format(date));
    //=> "2020-12-28"
    System.out.println(YYYYMMdd.format(date));
    //=> "2021-12-28"
  }
}
```

# 解説
> [SimpleDateFormat - JavaDoc](https://docs.oracle.com/javase/jp/8/docs/api/java/text/SimpleDateFormat.html)
> y: 年
> Y: 暦週の基準年
yとYの違いはJavaDocを読むと上記のような記述があります。

"暦週の基準年"は簡単にいうと、1月1日と同じ週の日は1月1日の年とするという意味になります。
曜日始まりを日曜日とすると、たとえば下記のような年の扱いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136617/f480733b-a717-9b54-ff40-34f45503d624.png)

また、暦週の基準年で関連する要素として以下のものがあります。

* 曜日始まり　（日曜日始まり？　月曜日始まり？）
* 年の最初の週に必要な最小日数
* ロケール

曜日始まり
[Calendar.html#getFirstDayOfWeek](https://docs.oracle.com/javase/jp/8/docs/api/java/util/Calendar.html#getFirstDayOfWeek--)
年の最初の週に必要な最小日数
[Calendar.html#getMinimalDaysInFirstWeek](https://docs.oracle.com/javase/jp/8/docs/api/java/util/Calendar.html#getMinimalDaysInFirstWeek--)

firstDayOfWeekとminimalDaysInFirstWeekを明示的に指定しないと、デフォルトロケールに応じたデフォルト値が使用されます。
日本_日本語がデフォルトロケール ("ja_JP")だとすると下記のようになります。

```java:デフォルトロケールとロケールごとの値の違い
import java.util.Calendar;
import java.util.Locale;

public class Main {
  public static void main(String[] args) {
    Locale defaultLocale = Locale.getDefault();
    System.out.println(defaultLocale);
    //=> ja_JP
    
    // "ja_JP" 日本
    {
      Locale locale = new Locale("ja","JP");
      Calendar calendar = Calendar.getInstance(locale);
      // 1: SUNDAY, 2: MONDAY, 3: TUESDAY, 4: WEDNESDAY, 5: THURSDAY, 6: FRIDAY
      System.out.println(calendar.getFirstDayOfWeek());
      // => 1 (SUNDAY)
      System.out.println(calendar.getMinimalDaysInFirstWeek());
      // => 1 (1日)
    }
    
    // "en_US" アメリカ
    {
      Locale locale = new Locale("en","US");
      Calendar calendar = Calendar.getInstance(locale);
      System.out.println(calendar.getFirstDayOfWeek());
      // => 1 (SUNDAY)
      System.out.println(calendar.getMinimalDaysInFirstWeek());
      // => 1 (1日)
    }
    
    // "fr_FR" フランス
    {
      Locale locale = new Locale("fr","FR");
      Calendar calendar = Calendar.getInstance(locale);
      System.out.println(calendar.getFirstDayOfWeek());
      // => 2 (MONDAY)
      System.out.println(calendar.getMinimalDaysInFirstWeek());
      // => 4 (4日 ISO8601基準)
    }
  }
}
```

下記のように明示的な指定をしない限りは、日曜日始まりで1月1日を含む週を1月1年と同じ年に属するというように扱われるということみたいです。

```java:明示的な指定例
Calendar calendar = Calendar.getInstance();
calendar.setFirstDayOfWeek(Calendar.MONDAY); // 月曜日始まりとする
calendar.setMinimalDaysInFirstWeek(4);       // ISO8601基準とする
```

# あとがき
こういった記事を書くきっかけと与えてくれた同じ開発メンバーに感謝！

# 参考
一番最初に参考としたサイト
[SimpleDateFormatのYYYYとyyyyの違いに要注意 - デブのDEV日記](http://devo.hatenablog.com/entry/2017/05/16/151819)

フランスだけ値がなぜ異なるのか？を調べた時の参考サイト
[1週間の始まりは月曜日：フランス語の暦（11）](https://furansu-go.com/11-monday/)
[フランスの曜日（語源など） - 生きたフランス語見聞録](https://deslys.blog.fc2.com/blog-entry-331.html)