Рассчитать дату по номеру недели


135

Любой знает простой способ получить дату первого дня недели (понедельник здесь, в Европе). Я знаю год и номер недели? Я собираюсь сделать это в C #.


Сильно связаны, возможно, дубликаты: stackoverflow.com/questions/659183/…
Джон Раш

Ответы:


251

У меня были проблемы с решением @HenkHolterman даже с исправлением @RobinAndersson.

Чтение по стандарту ISO 8601 хорошо решает проблему. Используйте первый четверг в качестве цели, а не понедельник. Код ниже будет работать и для 53 недели 2009 года.

public static DateTime FirstDateOfWeekISO8601(int year, int weekOfYear)
{
    DateTime jan1 = new DateTime(year, 1, 1);
    int daysOffset = DayOfWeek.Thursday - jan1.DayOfWeek;

    // Use first Thursday in January to get first week of the year as
    // it will never be in Week 52/53
    DateTime firstThursday = jan1.AddDays(daysOffset);
    var cal = CultureInfo.CurrentCulture.Calendar;
    int firstWeek = cal.GetWeekOfYear(firstThursday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

    var weekNum = weekOfYear;
    // As we're adding days to a date in Week 1,
    // we need to subtract 1 in order to get the right date for week #1
    if (firstWeek == 1)
    {
        weekNum -= 1;
    }

    // Using the first Thursday as starting week ensures that we are starting in the right year
    // then we add number of weeks multiplied with days
    var result = firstThursday.AddDays(weekNum * 7);

    // Subtract 3 days from Thursday to get Monday, which is the first weekday in ISO8601
    return result.AddDays(-3);
}       

3
Я попробовал почти все решения, приведенные здесь, это единственное, которое правильно работает для меня прямо сейчас. В настоящее время это 7 февраля 2012 года. № недели # 6. Этот код правильно дает мне 6 февраля в качестве даты начала недели. Все остальные решения дали мне 13 февраля, которое фактически является датой начала недели № 7.
HaukurHaf

1
Работает как шарм .. протестировал несколько данных: pastebin.com/mfx8s1vq Все работают без нареканий! Спасибо, Микаэль!
Митчел

1
@ RobinWassén-Andersson Хорошо, что вы еще раз вернулись к этому вопросу: еще 6 голосов, и я свяжусь с "не" правильным ответом, хе-хе.
Микаэль Свенсон

2
Предложение: сделайте «ISO8601» подходящим для имени метода, поскольку универсального «правильного» ответа просто не существует.
Хенк Холтерман

1
@Muflix Я не знаю достаточно о SQL и датах / календарях, чтобы знать - но работа CLR будет работать.
Микаэль Свенсон

36

Мне нравится решение, предоставленное Хенком Холтерманом. Но чтобы быть немного более независимым от культуры, вы должны получить первый день недели для текущей культуры (это не всегда понедельник):

using System.Globalization;

static DateTime FirstDateOfWeek(int year, int weekOfYear)
{
  DateTime jan1 = new DateTime(year, 1, 1);

  int daysOffset = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)jan1.DayOfWeek;

  DateTime firstMonday = jan1.AddDays(daysOffset);

  int firstWeek = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(jan1, CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule, CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek);

  if (firstWeek <= 1)
  {
    weekOfYear -= 1;
  }

  return firstMonday.AddDays(weekOfYear * 7);
}

Я добавил это в Mannex, как метод расширения для Calendar и DateTimeFormatInfo. Я также дал перекрестную ссылку на этот ответ для кредита.
Атиф Азиз

1
Это не работает должным образом на моей машине. Он показывает дату с 0010 вместо 2010. Я не знаю, если это проблема в .net Framework или в этой функции. Хорошая попытка, во всяком случае ...
Эдуардо Ксавье

6
firstMondayплохое имя переменной для чего-то, что не может быть понедельником. =)
мфлодин

11

ОБНОВЛЕНИЕ : .NET Core 3.0 и .NET Standard 2.1 поставляются с этим типом.

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

Вы должны быть в состоянии использовать ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)метод для расчета этого.

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


9

Самый простой способ - это найти первый понедельник года, а затем добавить соответствующее количество недель. Вот пример кода. Предполагается, что номер недели начинается с 1, кстати:

using System;

