WeekNumber计算.NET中的错误?

我有一个相当奇怪的问题。 我住在丹麦,这里2013年的第一周(第1周)开始于2012年12月31日,持续7天 – 通常几周:)

据.NET称,12月30日是第52周,第31周是第53周,1月1日是第1周。

第53周仅持续一天,第1周持续6天。 显然,这一定是错误的(一周不到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中的错误? 如果是后者 – 您是否有正确计算周数的建议?

问题是GetWeekOfYear方法不符合ISO 8601,这是你所期望的,但事实并非如此。

请注意,当您使用FirstFourDayWeek , 文档说:

基于FirstFourDayWeek值的第一周可以有四到七天。

这违反了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; } } 

来源 (还有很多其他function......)


所以,将你的循环改为

 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

感谢所有的答案。 我还搜索了一些,最后创建了两个C#方法来实现我想要的:

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

其中Jon Senchyna也指出:

  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

  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; } 

两者都给了我想要的东西。

我还写了一个小的unit testing,certificate他们在日历的前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 WeekNumbers3000Years(Func 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); } 

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

没有规定一周必须正好7天。

Calendar.GetWeekOfYear不支持ISO8601规范,另请参阅Microsoft .Net中的ISO 8601 Week of Year格式 。

您可以使用.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 

我对丹麦日历一无所知,但.NET的规则很清楚,你的代码与那些完全对应。 CalendarWeekRule枚举的MSDN文档在此处供参考。 综上所述:

.NET将永远把12月31日视为“今年的最后一周”。

你说你得到了FirstFourDayWeek一周的规则值。 自2013年1月1日起是星期二,该星期的剩余时间为6天(下周将在下一个星期一开始)。 因此,2013年1月1日至6日被视为“第1周”。 如果您有该规则的FirstFullWeek ,第1周将从周一7日开始。

至于“显然这一定是错的:”显然它是正确的,根据它自己的规范。 任何编程语言或API都不需要符合特定的期望; 每个项目都定义了自己的要求。 .NET遵循我们可能认为意外的规则,但它们是有文档记录的,并且与其文档一致。

这是否有用是另一个问题……

您可以使用以下代码计算给定日期的周数:

  public static int GetWeekNumber(DateTime date) { var firstDayOfYear = new DateTime(date.Year, 1, 1); var lastDayOfYear = new DateTime(date.Year, 12, 31); var lastDayOfPreviousYear = new DateTime(date.Year - 1, 12, 31); var weekDayOfFirstDayOfYear = (int)firstDayOfYear.DayOfWeek + 1; var weekDayOfLastDayOfYear = (int)lastDayOfYear.DayOfWeek + 1; var days = (date - firstDayOfYear).Days; if (days <= 7 - weekDayOfFirstDayOfYear) // My day fall in 1'st week of the year { if (weekDayOfFirstDayOfYear > 5) return GetWeekNumber(lastDayOfPreviousYear); return 1; } else // My day fall not on 1'st week of the year { // Number of weeks that pass from 1'st Sunday of 2'nd week of the year var weekNo = ((days - (8 - weekDayOfFirstDayOfYear)) / 7) + 1; if (weekDayOfFirstDayOfYear < 6) // if Year start at Sun...Thursday the first week is added. weekNo++; // Check if Last week of the year belong to next year if (weekDayOfLastDayOfYear < 5) // if the year end in Sunday to Wednesday then it might belong to the 1'st week of the next year { if ((lastDayOfYear - date).Days < weekDayOfLastDayOfYear) { return 1; } } return weekNo; } }