Отримайте правильний номер тижня для даної дати


222

Я багато в Google і знайшов багато рішень, але жодне з них не дало мені правильного номера тижня за 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);
}

4
Як відбувається тиждень 1 на кінець року? Я маю на увазі, я бачу, де ти це дістаєш. Але 53 має для мене сенс.
benjer3

1
У своїх фрагментах коду я отримую CultureInfo та інше. Я думав, що моя програма до того часу знає, який саме календарем я використовую. (Тут, у Німеччині, 31 грудня 2012 року - 1 тижня 2013 року)
Amberlamps

Цей код працює не так, як слід спробувати дати, наприклад, 31 грудня 2016 року або 1 січня 2016 року
cavej03

@ cavej03 31 грудня 2016 року - 52 тиждень, і GetIso8601WeekOfYear повертається 52, тому я гадаю, що це працює правильно.
Муфлікс

Відповіді:


311

Як зазначається на цій сторінці 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);
} 

1
@il_guru Що робити, якщо я хочу, щоб тиждень починався з неділі ?? Чи я замінюю весь "понеділок" на "неділю" ??
Користувач2012384

2
@ User2012384 fyi, якщо ви хочете слідувати стандарту ISO8601, першийDayOfWeek завжди має бути понеділком.
Starceaker

1
@il_guru Вам навіть не потрібна ця "накрутка", чи не так? Наскільки я можу сказати "GetWeekOfYear (час, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);" працює просто чудово.
Starceaker

2
Якщо ви переглянете друге посилання (формат ISO 8601 тиждень року в Microsoft .Net), ви можете знайти відповідь на своє запитання безпосередньо від Microsoft. Приклад - понеділок 31/12/2007: за допомогою функції .Net повернеться тиждень № 53 2007 року, тоді як для стандарту ISO це тиждень №1 2008 року
il_guru

3
Я вручну перевіряв, що це правильно за 2012–2018 роки (припускаючи, що epochconverter.com є правильним). Цього року ми повинні всі радіти, що 1-го тижня 2018 року насправді розпочалось першого січня один раз!
Айдан

32

За рік може пройти більше 52 тижнів. Кожен рік має 52 повні тижні + 1 або +2 (високосний рік) додаткові дні. Вони складають 53-й тиждень.

  • 52 тижні * 7 днів = 364 дні.

Тож на кожен рік у вас є хоча б один додатковий день. Два на високосні роки. Чи вважаються ці додаткові дні окремими тижнями?

Скільки тижнів існує насправді, залежить від дня початку вашого тижня. Розглянемо це за 2012 рік.

  • США (неділя -> субота): 52 тижні + один короткий 2-денний тиждень для 2012-12-30 та 2012-12-31. Це в цілому становить 53 тижні. Останні два дні цього року (неділя + понеділок) складають свій власний короткий тиждень.

Перевірте свої поточні налаштування культури, щоб побачити, що вона використовується як перший день тижня.

Як ви бачите, це нормально отримати 53 результати.

  • Європа (понеділок -> неділя): 2 січня (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");

Я знаю, що можливо, що є 53 тижні. Але коли у вас є неділя (12/30) та понеділок (12/31), чи не вважаєте ви день (01/01) також частиною 53-го тижня в США?
Бурштинові

Я ніколи не бачив 54-го тижня!
Бурштинові

Деякі фіскальні програмні пакети їх мають.
Крістоф Гірс

Гаразд, я дізнався, що це можливо в календарях США, але в німецьких календарях це не відбудеться.
Бурштинові світильники

1
+1 за ваші глибокі роздуми щодо цього питання, але фрагмент коду у записі блогу, наданий в іншій відповіді, насправді вирішив мою проблему.
Бурштинові світильники

22

Це шлях:

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


3
"Декілька людей помітили, що Calendar.GetWeekOfYear () майже схожий на ISO 8601 тиждень, коли пройшли CalendarWeekRule.FirstFourDayWeek та DayOfWeek.Monday, проте дещо інше. Зокрема, ISO 8601 завжди має 7 денних тижнів." Blogs.msdn. microsoft.com/shawnste/2006/01/24/…
Juha Palomäki

1
Ваше посилання італійською мовою
radbyx

для нового DateTime (2000,12,31) він повертає всього 52?
Лешек П

18

Гарні новини! Запит тягнути додаванняSystem.Globalization.ISOWeek до .NET Ядра просто зливалися і в даний час планується до випуску 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 також включені у версію 2.1 .NET Standard .


Будь-яка ідея, коли v 3.0 вийде?
Жак

"Ми плануємо випустити перший перегляд .NET Core 3 пізніше цього року та остаточну версію в 2019 році." (з допису блогу дорожньої карти )
khellang

@khellang Як ​​я міг отримати черговий тиждень у місяці на основі дати? Замість того щоб отримати 33 тиждень, я хотів би побачити тиждень 2 (тому що тиждень 33 - це тиждень 2). stackoverflow.com/questions/58039103 / ...
Roxy'Pro

11

Оскільки, здається, не існує .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) визначає дату понеділка, що передує запитуваному номеру тижня.

Через три дні - цільовий четвер, який визначає, в якому році тиждень.

Якщо ви поділите (нульовий) число дня протягом року на сім (округлення вниз), ви отримаєте (нульове) число тижня в році.

У c # результати обчислення цілих чисел підкреслюються неявно.


1
Це найкраще рішення на сьогоднішній день, але я зазначу дещо інший спосіб цього: DateTime заснований на пролептичному григоріанському календарі, тобто він поширює григоріанський календар назад до року 1. 1 січня 0001 Понеділок. Ticks- кількість приростів 100 наносекунд з 1 січня 0001. Зважаючи на те, що за день є 86 400 секунд, ми можемо написати:var thursday = date.AddDays(3 - date.Ticks / 86400 / 10_000_000 % 7); return (thursday.DayOfYear - 1) / 7 + 1;
Адріан S

