Вычислить дату по номеру недели
кто-нибудь знает простой способ получить дату первого дня недели (понедельник здесь, в Европе). Я знаю год и номер недели? Я собираюсь сделать это в C#.
23 ответа:
У меня были проблемы с решением @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); }
Мне нравится решение, предложенное Хенк Holterman. Но чтобы быть немного более независимой от культуры, вы должны получить первый день недели для текущей культуры (это не всегда понедельник):
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); }
самый простой способ, вероятно, найти первый понедельник года, а затем добавить соответствующее количество недель. Вот пример кода. Он предполагает номер недели, начиная с 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); } }
лично я бы воспользовался информацией о культуре, чтобы получить день недели и перейти к первому дню недели культуры. Я не уверен, если я объясняю это правильно, вот пример:
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; }
согласно ISO 8601: 1988 , который используется в Швеции первая неделя года-это первая неделя, которая имеет по крайней мере четыре дня в Новый год.
Так что если ваша неделя начинается в понедельник первый четверг любого года в течение первой недели. Вы можете DateAdd или DateDiff от этого.
использование Fluent DateTime http://fluentdatetime.codeplex.com/
var year = 2009; var firstDayOfYear = new DateTime(year, 1, 1); var firstMonday = firstDayOfYear.Next(DayOfWeek.Monday); var weeksDateTime = 12.Weeks().Since(firstMonday);
свободный библиотека периодов времени для .NET включает в себя ISO 8601 соответствуют класс неделя:
// ---------------------------------------------------------------------- public static DateTime GetFirstDayOfWeek( int year, int weekOfYear ) { return new Week( year, weekOfYear ).FirstDayStart; } // GetFirstDayOfWeek
этот работал для меня, он также имеет преимущество ожидать cultureinfo в качестве параметра для проверки формулы с различными культурами. Если он пуст, он получает текущую информацию о культуре... допустимые значения: "it", "en-us", "fr",... и так далее. Хитрость заключается в том, чтобы вычесть номер недели первого дня года, которые могут быть 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
вот метод, который совместим с номерами недель, которые 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); }
предполагая, что номер недели начинается с 1
DateTime dt = new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1)); return dt.AddDays(-(int)dt.DayOfWeek);
Это должно дать вам первый день в неделю. Я не делал много тестов на нем, но похоже, что это работает. Это меньше, чем большинство других, я нашел в интернете, поэтому хотел поделиться.
слегка изменен код Микаэля Свенсона. Я нашел неделю первого понедельника и соответствующее изменение номера недели.
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; }
Я пробовал некоторые коды выше, и у некоторых есть небольшие ошибки, когда вы пробуете разные годы с разными стартовыми днями недели, вы увидите их, я взял код Джона Скита, исправил его, и он работает, очень простой код.
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
хорошая новость! А pull-запрос добавлять
System.Globalization.ISOWeek
чтобы .NET Core был просто объединен и в настоящее время запланирован для выпуска 3.0. Надеюсь, он будет распространяться на другие платформы .NET в не слишком отдаленном будущем.вы должны быть в состоянии использовать
ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)
метод для вычисления этого.Вы можете найти исходный код здесь.
для преобразования в обоих направлениях, смотрите здесь:статья в Википедии о датах недели ISO
Я немного улучшил решение Томаса с переопределением:
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; } */
в противном случае дата предшествовала одной неделе..
спасибо Томас, большая помощь.
я использовал одно из решений, но это дало мне неправильные результаты, просто потому, что он считает воскресенье как первый день недели.
Я поменял:
var firstDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber - 1) * 7); var lastDay = firstDay.AddDays(6);
to:
var lastDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber) * 7); var firstDay = lastDay.AddDays(-6);
и теперь он работает как шарм.
предложенное решение не является полным - оно работает только для CalendarWeekRule.Первая неделя. Другие типы правил недели не работают. Это можно увидеть, используя этот тестовый случай:
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); } }
Я сделал уточненный вариант предлагаемого решения, который является более простым и параметризует 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; }
Это мое решение, когда мы хотим вычислить дату, год, номер недели и день недели.
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); }
я упростил код Микаэль Свенсон при условии, что это правильно для многих стран Европы.
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)); }
Я написал и протестировал следующий код, и это работает прекрасно для меня. Пожалуйста, дайте мне знать, если кто-то сталкивается с проблемой с этим, я разместил вопрос, а для того, чтобы получить наилучший ответ. Кто-то может найти это полезным.
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; }
В настоящее время нет класса 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; }