根据官方(公历)日历,29/12/2008 的周数为 1,因为在第 52 周的最后一天(即 28/12)之后,一年中剩下的天数或更少。有点奇怪,但好吧,规则就是规则。
根据这个日历,我们有 2008/2009 年的边界值
C# 提供了一个 GregorianCalendar 类,它有一个函数
GetWeekOfYear(date, rule, firstDayOfWeek)
。
参数
rule
是一个具有 3 个可能值的枚举:FirstDay, FirstFourWeekDay, FirstFullWeek
。根据我的理解,我应该遵循 FirstFourWeekDay
规则,但为了以防万一,我尝试了所有这些规则。
最后一个参数告知哪一天应被视为一周的第一天,根据该日历,它是星期一,所以是星期一。
所以我启动了一个快速而肮脏的控制台应用程序来测试这个:
using System;
using System.Globalization;
namespace CalendarTest
{
class Program
{
static void Main(string[] args)
{
var cal = new GregorianCalendar();
var firstWeekDay = DayOfWeek.Monday;
var twentyEighth = new DateTime(2008, 12, 28);
var twentyNinth = new DateTime(2008, 12, 29);
var firstJan = new DateTime(2009, 1, 1);
var eightJan = new DateTime(2009, 1, 8);
PrintWeekDays(cal, twentyEighth, firstWeekDay);
PrintWeekDays(cal, twentyNinth, firstWeekDay);
PrintWeekDays(cal, firstJan, firstWeekDay);
PrintWeekDays(cal, eightJan, firstWeekDay);
Console.ReadKey();
}
private static void PrintWeekDays(Calendar cal, DateTime dt, DayOfWeek firstWeekDay)
{
Console.WriteLine("Testing for " + dt.ToShortDateString());
Console.WriteLine("--------------------------------------------");
Console.Write(CalendarWeekRule.FirstDay.ToString() + "\t\t");
Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, firstWeekDay));
Console.Write(CalendarWeekRule.FirstFourDayWeek.ToString() + "\t");
Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, firstWeekDay));
Console.Write(CalendarWeekRule.FirstFullWeek.ToString() + "\t\t");
Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFullWeek, firstWeekDay));
Console.WriteLine("--------------------------------------------");
}
}
}
...这就是我得到的
Testing for 28.12.2008
--------------------------------------------
FirstDay 52
FirstFourDayWeek 52
FirstFullWeek 51
--------------------------------------------
Testing for 29.12.2008
--------------------------------------------
FirstDay 53
FirstFourDayWeek 53
FirstFullWeek 52
--------------------------------------------
Testing for 01.01.2009
--------------------------------------------
FirstDay 1
FirstFourDayWeek 1
FirstFullWeek 52
--------------------------------------------
Testing for 08.01.2009
--------------------------------------------
FirstDay 2
FirstFourDayWeek 2
FirstFullWeek 1
--------------------------------------------
因此,正如我们所见,上面的组合都不符合官方日历(如果您很着急,请注意 29/12 永远不会成为第 1 周)。
我在这里做错了什么?也许我缺少一些明显的东西? (现在是周五,比利时工作时间很晚,请耐心等待;))
编辑:也许我应该解释一下:我需要的是一个适用于任何年份的函数,返回与我链接的公历相同的结果。因此 2008 年没有特殊的解决方法。
这篇文章更深入地探讨了这个问题和可能的解决方法。问题的核心是 .NET 日历实现似乎没有忠实地实现 ISO 标准
@康拉德是正确的。 DateTime 和 GregorianCalendar 的 .NET 实现未实现/遵循完整的 ISO 8601 规范。话虽这么说,他们的规范非常详细,并且完全实现起来并不简单,至少在解析方面是这样。
以下站点提供更多信息:
简单来说:
一周由给定年份中的编号来标识,并从星期一开始。一年的第一周是包含第一个星期四的一周,或者等同于包含 1 月 4 日的一周。
这是我用于正确处理 ISO 8601 日期的部分代码:
#region FirstWeekOfYear
/// <summary>
/// Gets the first week of the year.
/// </summary>
/// <param name="year">The year to retrieve the first week of.</param>
/// <returns>A <see cref="DateTime"/>representing the start of the first
/// week of the year.</returns>
/// <remarks>
/// Week 01 of a year is per definition the first week that has the Thursday
/// in this year, which is equivalent to the week that contains the fourth
/// day of January. In other words, the first week of a new year is the week
/// that has the majority of its days in the new year. Week 01 might also
/// contain days from the previous year and the week before week 01 of a year
/// is the last week (52 or 53) of the previous year even if it contains days
/// from the new year.
/// A week starts with Monday (day 1) and ends with Sunday (day 7).
/// </remarks>
private static DateTime FirstWeekOfYear(int year)
{
int dayNumber;
// Get the date that represents the fourth day of January for the given year.
DateTime date = new DateTime(year, 1, 4, 0, 0, 0, DateTimeKind.Utc);
// A week starts with Monday (day 1) and ends with Sunday (day 7).
// Since DayOfWeek.Sunday = 0, translate it to 7. All of the other values
// are correct since DayOfWeek.Monday = 1.
if (date.DayOfWeek == DayOfWeek.Sunday)
{
dayNumber = 7;
}
else
{
dayNumber = (int)date.DayOfWeek;
}
// Since the week starts with Monday, figure out what day that
// Monday falls on.
return date.AddDays(1 - dayNumber);
}
#endregion
#region GetIsoDate
/// <summary>
/// Gets the ISO date for the specified <see cref="DateTime"/>.
/// </summary>
/// <param name="date">The <see cref="DateTime"/> for which the ISO date
/// should be calculated.</param>
/// <returns>An <see cref="Int32"/> representing the ISO date.</returns>
private static int GetIsoDate(DateTime date)
{
DateTime firstWeek;
int year = date.Year;
// If we are near the end of the year, then we need to calculate
// what next year's first week should be.
if (date >= new DateTime(year, 12, 29))
{
if (date == DateTime.MaxValue)
{
firstWeek = FirstWeekOfYear(year);
}
else
{
firstWeek = FirstWeekOfYear(year + 1);
}
// If the current date is less than next years first week, then
// we are still in the last month of the current year; otherwise
// change to next year.
if (date < firstWeek)
{
firstWeek = FirstWeekOfYear(year);
}
else
{
year++;
}
}
else
{
// We aren't near the end of the year, so make sure
// we're not near the beginning.
firstWeek = FirstWeekOfYear(year);
// If the current date is less than the current years
// first week, then we are in the last month of the
// previous year.
if (date < firstWeek)
{
if (date == DateTime.MinValue)
{
firstWeek = FirstWeekOfYear(year);
}
else
{
firstWeek = FirstWeekOfYear(--year);
}
}
}
// return the ISO date as a numeric value, so it makes it
// easier to get the year and the week.
return (year * 100) + ((date - firstWeek).Days / 7 + 1);
}
#endregion
#region Week
/// <summary>
/// Gets the week component of the date represented by this instance.
/// </summary>
/// <value>The week, between 1 and 53.</value>
public int Week
{
get
{
return this.isoDate % 100;
}
}
#endregion
#region Year
/// <summary>
/// Gets the year component of the date represented by this instance.
/// </summary>
/// <value>The year, between 1 and 9999.</value>
public int Year
{
get
{
return this.isoDate / 100;
}
}
#endregion
周数因国家/地区而异,如果我没有完全错的话,应该取决于您的语言环境/区域设置。
编辑:维基百科支持我模糊的记忆,这些数字因国家/地区而异:http://en.wikipedia.org/wiki/Week_number#Week_number
我希望有一个受人尊敬的框架遵守您本地运行时中选择的国家/地区。
根据我的经验,所展示的行为是典型行为,指的是第 53 周的最后一周。这可能是因为我对周数的所有重大暴露都与会计日历年末的报告有关目的,并且 IRS(或您选择的税务机构)认为日历年于 12 月 31 日结束,而不是一年中的最后一个整周。
我知道这是一篇旧帖子,但无论如何 noda time 似乎得到了正确的结果..
作为解决方法,您可以说周数是 WeekNumber mod 52。我相信这适用于您描述的情况。
作为解决方法,为什么不使用
FirstFourDayWeek
而是添加:
if ( weekNumber > 52 )
weekNumber = 1;