class Test
{
    static void Main()
    {
        // Show the third Tuesday in 2009. Should be January 20th
        Console.WriteLine(YearWeekDayToDateTime(2009, DayOfWeek.Tuesday, 3));
    }

    static DateTime YearWeekDayToDateTime(int year, DayOfWeek day, int week)
    {
        DateTime startOfYear = new DateTime (year, 1, 1);

        // The +7 and %7 stuff is to avoid negative numbers etc.
        int daysToFirstCorrectDay = (((int)day - (int)startOfYear.DayOfWeek) + 7) % 7;

        return startOfYear.AddDays(7 * (week-1) + daysToFirstCorrectDay);
    }
}

4
Да, но первый понедельник года может принадлежать 52 | 53 неделе предыдущего года.
Хенк Холтерман

1
Это зависит от того, как вы хотите определить вещи. К сожалению, у нас не так много информации, чтобы продолжить здесь ... Я надеюсь, что это полезно.
Джон Скит

3

Лично я бы использовал информацию о культуре, чтобы узнать день недели и перейти к первому дню культуры. Я не уверен, правильно ли я объясняю это, вот пример:

    public DateTime GetFirstDayOfWeek(int year, int weekNumber)
    {
        return GetFirstDayOfWeek(year, weekNumber, Application.CurrentCulture);
    }

    public DateTime GetFirstDayOfWeek(int year, int weekNumber,
        System.Globalization.CultureInfo culture)
    {
        System.Globalization.Calendar calendar = culture.Calendar;
        DateTime firstOfYear = new DateTime(year, 1, 1, calendar);
        DateTime targetDay = calendar.AddWeeks(firstOfYear, weekNumber);
        DayOfWeek firstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek;

        while (targetDay.DayOfWeek != firstDayOfWeek)
        {
            targetDay = targetDay.AddDays(-1);
        }

        return targetDay;
    }


2

Согласно ISO 8601: 1988, который используется в Швеции, первая неделя года - это первая неделя, на которую в новом году уходит как минимум четыре дня.

Так что, если ваша неделя начинается в понедельник, первый четверг, то любой год находится в пределах первой недели. Вы можете DateAdd или DateDiff из этого.


2

Предполагая, что номер недели начинается с 1

DateTime dt =  new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1));
return dt.AddDays(-(int)dt.DayOfWeek);

Это должно дать вам первый день в любой данной неделе. Я не проводил много испытаний, но похоже, что это работает. Это меньшее решение, чем большинство других я нашел в Интернете, поэтому хотел поделиться.


плохой ответ. Использование DateTime.Parse не требуется, поскольку DateTime имеет конструктор, который принимает год, месяц и день. new DateTime(1,1,YearNumber)
Jamiec

Обновлен код для создания нового DateTime вместо использования анализа. Было поздно. ;)
QuinnG

2

Бесплатно Период времени библиотека .NET включает в себя ISO 8601 соответствует классу Неделя :

// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
  return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek

2

Это сработало для меня, у него также есть преимущество в том, что в качестве параметра для проверки формулы с различными культурами нужно использовать параметр Cultureinfo. Если он пуст, он получает информацию о текущей культуре ... допустимые значения такие: "it", "en-us", "fr", ... ando и так далее. Хитрость заключается в том, чтобы вычесть номер недели первого дня года, который может быть 1, чтобы указать, что первый день находится в пределах первой недели. Надеюсь это поможет.

Public Shared Function FirstDayOfWeek(ByVal year As Integer, ByVal weekNumber As Integer, ByVal culture As String) As Date
    Dim cInfo As System.Globalization.CultureInfo
    If culture = "" Then
        cInfo = System.Globalization.CultureInfo.CurrentCulture
    Else
        cInfo = System.Globalization.CultureInfo.CreateSpecificCulture(culture)
    End If
    Dim calendar As System.Globalization.Calendar = cInfo.Calendar
    Dim firstOfYear As DateTime = New DateTime(year, 1, 1, calendar)
    Dim firstDayWeek As Integer = calendar.GetWeekOfYear(firstOfYear, cInfo.DateTimeFormat.CalendarWeekRule, cInfo.DateTimeFormat.FirstDayOfWeek)
    weekNumber -= firstDayWeek
    Dim targetDay As DateTime = calendar.AddWeeks(firstOfYear, weekNumber)
    Dim fDayOfWeek As DayOfWeek = cInfo.DateTimeFormat.FirstDayOfWeek

    While (targetDay.DayOfWeek <> fDayOfWeek)
        targetDay = targetDay.AddDays(-1)
    End While
    Return targetDay
