我自己的答案是三方面的。首先,我想出了一个包装器来Calendar.GetWeekOfYear
返回一周的“年”,因为今年可能与DateTime
对象的年份不同。
public static void GetWeek(DateTime dt, CultureInfo ci, out int week, out int year)
{
year = dt.Year;
week = ci.Calendar.GetWeekOfYear(dt, ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
int prevweek = ci.Calendar.GetWeekOfYear(dt.AddDays(-7.0), ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
if (prevweek + 1 == week) {
// year of prevweek should be correct
year = dt.AddDays(-7.0).Year;
} else {
// stay here
year = dt.Year;
}
}
接下来,这是答案的核心。这将year
and反转weekOfYear
为一个DateTime
对象。请注意,我使用了年中,因为这似乎避免了新年奇点的问题(其中 52 或 53 可能会或不会围绕 1)。我还通过查找确实是一周的第一天的日期来同步日期,避免比较两个 DayOfWeek 值的负偏移量问题。
public static DateTime FirstDateOfWeek(int year, int weekOfYear, CultureInfo ci)
{
DateTime jul1 = new DateTime(year, 7, 1);
while (jul1.DayOfWeek != ci.DateTimeFormat.FirstDayOfWeek)
{
jul1 = jul1.AddDays(1.0);
}
int refWeek = ci.Calendar.GetWeekOfYear(jul1, ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
int weekOffset = weekOfYear - refWeek;
return jul1.AddDays(7 * weekOffset );
}
最后,对于所有对此表示怀疑的人,这是我的单元测试,它在许多日期和文化中循环,以确保它适用于所有这些。
public static void TestDates()
{
foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures).Where((x)=>!x.IsNeutralCulture && x.Calendar.AlgorithmType == CalendarAlgorithmType.SolarCalendar))
{
for (int year = 2010; year < 2040; year++)
{
// first try a bunch of hours in this year
// convert from date -> week -> date
for (int hour = 0; hour < 356 * 24; hour+= 6)
{
DateTime dt = new DateTime(year, 1, 1).AddHours(hour);
int ww;
int wyear;
Gener8.SerialNumber.GetWeek(dt, ci, out ww, out wyear);
if (wyear != year)
{
//Console.WriteLine("{0} warning: {1} {2} {3}", ci.Name, dt, year, wyear);
}
DateTime dt1 = Gener8.SerialNumber.FirstDateOfWeek(wyear, ww, ci);
DateTime dt2 = Gener8.SerialNumber.FirstDateOfWeek(wyear, ww, ci).AddDays(7.0);
if (dt < dt1 || dt > dt2)
{
Console.WriteLine("{3} Bad date {0} not between {1} and {2}", dt, dt1, dt2, ci.Name);
}
}
// next try a bunch of weeks in this year
// convert from week -> date -> week
for (int week = 1; week < 54; week++)
{
DateTime dt0 = FirstDateOfWeek(year, week, ci);
int ww0;
int wyear0;
GetWeek(dt0, ci, out ww0, out wyear0);
DateTime dt1 = dt0.AddDays(6.9);
int ww1;
int wyear1;
GetWeek(dt1, ci, out ww1, out wyear1);
if ((dt0.Year == year && ww0 != week) ||
(dt1.Year == year && ww1 != week))
{
Console.WriteLine("{4} Bad date {0} ww0={1} ww1={2}, week={3}", dt0, ww0, ww1, week, ci.Name);
}
}
}
}
}