デブのDEV日記

港区で働くデブによるDEV(DEVELOPE)やデブ飯の記録。そんな美味いならデブになっても構わない。

SimpleDateFormatのYYYYとyyyyの違いに要注意

クライアントより連絡があり、本番環境である年月日表示が合わないと調査依頼があった。
画面とCSVで確かに生年月日が合っていない。。
CSV側がの西暦がずれている。
「2015/12/28」と表記されなければいけないものが「2016/12/28」と表示されている。
1年余分に年が進んでしまっているではないか!

調べてみたところ実装で怪しい箇所を見つけた。
SimpleDateFormatに指定している年のフォーマットが大文字の「YYYY」となっているではないか。

SimpleDateFormatのJavaDocを見ると

Y 暦週の基準年

と記載があった。この記載は、Java7以降からかな。
暦週の基準年ってなんやねん、っていう事でググってみました。

暦週の基準年は、WEEK_OF_YEAR のサイクルと同期がとられます。最初の週と最後の週の間にあるすべての週 (両端の週を含む) の暦週の基準年は、同じ値になります。したがって、暦週の基準年が同じでも、最初の日と最後の日では暦年の値が異なる場合があります。

たとえば、1998 年 1 月 1 日は木曜日です。getFirstDayOfWeek() が MONDAY で getMinimalDaysInFirstWeek() が 4 (ISO 8601 規格に準拠した設定) の場合、1998 年の第 1 週は 1997 年 12 月 29 日に始まり 1998 年 1 月 4 日で終わります。 暦年が 1997 年の最後の 3 日については、暦週の基準年が 1998 になります。 ただし、getFirstDayOfWeek() が SUNDAY の場合、1998 年の第 1 週は 1998 年 1 月 4 日に始まり 1998 年 1 月 10 日に終わります。1998 年の最初の 3 日間は 1997 年の第 53 週に入り、それらの日の暦週の基準年は 1997 です。

f:id:devdebu:20170516142501p:plain

↓ここ注目です。
暦年が 1997 年の最後の 3 日については、暦週の基準年が 1998 になります。

1997年でも年末の最後の3日は「YYYY」を指定すると1998年が返却される模様です。

以下のコードで検証してみました。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatterTest {

	public static void main(String[] args) throws ParseException {

		System.out.println("===== 2015年 ====================================");

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
		Date date = sdf.parse("2015/12/26");
		SimpleDateFormat sdf1 = new SimpleDateFormat("YYYY/MM/dd");
		String date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy/MM/dd");
		String date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/27");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/28");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/29");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/30");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/31");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		System.out.println("===== 2012年 ====================================");
		date = sdf.parse("2012/12/29");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2012/12/30");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);
		System.out.println("=================================================");

	}
}

実行結果

===== 2015年 ====================================
YYYY→2015/12/26
yyyy→2015/12/26
YYYY→2016/12/27
yyyy→2015/12/27
YYYY→2016/12/28
yyyy→2015/12/28
YYYY→2016/12/29
yyyy→2015/12/29
YYYY→2016/12/30
yyyy→2015/12/30
YYYY→2016/12/31
yyyy→2015/12/31
===== 2012年 ====================================
YYYY→2012/12/29
yyyy→2012/12/29
YYYY→2013/12/30
yyyy→2012/12/30
=================================================

2015年は、12/26まで正しく表示されていますが、12/27になると急に年がずれはじめてます。
2012年は、12/29まで正しく表示されていますが、12/30になると急に年がずれはじめてます。

YYYYを指定すると1月1日が所属する週にいる12月の日付に関して、+1年で年を返すようです。

年末年始返上になりかねない嫌がらせのような仕様です・・・