Является ли .NET указанием неверного номера недели на 29 декабря 2008 года?
Согласно официальному (gregorian) календарю номер недели для 29/12/2008 равен 1, потому что после последнего дня недели 52 (т.е. 28/12) в течение года осталось три или менее дней. Собственно, странно, но хорошо, правила - это правила.
Итак, согласно этому календарю, мы имеем эти граничные значения для 2008/2009
- 28/12 - неделя 52
- 29/12 - неделя 1
- 1/1 - неделя 1
- 8/1 - неделя 2
С# предлагает класс GregorianCalendar, который имеет функцию GetWeekOfYear(date, rule, firstDayOfWeek)
.
Параметр rule
- это перечисление с тремя возможными значениями: 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).
Чем я здесь ошибаюсь? Может, там что-то вопиющее, что мне не хватает? (это пятница и поздние рабочие часы здесь, в Бельгии, несут меня;))
Изменить: Возможно, я должен объяснить: мне нужна функция, которая работает в течение любого года, возвращая те же результаты, что и связанный с gregorian календарь. Поэтому никаких специальных обходных решений для 2008 года.
Ответы
Ответ 2
@Conrad верен..NET-версия DateTime и GregorianCalendar не реализует/не соответствует полной спецификации ISO 8601. При этом они являются чрезвычайно подробными и нетривиальными для полного осуществления, по крайней мере, для парсинговой стороны вещей.
Дополнительная информация доступна на следующих сайтах:
- http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html
- http://www.w3.org/TR/NOTE-datetime
- http://www.cl.cam.ac.uk/~mgk25/iso-time.html
- http://www.cs.tut.fi/~jkorpela/iso8601.html
- http://hydracen.com/dx/iso8601.htm
- http://www.probabilityof.com/ISO8601.shtml
- http://www.mcs.vuw.ac.nz/technical/software/SGML/doc/iso8601/ISO8601.html
Проще говоря:
В определенный год неделя определяется по количеству и начинается с понедельника. Первая неделя года - та, которая включает первый четверг, или, что то же самое, которая включает 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 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
Ответ 3
Число недель отличается от страны к стране и должно зависеть от ваших региональных/региональных настроек, если я не совсем ошибаюсь.
Изменить: Wikipedia поддерживает мое смутное воспоминание о том, что эти цифры отличаются по стране: http://en.wikipedia.org/wiki/Week_number#Week_number
Я бы ожидал, что уважаемая структура будет подчиняться COUNTRY, выбранной в вашей локальной среде выполнения.
Ответ 4
По моему опыту, продемонстрированное поведение - это типичное поведение, относящееся к частичной конечной неделе как неделя 53. Это может быть связано с тем, что все существенные воздействия, которые у меня были на недельные числа, были связаны с учётом конца календарного года для целей отчетности, а IRS (или налоговое агентство по вашему выбору) считает, что календарный год закончится 31 декабря, а не последняя полная неделя года.
Ответ 5
Я знаю, что это старое сообщение, но любое время в ноде, похоже, дает правильный результат.
Ответ 6
Как работа, вы можете сказать, что номер недели - WeekNumber mod 52. Я считаю, что это будет работать для описанных вами случаев.
Ответ 7
Как обходной путь, почему бы не использовать FirstFourDayWeek
, а добавить:
if ( weekNumber > 52 )
weekNumber = 1;