5

У .NET 3.0 та новіших версіях ви можете використовувати ISOWeek.GetWeekOfDate-Method .

Зауважте, що формат чисел року в році + тиждень може відрізнятися від року року DateTimeчерез тижні, що перетинають межу року.


3

C # до порту 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
}

1
Чи можете ви пояснити свій код? Відповіді, що стосуються лише коду, як правило, неприйнятні для StackOverflow, і вони можуть бути видалені.
Вай Ха Лі

@ Вай Ха Лі код уже пояснено. Дивіться публікацію вище від il_guru. Я переніс його код до powershell, тому інші люди можуть використовувати його в powershell, оскільки поки що немає хорошого рішення в powershell.
Райнер

Якщо ви це зробили, вам слід покласти кредит на оригінального автора у самій відповіді. Відповідь, яку ви надіслали, має кращі коментарі, які ви, мабуть, видалили. Крім того, це не є відповіддю на питання, оскільки в питанні PowerShell не згадується.
Вай Ха Лі

1
Я це зробив зараз @Wai Ha Lee. На питання вже позначено як відповідь, тому це було б те саме рішення, написане іншою мовою. Я зробив прихильність людям, які шукають рішення мовою Powershell (я дуже важко шукав рішення в Powershell, але те, що я знайшов, було рішенням у C #). Ідея залишається такою ж, і автор отримує заслугу за це.
Райнер

2

Найпростіший спосіб визначити номер тижня ISO 8601 стиль за допомогою c # та класу 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;

1
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;

Це добре працює як для американської, так і для російської культур. ISO 8601 також буде правильним, оскільки російський тиждень починається з понеділка.


1

Ось версія розширення та нульова версія відповіді 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);
}

Знімається:

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

0

Питання: Як визначити, чи є тиждень у 2012 чи 2013 році? Я вважаю, що оскільки 6 днів тижня припадає на 2013 рік, цей тиждень має бути позначений як перший тиждень 2013 року.

Не впевнений, чи це правильний шлях. Цей тиждень розпочався з 2012 року (у понеділок 31 грудня), тому його слід позначити як останній тиждень 2012 року, тому він повинен бути 53-м 2012 року. Перший тиждень 2013 року повинен розпочатися у понеділок 7-го.

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


Гаразд, я бачу вашу думку. Річ у тім, коли я використовую 2013/01/07 будь-яким із своїх методів, скажімо, тиждень 2. Отже, 2012/12/31 - це тиждень 53, а 2013/01/07 - тиждень 2. Ви можете подумати, що немає тижня 1 2013 р. Але, коли я спробую 2013/01/01, він говорить тиждень 1.
Бурштинові

0
  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));      

0

На основі відповіді il_guru я створив цю версію для власних потреб, яка також повертає компонент року.

    /// <summary>
    /// This presumes that weeks start with Monday.
    /// Week 1 is the 1st week of the year with a Thursday in it.
    /// </summary>
    /// <param name="time">The date to calculate the weeknumber for.</param>
    /// <returns>The year and weeknumber</returns>
    /// <remarks>
    /// Based on Stack Overflow Answer: https://stackoverflow.com/a/11155102
    /// </remarks>
    public static (short year, byte week) 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
        var week = (byte)CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        return ((short)(week >= 52 & time.Month == 1 ? time.Year - 1 : time.Year), week);
    }

0

Ці два методи допоможуть, якщо припустити, що наш тиждень розпочинається в понеділок

/// <summary>
    /// Returns the weekId
    /// </summary>
    /// <param name="DateTimeReference"></param>
    /// <returns>Returns the current week id</returns>
    public static DateTime GetDateFromWeek(int WeekReference)
    {
        //365 leap
        int DaysOffset = 0;
        if (WeekReference > 1)
        {
            DaysOffset = 7;
            WeekReference = WeekReference - 1;
        }
        DateTime DT = new DateTime(DateTime.Now.Year, 1, 1);
        int CurrentYear = DT.Year;
        DateTime SelectedDateTime = DateTime.MinValue;

        while (CurrentYear == DT.Year)
        {
            int TheWeek = WeekReportData.GetWeekId(DT);
            if (TheWeek == WeekReference)
            {
                SelectedDateTime = DT;
                break;
            }
            DT = DT.AddDays(1.0D);
        }

        if (SelectedDateTime == DateTime.MinValue)
        {
            throw new Exception("Please check week");
        }

        return SelectedDateTime.AddDays(DaysOffset);
    }
/// <summary>
    /// Returns the weekId
    /// </summary>
    /// <param name="DateTimeReference"></param>
    /// <returns>Returns the current week id</returns>
    public static int GetWeekId(DateTime DateTimeReference)
    {
        CultureInfo ciCurr = CultureInfo.InvariantCulture;
        int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTimeReference,
        CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
        return weekNum;
    }

-1

На рік припадає 52 тижні та 1 день або 2 у випадку року колу (52 х 7 = 364). 2012-12-31 буде 53 тижні, тиждень, який матиме лише 2 дні, тому що 2012 рік - рік у колі.


Це неправильно. Перший день тижня року може припадати на будь-який день тижня, залежно від того, як ви рахуєте тижні, рік може мати 54-й тиждень.
krowe2

-2
public int GetWeekNumber()
{
   CultureInfo ciCurr = CultureInfo.CurrentCulture;
   int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, 
   CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
   return weekNum;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.