Получить правильный номер недели данной даты

У меня много Googled и нашел много решений, но ни один из них не дал мне правильный номер недели для 2012-12-31. Даже пример в MSDN (ссылка) терпит неудачу.

2012-12-31 - понедельник, поэтому это должна быть неделя 1, но каждый метод, который я пытался, дает мне 53. Вот некоторые из методов, которые я пробовал:

Из библиотеки MDSN:

DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
Calendar cal = dfi.Calendar;

return cal.GetWeekOfYear(date, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);

Решение 2:

return new GregorianCalendar(GregorianCalendarTypes.Localized).GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

Решение 3:

CultureInfo ciCurr = CultureInfo.CurrentCulture;
int weekNum = ciCurr.Calendar.GetWeekOfYear(dtPassed, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return weekNum;

Обновление

Следующий метод фактически возвращает 1, когда дата 2012-12-31. Другими словами, моя проблема заключалась в том, что мои методы не соответствовали стандарту ISO-8601.

// This presumes that weeks start with Monday.
// Week 1 is the 1st week of the year with a Thursday in it.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // Seriously cheat.  If its Monday, Tuesday or Wednesday, then it'll 
    // be the same week# as whatever Thursday, Friday or Saturday are,
    // and we always get those right
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    // Return the week of our adjusted day
    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

Ответ 1

Как отмечено на этой странице MSDN, существует небольшая разница между неделей ISO8601 и нумерацией .Net.

Вы можете обратиться к этой статье в блоге MSDN для лучшего объяснения: "ISO 8601 Формат недели в Microsoft.Net"

Проще говоря,.Net позволяет разделить недели на несколько лет, а стандарт ISO - нет. В этой статье также есть простая функция для получения правильного номера номера ISO 8601 за последнюю неделю года.

Обновление. Следующий метод фактически возвращает 1 для 2012-12-31, что верно в ISO 8601 (например, в Германии).

// This presumes that weeks start with Monday.
// Week 1 is the 1st week of the year with a Thursday in it.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // Seriously cheat.  If its Monday, Tuesday or Wednesday, then it'll 
    // be the same week# as whatever Thursday, Friday or Saturday are,
    // and we always get those right
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    // Return the week of our adjusted day
    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
} 

Ответ 2

В году может быть более 52 недель. Каждый год имеет 52 полных недели + 1 или +2 (високосный год) дополнительных дней. Они составляют 53-ю неделю.

  • 52 недели * 7 дней = 364 дней.

Итак, для каждого года у вас есть хотя бы один дополнительный день. Два для високосных лет. Эти дополнительные дни считаются отдельными неделями?

Сколько недель действительно зависит от начального дня вашей недели. Давайте рассмотрим это на 2012 год.

  • США (воскресенье → суббота): 52 недели + одна короткая 2-дневная неделя для 2012-12-30 и 2012-12-31. Это в итоге составляет 53 недели. Последние два дня этого года (воскресенье + понедельник) составляют свою короткую неделю.

Проверьте текущие настройки культуры, чтобы увидеть, что он использует в качестве первого дня недели.

Как вы видите, нормально получить 53 в результате.

  • Европа (понедельник → воскресенье): Январь 2dn (2012-1-2) - первый понедельник, так что это первый день первой недели. Спросите номер недели на 1 января, и вы вернетесь 52, так как это считается частью 2011 на прошлой неделе.

Возможно даже иметь 54-ю неделю. Случается каждые 28 лет, когда 1 января и 31 декабря рассматриваются как отдельные недели. Это тоже должен быть високосный год.

Например, в 2000 году было 54 недели. 1 января (сидел) был первый день недели, а 31 декабря (солнце) был вторым недельным днем.

var d = new DateTime(2012, 12, 31);
CultureInfo cul = CultureInfo.CurrentCulture;

var firstDayWeek = cul.Calendar.GetWeekOfYear(
    d,
    CalendarWeekRule.FirstDay,
    DayOfWeek.Monday);

int weekNum = cul.Calendar.GetWeekOfYear(
    d,
    CalendarWeekRule.FirstDay,
    DayOfWeek.Monday);

