きっかけ
自分が携わるWEBシステムのある画面で2020年12月28日に作業していると、他メンバーから「"2021年12月28日"と1年先で表示されている画面があるんだけど?」と言われたこと
先に結論を言うと 日付のフォーマット形式で
"yyyy/MM/dd"
ではなく、**"YYYY/MM/dd"
**で記述していたことが原因だった。
検証コード
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
y: 年
Y: 暦週の基準年
yとYの違いはJavaDocを読むと上記のような記述があります。
"暦週の基準年"は簡単にいうと、1月1日と同じ週の日は1月1日の年とするという意味になります。
曜日始まりを日曜日とすると、たとえば下記のような年の扱いです。
また、暦週の基準年で関連する要素として以下のものがあります。
- 曜日始まり (日曜日始まり? 月曜日始まり?)
- 年の最初の週に必要な最小日数
- ロケール
曜日始まり
Calendar.html#getFirstDayOfWeek
年の最初の週に必要な最小日数
Calendar.html#getMinimalDaysInFirstWeek
firstDayOfWeekとminimalDaysInFirstWeekを明示的に指定しないと、デフォルトロケールに応じたデフォルト値が使用されます。
日本_日本語がデフォルトロケール ("ja_JP")だとすると下記のようになります。
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年と同じ年に属するというように扱われるということみたいです。
Calendar calendar = Calendar.getInstance();
calendar.setFirstDayOfWeek(Calendar.MONDAY); // 月曜日始まりとする
calendar.setMinimalDaysInFirstWeek(4); // ISO8601基準とする
あとがき
こういった記事を書くきっかけと与えてくれた同じ開発メンバーに感謝!
参考
一番最初に参考としたサイト
SimpleDateFormatのYYYYとyyyyの違いに要注意 - デブのDEV日記
フランスだけ値がなぜ異なるのか?を調べた時の参考サイト
1週間の始まりは月曜日:フランス語の暦(11)
フランスの曜日(語源など) - 生きたフランス語見聞録