18

私はかなり奇妙な問題を抱えています。私はデンマークに住んでおり、2013 年の最初の週 (第 1 週) は 2012 年 12 月 31 日から始まり、7 日間続きます。

.NET によると、12 月 30 日は第 52 週、31 日は第 53 週、1 月 1 日は第 1 週です。

第 53 週は 1 日だけ続き、第 1 週は 6 日間続きます。明らかにこれは間違いであり (1 週間が 7 日未満である)、デンマークの文脈では間違いです。12 月 31 日は第 1 週であり、第 53 週ではありません。

次のコードは問題を示しています (CurrentCulture は "da-DK" です)。

    static void Main(string[] args)
    {
        //Here I get Monday
        DayOfWeek firstDayOfWeek = DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek;             
        //Here I get FirstFourDayWeek
        CalendarWeekRule weekRule = DateTimeFormatInfo.CurrentInfo.CalendarWeekRule; 

        DateTime date = new DateTime(2012,12,30);

        for (int i = 0; i <= 10; i++)
        {
            DateTime currentDate = date.AddDays(i);
            Console.WriteLine("Date: {0} WeekNumber: {1}",
                currentDate.ToShortDateString(),
                CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(currentDate, weekRule, firstDayOfWeek));
        }
        Console.ReadLine();
    }

何か間違ったことをしたのでしょうか、それとも .NET のバグですか? 後者の場合、週番号を正しく計算するための提案はありますか?

4

6 に答える 6

22

問題は、GetWeekOfYearメソッドが ISO 8601 を尊重していないことです。これは期待どおりですが、そうではありません。

FirstFourDayWeekを使用している間、ドキュメントには次のように記載されていることに注意してください。

FirstFourDayWeek 値に基づく最初の週は、4 ~ 7 日になる場合があります。

これは、すべての週が7日でなければならないという ISO 8601 規則に違反しています。

また:

ここに画像の説明を入力


次の方法を使用して、ISO 8601 に従って正しい週番号を取得できます。

int weekNumber(DateTime fromDate)
{
    // Get jan 1st of the year
    DateTime startOfYear = fromDate.AddDays(- fromDate.Day + 1).AddMonths(- fromDate.Month +1);
    // Get dec 31st of the year
    DateTime endOfYear = startOfYear.AddYears(1).AddDays(-1);
    // ISO 8601 weeks start with Monday 
    // The first week of a year includes the first Thursday 
    // DayOfWeek returns 0 for sunday up to 6 for saterday
    int[] iso8601Correction = {6,7,8,9,10,4,5};
    int nds = fromDate.Subtract(startOfYear).Days  + iso8601Correction[(int)startOfYear.DayOfWeek];
    int wk = nds / 7;
    switch(wk)
    {
        case 0 : 
            // Return weeknumber of dec 31st of the previous year
            return weekNumber(startOfYear.AddDays(-1));
        case 53 : 
            // If dec 31st falls before thursday it is week 01 of next year
            if (endOfYear.DayOfWeek < DayOfWeek.Thursday)
                return 1;
            else
                return wk;
        default : return wk;
    }
}

ソース(他にもたくさんの機能があります...)


したがって、ループを次のように変更します

for (int i = 0; i <= 10; i++)
{
    DateTime currentDate = date.AddDays(i);
    Console.WriteLine("Date: {0} WeekNumber: {1}: CorrectWeekNumber: {2}",
        currentDate.ToShortDateString(),
        CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(currentDate, weekRule, firstDayOfWeek),
        weekNumber(currentDate));
}

結果は次のとおりです。

日付: 30.12.2012 WeekNumber: 52: CorrectWeekNumber: 52
日付: 31.12.2012 WeekNumber: 53: CorrectWeekNumber: 1
日付: 01.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
日付: 02.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
日付: 03.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Date: 04.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Date: 05.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Date: 06.01.2013 WeekNumber: 1: CorrectWeekNumber: 1
Date: 07.01. 2013 WeekNumber: 2: CorrectWeekNumber: 2
Date: 08.01.2013 WeekNumber: 2: CorrectWeekNumber: 2
Date: 09.01.2013 WeekNumber: 2: CorrectWeekNumber: 2

于 2012-08-30T12:40:10.070 に答える
2

すべての答えをありがとう。また、さらに検索し、最終的に 2 つの C# メソッドを作成して、目的を達成しました。

まず、次のコメントの 1 つに簡潔なものがあります 。 net.aspx

どのジョン・センチーナも指摘しました:

     public static int WeekNumber(this DateTime date)
    {
        Calendar cal = CultureInfo.InvariantCulture.Calendar;
        DayOfWeek day = cal.GetDayOfWeek(date);
        date = date.AddDays(4 - ((int)day == 0 ? 7 : (int)day));
        return cal.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
    }