int year = weekNum == 52 && d.Month == 1 ? d.Year - 1 : d.Year;
Console.WriteLine("Year: {0} Week: {1}", year, weekNum);

Распечатки: Год: 2012 Неделя: 54

Измените CalendarWeekRule в приведенном выше примере на FirstFullWeek или FirstFourDayWeek, и вы вернетесь назад 53. Дайте сохранить начальный день в понедельник, так как мы имеем дело с Германией.

Итак, неделя 53 начинается в понедельник 2012-12-31, длится один день, а затем останавливается.

53 - правильный ответ. Измените культуру на германию, если хотите попробовать.

CultureInfo cul = CultureInfo.GetCultureInfo("de-DE");

Ответ 3

Это способ:

public int GetWeekNumber()
{
    CultureInfo ciCurr = CultureInfo.CurrentCulture;
    int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
    return weekNum;
}

Наиболее важным является параметр CalendarWeekRule.

Смотрите здесь: https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=IT-IT&k=k(System.Globalization.CalendarWeekRule);k(TargetFrameworkMoniker-.NETFramework

Ответ 4

Поскольку, по-видимому, не существует .Net-культуры, которая выдает правильный номер недели ISO-8601, я бы предпочел вообще обойти определение встроенной недели и выполнить расчет вручную, вместо того, чтобы пытаться исправить частично правильное значение. результат.

В итоге я выбрал следующий метод расширения:

/// <summary>
/// Converts a date to a week number.
/// ISO 8601 week 1 is the week that contains the first Thursday that year.
/// </summary>
public static int ToIso8601Weeknumber(this DateTime date)
{
    var thursday = date.AddDays(3 - date.DayOfWeek.DayOffset());
    return (thursday.DayOfYear - 1) / 7 + 1;
}

/// <summary>
/// Converts a week number to a date.
/// Note: Week 1 of a year may start in the previous year.
/// ISO 8601 week 1 is the week that contains the first Thursday that year, so
/// if December 28 is a Monday, December 31 is a Thursday,
/// and week 1 starts January 4.
/// If December 28 is a later day in the week, week 1 starts earlier.
/// If December 28 is a Sunday, it is in the same week as Thursday January 1.
/// </summary>
public static DateTime FromIso8601Weeknumber(int weekNumber, int? year = null, DayOfWeek day = DayOfWeek.Monday)
{
    var dec28 = new DateTime((year ?? DateTime.Today.Year) - 1, 12, 28);
    var monday = dec28.AddDays(7 * weekNumber - dec28.DayOfWeek.DayOffset());
    return monday.AddDays(day.DayOffset());
}

/// <summary>
/// Iso8601 weeks start on Monday. This returns 0 for Monday.
/// </summary>
private static int DayOffset(this DayOfWeek weekDay)
{
    return ((int)weekDay + 6) % 7;
}

Прежде всего, ((int)date.DayOfWeek + 6) % 7) определяет номер дня недели, 0 = понедельник, 6 = воскресенье.

date.AddDays(-((int)date.DayOfWeek + 6) % 7) определяет дату понедельника, предшествующего запрошенному номеру недели.

Три дня спустя - целевой четверг, который определяет год недели.

Если вы разделите (на основе нуля) число дней в году на семь (округление вниз), вы получите (на основе нуля) номер недели в году.

В С# результаты вычисления целых чисел неявно округляются.

Ответ 5

Хорошие новости! Запрос на добавление, добавляющий System.Globalization.ISOWeek в .NET Core, был только что объединен и в настоящее время планируется выпустить версию 3.0. Надеемся, что он распространится на другие платформы .NET в недалеком будущем.

Тип имеет следующую подпись, которая должна охватывать большинство потребностей недели ISO:

namespace System.Globalization
{
    public static class ISOWeek
    {
        public static int GetWeekOfYear(DateTime date);
        public static int GetWeeksInYear(int year);
        public static int GetYear(DateTime date);
        public static DateTime GetYearEnd(int year);
        public static DateTime GetYearStart(int year);
        public static DateTime ToDateTime(int year, int week, DayOfWeek dayOfWeek);
    }
}

Вы можете найти исходный код здесь.

