首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WeekNumber calculation .NET中的错误?

WeekNumber calculation .NET中的错误?
EN

Stack Overflow用户
提问于 2012-08-30 20:20:05
回答 6查看 6.7K关注 0票数 18

我有一个相当奇怪的问题。我住在丹麦,2013年的第一周(第一周)从2012年12月31日开始,持续7天--就像往常一样:)

根据.NET的说法,12月30日是52周,31号是53周,1月1日是1周。

第53周仅持续一天,第1周持续6天。显然,这肯定是错误的(一周由不到7天组成),在丹麦上下文中肯定是错误的。其中12月31日是第1周,而不是第53周。

下面的代码说明了这个问题(CurrentCulture是"da-DK")

代码语言:javascript
复制
    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中的一个bug?如果是后者-你对正确计算周数有什么建议吗?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2012-08-30 20:40:10

问题是GetWeekOfYear方法没有遵守ISO8601,这是您所期望的,但它没有。

请注意,当您使用FirstFourDayWeek时,documentation会显示:

基于FirstFourDayWeek值的

第一周可以有四到七天。

这违反了ISO 8601规则,即所有星期都必须有七天。

Also:

您可以使用以下方法根据ISO 8601获取正确的周数:

代码语言:javascript
复制
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;
    }
}

Source (还有很多其他函数...)

因此,将您的循环更改为

代码语言:javascript
复制
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

日期: 04.01.2013 WeekNumber: 1: CorrectWeekNumber: 1

日期: 05.01.2013 WeekNumber: 1: CorrectWeekNumber: 1

日期: 06.01.2013 WeekNumber: 1: CorrectWeekNumber: 1

日期: 07.01.2013 WeekNumber: 2: CorrectWeekNumber: 2

日期: 08.01.2013 WeekNumber: 2: CorrectWeekNumber: 2

日期: 09.01.2013 WeekNumber: 2: CorrectWeekNumber: 2

票数 22
EN

Stack Overflow用户

发布于 2012-08-30 21:36:39

谢谢你所有的答案。我还进行了更多的搜索,最终创建了两个C#方法来实现我想要的:

首先,在http://blogs.msdn.com/b/shawnste/archive/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net.aspx的一条评论中找到了一个简洁的评论:

Jon Senchyna还指出:

代码语言:javascript
复制
     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

代码语言:javascript
复制
    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年返回相同的周数字。

代码语言:javascript
复制
    [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);
    }
票数 2
EN

Stack Overflow用户

发布于 2012-08-30 20:25:02

1.1.2013是星期二,从第1周开始。2012年12月31日是星期一,属于2012年,因此是第53周。当然,你不能在12月有第一周,因为这意味着一年中会有两个第一周,因此会导致各种令人讨厌的代码问题。

没有规定一周必须恰好是7天长。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12196714

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档