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 です。
↓ここ注目です。
暦年が 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年で年を返すようです。
年末年始返上になりかねない嫌がらせのような仕様です・・・