ОБНОВЛЕНИЕ: Эти API также были включены в версию .NET Standard 2.1.

Ответ 6

С# в порт Powershell из кода выше из il_guru:

function GetWeekOfYear([datetime] $inputDate)
{
   $day = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetDayOfWeek($inputDate)
   if (($day -ge [System.DayOfWeek]::Monday) -and ($day -le [System.DayOfWeek]::Wednesday))
   {
      $inputDate = $inputDate.AddDays(3)
   }

   # Return the week of our adjusted day
   $weekofYear = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetWeekOfYear($inputDate, [System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [System.DayOfWeek]::Monday)
   return $weekofYear
}

Ответ 7

Самый простой способ определить номер недели по ISO 8601 с использованием С# и класса DateTime.

Спросите об этом: многолетний день года - это четверг этой недели. Ответ равен желаемому номеру недели.

var dayOfWeek = (int)moment.DayOfWeek;
// Make monday the first day of the week
if (--dayOfWeek < 0)
    dayOfWeek = 6;
// The whole nr of weeks before this thursday plus one is the week number
var weekNumber = (moment.AddDays(3 - dayOfWeek).DayOfYear - 1) / 7 + 1;

Ответ 8

var cultureInfo = CultureInfo.CurrentCulture;
var calendar = cultureInfo.Calendar;

var calendarWeekRule = cultureInfo.DateTimeFormat.CalendarWeekRule;
var firstDayOfWeek = cultureInfo.DateTimeFormat.FirstDayOfWeek;
var lastDayOfWeek = cultureInfo.LCID == 1033 //En-us
                    ? DayOfWeek.Saturday
                    : DayOfWeek.Sunday;

var lastDayOfYear = new DateTime(date.Year, 12, 31);

var weekNumber = calendar.GetWeekOfYear(date, calendarWeekRule, firstDayOfWeek);

 //Check if this is the last week in the year and it doesn`t occupy the whole week
return weekNumber == 53 && lastDayOfYear.DayOfWeek != lastDayOfWeek 
       ? 1  
       : weekNumber;

Это хорошо работает как для американской, так и для русской культур. ИСО 8601 также будет правильным, потому что русская неделя начинается в понедельник.

Ответ 9

Вопрос: как вы определяете, есть ли неделя в 2012 году или в 2013 году? Ваше предположение, я думаю, состоит в том, что с 6 дней недели в 2013 году эта неделя должна быть отмечена как первая неделя 2013 года.

Не уверен, что это правильный путь. Эта неделя началась в 2012 году (понедельник 31 декабря), поэтому ее следует отметить как последнюю неделю 2012 года, поэтому она должна быть 53-м 2012 года. Первая неделя 2013 года должна начаться в понедельник 7-м.

Теперь вы можете обработать конкретный случай крайних недель (первая и последняя неделя года) с использованием информации дня недели. Все зависит от вашей логики.

Ответ 10

  DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
  DateTime date1 = new DateTime(2011, 1, 1);
  Calendar cal = dfi.Calendar;

  Console.WriteLine("{0:d}: Week {1} ({2})", date1, 
                    cal.GetWeekOfYear(date1, dfi.CalendarWeekRule, 
                                      dfi.FirstDayOfWeek),
                    cal.ToString().Substring(cal.ToString().LastIndexOf(".") + 1));      

Ответ 11

Вот расширенная версия и версия ответа il_guru.

Расширение:

public static int GetIso8601WeekOfYear(this DateTime time)
{
    var day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

Nullable:

public static int? GetIso8601WeekOfYear(this DateTime? time)
{
    return time?.GetIso8601WeekOfYear();
}

Обычаи:

new DateTime(2019, 03, 15).GetIso8601WeekOfYear(); //returns 11
((DateTime?) new DateTime(2019, 03, 15)).GetIso8601WeekOfYear(); //returns 11
((DateTime?) null).GetIso8601WeekOfYear(); //returns null

Ответ 12

В течение года (52 х 7 = 364) год имеет 52 недели и 1 день или 2 года. 2012-12-31 будет неделя 53, неделя, которая будет иметь только 2 дня, потому что 2012 год круглый год.