End Function

2

Вот метод, который совместим с номерами недель, которые использует Google Analytics, а также с той же схемой нумерации, которую мы использовали внутри Intel, и которая, я уверен, также используется во многих других контекстах.

// Google Analytics does not follow ISO standards for date.
// It numbers week 1 starting on Jan. 1, regardless what day of week it starts on.
// It treats Sunday as the first day of the week.
// The first and last weeks of a year are usually not complete weeks.
public static DateTime GetStartDateTimeFromWeekNumberInYear(int year, uint weekOfYear)
{
  if (weekOfYear == 0 || weekOfYear > 54) throw new ArgumentException("Week number must be between 1 and 54! (Yes, 54... Year 2000 had Jan. 1 on a Saturday plus 53 Sundays.)");

  // January 1 -- first week.
  DateTime firstDayInWeek = new DateTime(year, 1, 1);
  if (weekOfYear == 1) return firstDayInWeek;

  // Get second week, starting on the following Sunday.      
  do
  {
    firstDayInWeek = firstDayInWeek.AddDays(1);
  } while (firstDayInWeek.DayOfWeek != DayOfWeek.Sunday);

  if (weekOfYear == 2) return firstDayInWeek;

  // Now get the Sunday of whichever week we're looking for.
  return firstDayInWeek.AddDays((weekOfYear - 2)*7);
}

2

Я попробовал некоторые коды выше, и некоторые из них имеют небольшие ошибки, когда вы пробуете разные годы с разными начальными днями недели, вы увидите их, я взял код Jon Skeet, исправил его, и он работает, очень простой код.

Public Function YearWeekDayToDateTime(ByVal year As Integer, ByVal weekDay As Integer, ByVal week As Integer) As DateTime
   ' weekDay, day you want
    Dim startOfYear As New DateTime(year, 1, 1)
    Dim startOfYearFixDay As Integer

    If startOfYear.DayOfWeek <> DayOfWeek.Sunday Then
        startOfYearFixDay = startOfYear.DayOfWeek
    Else
        startOfYearFixDay = 7
    End If

    Return startOfYear.AddDays((7 * (week)) - startOfYearFixDay + weekDay)
End Function

1

Слегка изменил код Микаэля Свенсона. Я нашел неделю первого понедельника и изменил номер недели.

 DateTime GetFirstWeekDay(int year, int weekNum)
    {
        Calendar calendar = CultureInfo.CurrentCulture.Calendar;

        DateTime jan1 = new DateTime(year, 1, 1);

        int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
        DateTime firstMonday = jan1.AddDays(daysOffset);
        int firstMondayWeekNum = calendar.GetWeekOfYear(firstMonday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

        DateTime firstWeekDay = firstMonday.AddDays((weekNum-firstMondayWeekNum) * 7);

        return firstWeekDay;
    }

0

Неделя 1 определяется как неделя, которая начинается в понедельник и содержит первый четверг года.


1
Это одно определение, есть и другие.
Хенк Холтерман

1
Стандарт ISO определяет неделю 1 как неделю с первым четвергом года.
RickardN


0

Я немного улучшил решение Томаса с помощью переопределения:

   public static DateTime FirstDateOfWeek(int year, int weekOfYear)
    {
      return Timer.FirstDateOfWeekOfMonth(year, 1, weekOfYear);
    }

    public static DateTime FirstDateOfWeekOfMonth(int year, int month, 
    int weekOfYear)
    {
      DateTime dtFirstDayOfMonth = new DateTime(year, month, 1);

       //I also commented out this part:
      /*
      if (firstWeek <= 1)
      {
        weekOfYear -= 1;
      }
      */

В противном случае дате предшествовала одна неделя.

Спасибо Томас, отличная помощь.


0

Я использовал одно из решений, но оно дало мне неправильные результаты, просто потому, что оно считает воскресенье первым днем ​​недели.

Я изменился:

var firstDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber - 1) * 7);
var lastDay = firstDay.AddDays(6);

чтобы:

var lastDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber) * 7);
var firstDay = lastDay.AddDays(-6);

и теперь это работает как шарм.