また、http: //www.tondering.dk/claus/cal/week.php#calcweeknoにも 1 つあります。

    public static int WeekNumber2(this DateTime date)
    {
        int a;
        int b;
        int c;
        int s;
        int e;
        int f;

        if (date.Month <= 2)
        {
            a = date.Year - 1;
            b = a / 4 - a / 100 + a / 400;
            c = (a - 1) / 4 - (a - 1) / 100 + (a - 1) / 400;
            s = b - c;
            e = 0;
            f = date.Day - 1 + 31 * (date.Month - 1);
        }
        else
        {
            a = date.Year;
            b = a / 4 - a / 100 + a / 400;
            c = (a - 1) / 4 - (a - 1) / 100 + (a - 1) / 400;
            s = b - c;
            e = s + 1;
            f = date.Day + ((153 * (date.Month - 3) + 2) / 5) + 58 + s;
        }

        int g = (a + b) % 7;
        int d = (f + g - e) % 7;
        int n = f + 3 - d;

        if (n < 0)
            return 53 - ((g - s) / 5);
        if (n > (364 + s))
            return 1;
        return n / 7 + 1;
    }

どちらも私が欲しかったものを与えてくれました。

また、カレンダーの最初の 3000 年間は同じ週番号を返すことを証明する小さな単体テストも作成しました。

    [TestMethod]
    public void WeekNumbers_CorrectFor_3000Years()
    {
        var weekNumbersMethod1 = WeekNumbers3000Years(DateManipulation.WeekNumber).ToList();
        var weekNumbersMethod2 = WeekNumbers3000Years(DateManipulation.WeekNumber2).ToList();
        CollectionAssert.AreEqual(weekNumbersMethod1, weekNumbersMethod2);
    }

    private IEnumerable<int> WeekNumbers3000Years(Func<DateTime, int> weekNumberCalculator)
    {
        var startDate = new DateTime(1,1,1);
        var endDate = new DateTime(3000, 12, 31);
        for(DateTime date = startDate; date < endDate; date = date.AddDays(1))
            yield return weekNumberCalculator(date);
    }
于 2012-08-30T13:36:39.073 に答える
0

Calendar.GetWeekOfYearは、ISO8601仕様をサポートしていません。Microsoft.NetのISO 8601 WeekofYear形式も参照してください。

.NETの期間ライブラリのWeekクラスを使用できます。

// ----------------------------------------------------------------------
public void CalendarWeekSample()
{
  DateTime testDate = new DateTime( 2007, 12, 31 );

  // .NET calendar week
  TimeCalendar calendar = new TimeCalendar();
  Console.WriteLine( "Calendar Week of {0}: {1}", testDate.ToShortDateString(),
                     new Week( testDate, calendar ).WeekOfYear );
  // > Calendar Week of 31.12.2007: 53

  // ISO 8601 calendar week
  TimeCalendar calendarIso8601 = new TimeCalendar(
    new TimeCalendarConfig { YearWeekType = YearWeekType.Iso8601 } );
  Console.WriteLine( "ISO 8601 Week of {0}: {1}", testDate.ToShortDateString(),
                     new Week( testDate, calendarIso8601 ).WeekOfYear );
  // > ISO 8601 Week of 31.12.2007: 1
} // CalendarWeekSample
于 2012-08-30T13:26:44.710 に答える
0

2013 年 1 月 1 日は火曜日で、第 1 週が始まります。2012 年 12 月 31 日は月曜日で、2012 年に属するため、第 53 週です。当然のことながら、12 月に週 1 を設定することはできません。これは、1 年に週 1 が 2 回あることを意味し、コードであらゆる種類の厄介な問題が発生することを意味します。

1 週間は正確に 7 日でなければならないという規則はありません。

于 2012-08-30T12:25:01.977 に答える
-1

デンマークのカレンダーについては何も知りませんが、.NETのルールは明確であり、コードはそれらに正確に対応しています。CalendarWeekRule列挙型のMSDNドキュメントは、参照用にここにあります。要約すれば:

.NETは、常に12月31日を「その年の最後の週」として扱います。

FirstFourDayWeek週ルールの値を取得していると言います。2013年1月1日は火曜日であるため、その週の残りの日数は6日です(次の週は次の月曜日に始まります)。したがって、2013年1月1日から6日は「第1週」と見なされます。そのルールがあればFirstFullWeek、代わりに第1週は月曜日の7日に始まります。

「明らかにこれは間違っているに違いない」に関しては、それ自体の仕様によれば、明らかに正しい。プログラミング言語やAPIが特定の期待に準拠する必要はありません。各プロジェクトは独自の要件を定義します。.NETは、予期しないと思われる規則に従っていますが、それら文書化されており、その文書化と一貫性があります。

それが役に立つかどうかは別の質問です...

于 2012-08-30T12:47:43.643 に答える