ややこしい日付の書式について

2014/12/29 に発生した Twitter にログインできない問題が Rebuild で言及されていた。
Date header がおかしくなっていた原因として、暦週の基準年(ISO 8601) が用いられていたのではないかという話があった。

暦週の基準年

たとえば、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 です。
GregorianCalendar (Java Platform SE 7)

月曜始まりの場合、getMinimalDaysInFirstWeek() が 4 だと最初の週が 1998 年になる。

      1 2 3 4
5 6 7 8 9 10 11

日曜始まりの場合、getMinimalDaysInFirstWeek() が 4 だと二週目から 1998 年になる。

        1 2 3
4 5 6 7 8 9 10

上記をふまえ、1998/1/1 に日付の書式 yyyy を使うと最初の例の場合 1998、YYYY だと 1997 となる。

文字 日付日付または時刻のコンポーネント
y
Y 暦週の基準年

SimpleDateFormat (Java Platform SE 7)

Java でこれを確認すると、2014/12/29 は 2015 年になった。

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

public class Main {

    public static void main(String[] args) {

        Date date = null;
        try {
            date = DateFormat.getDateInstance().parse("2014/12/29");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        System.out.println(sdf.format(date));

        sdf.applyPattern("YYYY/MM/dd");
        System.out.println(sdf.format(date));
    }
}

出力結果:

2014/12/29
2015/12/29

上記の結果になるということは、月曜始まりの暦週の基準年(ISO 8601)が使われていたのだろう。

22 23 24 25 26 27 28
29 30 31 1 2 3 4

なお .NET の場合、ISO 8601 とは微妙に違う挙動をする。
ISO 8601 Week of Year format in Microsoft .Net

Imports System
Imports System.Globalization

Module Module1
    Sub Main()
        Dim cal As Calendar = New CultureInfo("en-US").Calendar
        Dim cwr As CalendarWeekRule = CalendarWeekRule.FirstFourDayWeek
        Dim dow As DayOfWeek = DayOfWeek.Monday

        Dim dt = New System.DateTime(2014, 12, 29)
        Console.WriteLine("{0}, {1}", cal.GetWeekOfYear(dt, cwr, dow), dt.Year)

        dt = New System.DateTime(2015, 1, 1)
        Console.WriteLine("{0}, {1}", cal.GetWeekOfYear(dt, cwr, dow), dt.Year)
    End Sub
End Module

出力結果:

53, 2014
1, 2015

上記の設定の場合、年初が木曜日を含むとその週の 1 月の GetWeekOfYear が 1 を返し、年初が金曜以降の場合、その週の 1 月の GetWeekOfYear は前年の最終週を返す。