[英]Bug in WeekNumber calculation .NET?
我有一個很奇怪的問題。 我住在丹麥,在這里,2013年的第一周(第1周)從2012年12月31日開始,持續7天-就像通常的幾周一樣:)
根據.NET,但是12月30日為第52周,31日為第53周,1月1日為第1周。
第53周僅持續1天,而第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規則,即所有星期必須有7天。
您可以使用以下方法根據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星期數:52:正確星期數:52
日期:31.12.2012星期數:53:正確星期數:1
日期:01.01.2013星期數:1:正確的星期數:1
日期:02.01.2013星期數:1:正確的星期數:1
日期:03.01.2013星期數:1:正確的星期數:1
日期:04.01.2013星期數:1:正確的星期數:1
日期:05.01.2013星期數:1:正確的星期數:1
日期:06.01.2013星期數:1:正確的星期數:1
日期:07.01.2013星期數:2:正確的星期數:2
日期:08.01.2013星期數:2:正確的星期數:2
日期:09.01.2013星期數:2:正確的星期數: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;
}
兩者都給了我我想要的東西。
我還寫了一個小單元測試,證明他們在日歷的前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);
}
2013年1月1日是星期二,從第1周開始。 2012年12月31日是星期一,屬於2012年,因此是第53周。 自然地,您不能在12月擁有第1周,因為這意味着一年中將有兩個第1周,因此將導致各種代碼問題。
沒有規則,一周必須恰好是7天。
我對丹麥歷法一無所知,但.NET的規則很明確,您的代碼與之完全對應。 CalendarWeekRule
枚舉的MSDN文檔在此處 ,以供參考。 綜上所述:
.NET始終將12月31日視為“一年的最后一周”。
您說周規則獲得了FirstFourDayWeek
的值。 由於2013年1月1日為星期二,因此該周的剩余時間為六天(下周從下周一開始)。 因此,2013年1月1-6日被視為“第1周”。 如果您有FirstFullWeek
於該規則的FirstFullWeek
,則第1周將從7月星期一開始。
至於“顯然這一定是錯誤的:”顯然是正確的,根據它自己的規范。 從未要求任何編程語言或API都必須符合特定的期望。 每個項目定義自己的需求。 .NET遵循了我們認為可能出乎意料的規則,但已對其進行了記錄,並且與它的文檔一致。
這是否有用是另一個問題...
Calendar.GetWeekOfYear不支持ISO8601規范,另請參見Microsoft .Net中的ISO 8601年度周格式 。
您可以使用.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
您可以使用以下代碼來計算給定日期的星期數:
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;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.