Anyone know an easy way to get the date of the first day in the week (monday here in Europe). I know the year and the week number? I'm going to do this in C#.
I had issues with the solution by @HenkHolterman even with the fix by @RobinAndersson.
Reading up on the ISO 8601 standard resolves the issue nicely. Use the first Thursday as the target and not Monday. The code below will work for Week 53 of 2009 as well.
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);
}
1 hour
in time part (for CET/CEST time zone). I think this amount of hours must be subtracted in result date. –
Zared Note
The below answer uses the .NET Calendar rules. It does not promise ISO8601 conformance. See some of the other answers here when you need that. Week numbering is a mess, always try to find out what rules you need to follow first.
The code below correctly puts the start of week 1, 2009 at 29-12-2008. The CalendarWeekRule probably should be a parameter.
Note that the weekNum should be >= 1
static DateTime FirstDateOfWeek(int year, int weekNum, CalendarWeekRule rule)
{
Debug.Assert(weekNum >= 1);
DateTime jan1 = new DateTime(year, 1, 1);
int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
DateTime firstMonday = jan1.AddDays(daysOffset);
Debug.Assert(firstMonday.DayOfWeek == DayOfWeek.Monday);
var cal = CultureInfo.CurrentCulture.Calendar;
int firstWeek = cal.GetWeekOfYear(firstMonday, rule, DayOfWeek.Monday);
if (firstWeek <= 1)
{
weekNum -= 1;
}
DateTime result = firstMonday.AddDays(weekNum * 7);
return result;
}
CalendarWeekRule.FirstFourDayWeek
and year 2006 and week 32, the correct date to be returned is 2006, 7th of august. But it returns the 14th of august. I changed the GetWeekOfYear call to use firstMonday instead of jan1 and it seemed to work –
Accidence FirstDateOfWeek(2011, 1, CalendarWeekRule.FirstDay)
returns 27th of december 2010 –
Amalee UPDATE: .NET Core 3.0 and .NET Standard 2.1 has shipped with this type.
Good news! A pull request adding System.Globalization.ISOWeek
to .NET Core was just merged and is currently slated for the 3.0 release. Hopefully it will propagate to the other .NET platforms in a not-too-distant future.
You should be able to use the ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)
method to calculate this.
You can find the source code here.
I like the solution provided by Henk Holterman. But to be a little more culture independent, you have to get the first day of the week for the current culture ( it's not always monday ):
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);
}
firstMonday
is a bad variable name for something that might not be a Monday. =) –
Theolatheologian The easiest way is probably to find the first Monday of the year, and then add the relevant number of weeks. Here's some sample code. It assumes a week number starting at 1, by the way:
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);
}
}
Personally I'd take advantage of the culture info to get the day of the week and loop down to the culture's first day of the week. I'm not sure if I'm explaining it properly, here's an example:
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;
}
using 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);
Here is a method that is compatible with the week numbers that Google Analytics, and also the same numbering scheme we used internally at Intel, and which I'm sure is also used in a lot of other contexts.
// 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);
}
Lightly changed Mikael Svenson code. I found the week of the first monday and appropriate change the week number.
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;
}
According to ISO 8601:1988 that is used in Sweden the first week of the year is the first week that has at least four days within the new year.
So if your week starts on a Monday the first Thursday any year is within the first week. You can DateAdd or DateDiff from that.
Assuming the week number starts at 1
DateTime dt = new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1));
return dt.AddDays(-(int)dt.DayOfWeek);
This should give you the first day in any given week. I haven't done a lot of testing on it, but looks like it works. It's smaller solution than most other's I found on the web, so wanted to share.
new DateTime(1,1,YearNumber)
–
Deprived The free Time Period Library for .NET includes the ISO 8601 conform class Week:
// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek
I tried some codes above and some have small mistakes, when you try different years with different starting days of week you will see them, I took the code of Jon Skeet, fix it and it works, very simple code.
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
This one worked for me, it also have the advantage of expecting a cultureinfo as parameter to test the formula with different cultures. If empty, it gets the current culture info... valid values are like: "it", "en-us", "fr", ... ando so on. The trick is to subtract the week number of the first day of the year, that may be 1 to indicate that the first day is within the first week. Hope this helps.
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
I simplified the code Mikael Svensson provided which is correct for many countries in Europe.
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));
}
As suggested by khellang (sorry but I cannot add comments), with .NET Core is quite simple. This is my piece of code:
var today = DateTime.Today;
CultureInfo myCI = new CultureInfo("it-IT");
Calendar myCal = myCI.Calendar;
var weekNumber = myCal.GetWeekOfYear(today, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
var firstWeekDay = ISOWeek.ToDateTime(today.Year, weekNumber, DayOfWeek.Monday);
Week 1 is defined as being the week that starts on a Monday and contains the first Thursday of the year.
To convert in both directions, see here: Wikipedia article on ISO week dates
I improved a little on Thomas' solution with an override:
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;
}
*/
Otherwise the date was preceding by one week..
Thank you Thomas, great help.
I used one of the solutions but it gave me wrong results, simply because it counts Sunday as a first day of the week.
I changed:
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);
and now it is working as a charm.
The proposed solution is not complete - it only works for CalendarWeekRule.FirstFullWeek. Other types of week rules do not work. This can be seen using this test case:
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);
}
}
I have made a refined version of the proposed solution that is a simpler and parametrises the 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;
}
this is my solution when we want to calculate a date given year, week number and day of the week.
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);
}
I have written and tested the following code and is working perfectly fine for me. Please let me know if anyone face trouble with this, I have posted a question as well in order to get the best possible answer. Someone may find it useful.
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)
for Week 29 of 2020 this returns 07/20/2020
. But first day of 29th week was 07/13/2020
–
Felix Currently, there is no C# class that correctly handles ISO 8601week numbers. Even though you can instantiate a culture, look for the closest thing and correct that, I think it is better to do the complete calculation yourself:
/// <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;
}
One of the biggest problems I found was to convert from weeks to dates, and then from dates to weeks.
The main problem is when trying to get the correct week year from a date that belongs to a week of the previous year. Luckily System.Globalization.ISOWeek.GetYear
handles this.
Here is my solution:
public class WeekOfYear
{
public static (int Year, int Week) DateToWeekOfYear(DateTime date) =>
(ISOWeek.GetYear(date), ISOWeek.GetWeekOfYear(date));
public static bool ValidYearAndWeek(int year, int week) =>
year >= 1 && year <= 9999 && week >= 1 && week <= 53 // bounds of year/week
&& !(year <= 1 && week <= 1) && !(year >= 9999 && week >= 53); // bounds of DateTime
public int Year { get; }
public int Week { get; }
public virtual DateTime StartOfWeek { get; protected set; }
public virtual DateTime EndOfWeek { get; protected set; }
public virtual IEnumerable<DateTime> DaysInWeek =>
Enumerable.Range(1, 10).Select(i => StartOfWeek.AddDays(i));
public WeekOfYear(int year, int week)
{
if (!ValidYearAndWeek(year, week))
throw new ArgumentException($"DateTime can't represent {week} of year {year}.");
Year = year;
Week = week;
StartOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Monday);
EndOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Sunday).AddDays(1).AddTicks(-1);
}
public WeekOfYear((int Year, int Week) week) : this(week.Year, week.Week) { }
public WeekOfYear(DateTime date) : this(DateToWeekOfYear(date)) { }
}
The second biggest problem was the preference for weeks starting on Sundays in the US.
The solution I cam up with subclasses WeekOfYear
from above, and manages the offset of the in the constructor (which converts week to dates) and DateToWeekOfYear
(which converts from date to week).
public class UsWeekOfYear : WeekOfYear
{
public static new (int Year, int Week) DateToWeekOfYear(DateTime date)
{
// if date is a sunday, return the next week
if (date.DayOfWeek == DayOfWeek.Sunday) date = date.AddDays(1);
return WeekOfYear.DateToWeekOfYear(date);
}
public UsWeekOfYear(int year, int week) : base(year, week)
{
StartOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Monday).AddDays(-1);
EndOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Sunday).AddTicks(-1);
}
public UsWeekOfYear((int Year, int Week) week) : this(week.Year, week.Week) { }
public UsWeekOfYear(DateTime date) : this(DateToWeekOfYear(date)) { }
}
Here is some test code:
public static void Main(string[] args)
{
Console.WriteLine("== Last Week / First Week");
Log(new WeekOfYear(2020, 53));
Log(new UsWeekOfYear(2020, 53));
Log(new WeekOfYear(2021, 1));
Log(new UsWeekOfYear(2021, 1));
Console.WriteLine("\n== Year Crossover (iso)");
var start = new DateTime(2020, 12, 26);
var i = 0;
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-26 - Sat
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-27 - Sun
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-28 - Mon
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-29 - Tue
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-30 - Wed
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-30 - Thu
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-01 - Fri
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-02 - Sat
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-03 - Sun
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-04 - Mon
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-05 - Tue
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-06 - Wed
Console.WriteLine("\n== Year Crossover (us)");
i = 0;
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-26 - Sat
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-27 - Sun
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-28 - Mon
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-29 - Tue
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-30 - Wed
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-30 - Thu
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-01 - Fri
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-02 - Sat
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-03 - Sun
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-04 - Mon
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-05 - Tue
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-06 - Wed
var x = new UsWeekOfYear(2020, 53) as WeekOfYear;
}
public static void Log(WeekOfYear week)
{
Console.WriteLine($"{week} - {week.StartOfWeek:yyyy-MM-dd} ({week.StartOfWeek:ddd}) - {week.EndOfWeek:yyyy-MM-dd} ({week.EndOfWeek:ddd})");
}
public static void Log(DateTime date, WeekOfYear week)
{
Console.WriteLine($"{date:yyyy-MM-dd (ddd)} - {week} - {week.StartOfWeek:yyyy-MM-dd (ddd)} - {week.EndOfWeek:yyyy-MM-dd (ddd)}");
}
© 2022 - 2024 — McMap. All rights reserved.