1
Но отвечает ли это на вопрос ОП? Это 1/1 плюс фиксированное количество дней. Нет первого дня недели концепции.
Герт Арнольд

0

Предлагаемое решение не является полным - оно работает только для CalendarWeekRule.FirstFullWeek. Другие типы правил недели не работают. Это можно увидеть с помощью этого теста:

foreach (CalendarWeekRule rule in Enum.GetValues(typeof(CalendarWeekRule)))
{
    for (int year = 1900; year < 2000; year++)
    {
        DateTime date = FirstDateOfWeek(year, 1, rule);
        Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, rule, DayOfWeek.Monday) == 1);
        Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date.AddDays(-1), rule, DayOfWeek.Monday) != 1);
    }
}

0

Я сделал улучшенную версию предложенного решения, которая является более простой и параметризует firstDayOfWeek:

public static DateTime GetFirstDayOfWeek(int year, int week, DayOfWeek firstDayOfWeek)
{
    return GetWeek1Day1(year, firstDayOfWeek).AddDays(7 * (week - 1));
}

public static DateTime GetWeek1Day1(int year, DayOfWeek firstDayOfWeek)
{
    DateTime date = new DateTime(year, 1, 1);

    // Move towards firstDayOfWeek
    date = date.AddDays(firstDayOfWeek - date.DayOfWeek);

    // Either 1 or 52 or 53
    int weekOfYear = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFullWeek, firstDayOfWeek);

    // Move forwards 1 week if week is 52 or 53
    date = date.AddDays(7 * System.Math.Sign(weekOfYear - 1));

    return date;
}

0

Это мое решение, когда мы хотим вычислить дату, данный год, номер недели и день недели.

int Year = 2014;
int Week = 48;
int DayOfWeek = 4;

DateTime FecIni = new DateTime(Year, 1, 1);
FecIni = FecIni.AddDays(7 * (Week - 1));
if ((int)FecIni.DayOfWeek > DayOfWeek)
{
    while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(-1);
}
else
{
    while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(1);
}

0

Я упростил код, предоставленный Микаэлем Свенссоном, который подходит для многих стран Европы.

public static DateTime FirstDateOfWeekIso8601(int year, int week)
{
        var firstThursdayOfYear = new DateTime(year, 1, 1);
        while (firstThursdayOfYear.DayOfWeek != DayOfWeek.Thursday)
        {
            firstThursdayOfYear = firstThursdayOfYear.AddDays(1);
        }

        var startDateOfWeekOne = firstThursdayOfYear.AddDays(-(DayOfWeek.Thursday - DayOfWeek.Monday));

        return startDateOfWeekOne.AddDays(7 * (week - 1));        
}

0

Я написал и протестировал следующий код, и он прекрасно работает для меня. Пожалуйста, дайте мне знать, если у кого-то возникнут проблемы с этим, я также разместил вопрос, чтобы получить наилучший ответ. Кто-то может найти это полезным.

public static DateTime GetFirstDateOfWeekByWeekNumber(int year, int weekNumber)
        {
            var date = new DateTime(year, 01, 01);
            var firstDayOfYear = date.DayOfWeek;
            var result = date.AddDays(weekNumber * 7);

            if (firstDayOfYear == DayOfWeek.Monday)
                return result.Date;
            if (firstDayOfYear == DayOfWeek.Tuesday)
                return result.AddDays(-1).Date;
            if (firstDayOfYear == DayOfWeek.Wednesday)
                return result.AddDays(-2).Date;
            if (firstDayOfYear == DayOfWeek.Thursday)
                return result.AddDays(-3).Date;
            if (firstDayOfYear == DayOfWeek.Friday)
                return result.AddDays(-4).Date;
            if (firstDayOfYear == DayOfWeek.Saturday)
                return result.AddDays(-5).Date;
            return result.AddDays(-6).Date;
        }

Это неправильно - по крайней мере, для многих европейских стран. GetFirstDateOfWeekByWeekNumber(2020, 29)на 29 неделе 2020 года это возвращается 07/20/2020. Но первый день 29-й недели был07/13/2020
Матиас Бургер

0

В настоящее время не существует класса C #, который бы правильно обрабатывал номера ISO 8601week. Даже если вы можете создать культуру, найти ближайшую вещь и исправить ее, я думаю, что лучше сделать полный расчет самостоятельно:

    /// <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;
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.