I have to show a date's calendar week using the same numbering used in Outlook.
The numbering has to follow the italian (it-IT) culture.
The Outlook calendar is configured using Monday as first day of week and "1st of January is always on Week 1".
An example of another calendar using the same numbering logic is here (maybe you need to turn week numbers on).
I've tried different methods but I don't get the correct week number with some dates.
In particular, I have tried using:
Calendar
returned fromCultureInfo.GetCulture("it-IT")
GregorianCalendar
ISOWeek
Some of the dates I tried are:
- 31st December 200
- 29th March 2021
- 31 December 2012
- 21 December 1992
- 30 December 2019
The only reliable way I found to get the correct week number is to calculate it myself, but I don't really like the idea to have to depend on my date calculations code (I can't be 100% sure to have found the correct logic has I can't test every possible date).
There is a program below showing the various results for every method I tried and how I manually calculate week numbers.
You can also try it online here.
The output of the example (as an image to show the various colors):
So the question is: is there a built-in way to get Week numbers from a date using the same numbering logic used by Outlook ?
EDIT:
I've already tried to use CalendarWeekRule.FirstFourDayWeek
and CalendarWeekRule.FirstFullWeek
The example program I mentioned:
public sealed class Program
{
private static Calendar italianCalendar = CultureInfo.GetCultureInfo("it-IT").Calendar;
private static Calendar plainGregorianCalendar = new GregorianCalendar();
public static void Main()
{
var _31stOfDecember2000 = new DateTime(2000, 12, 31);
var _29thOfMarch2021 = new DateTime(2021, 3, 29);
var _31stOfDecember2012 = new DateTime(2012, 12, 31);
var _21stDecember1992 = new DateTime(1992, 12, 21);
var _30thDecember2019 = new DateTime(2019, 12, 30);
var _1stOfJanuary2013 = new DateTime(2013, 1, 1);
PrintWeek(_31stOfDecember2000, 53);
PrintWeek(_29thOfMarch2021, 14);
PrintWeek(_31stOfDecember2012, 1);
PrintWeek(_21stDecember1992, 52);
PrintWeek(_30thDecember2019, 1);
PrintWeek(_1stOfJanuary2013, 1);
Console.ReadKey();
}
private static void PrintWeek(DateTime date, int expectedWeek)
{
var isoWeek = ISOWeek.GetWeekOfYear(date);
var italianCalendarWeek = italianCalendar.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
var gregorianCalendarWeek = plainGregorianCalendar.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
var manuallyCalculatedWeek = CalculateWeek(date);
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Date: {0:yyyy MMMM dd} - Expected week {1}", date, expectedWeek);
Console.ForegroundColor = isoWeek == expectedWeek ? ConsoleColor.Green : ConsoleColor.Red;
Console.WriteLine("ISO Week: {0}", isoWeek);
Console.ForegroundColor = italianCalendarWeek == expectedWeek ? ConsoleColor.Green : ConsoleColor.Red;
Console.WriteLine("Italian calendar Week: {0}", italianCalendarWeek);
Console.ForegroundColor = gregorianCalendarWeek == expectedWeek ? ConsoleColor.Green : ConsoleColor.Red;
Console.WriteLine("Gregorian calendar Week: {0}", gregorianCalendarWeek);
Console.ForegroundColor = manuallyCalculatedWeek == expectedWeek ? ConsoleColor.Green : ConsoleColor.Red;
Console.WriteLine("Manually calculated Week: {0}", manuallyCalculatedWeek);
Console.WriteLine();
}
public static int CalculateWeek(DateTime date)
{
var firstDayOfFirstWeekOfDateYear = StartOfWeekOfYear(date.Year, 1);
var firstDayOfFirstWeekOfNextYear = StartOfWeekOfYear(date.Year + 1, 1);
var lastDayOfLastWeekOfDateYear = firstDayOfFirstWeekOfNextYear.AddDays(-1);
var timePassedSinceFirstWeek = date - firstDayOfFirstWeekOfDateYear;
var daysPassedSinceFirstWeek = timePassedSinceFirstWeek.Days;
var timePassedBetweenFirstAndEndOfYear = lastDayOfLastWeekOfDateYear - firstDayOfFirstWeekOfDateYear;
var daysPassedBetweenFirstAndEndOfYear = timePassedBetweenFirstAndEndOfYear.Days;
var weeksOfDateYear = (daysPassedBetweenFirstAndEndOfYear / 7) + 1;
//If the week number surpasses the effective number of weeks of the year it is wrapped around the next year using modulo operator
//Modulo will wrap the week number around the Year weeks leaving the reminder of the calculation as the Week number of the next year.
var week = (daysPassedSinceFirstWeek / 7 % weeksOfDateYear) + 1;
return week;
}
private static DateTime StartOfWeekOfYear(int year, int week)
{
var firstOfJanuary = new DateTime(year, 1, 1);
var dayOfWeekFirstOfJanuary = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(firstOfJanuary);
//Sunday is 0 instead of 7, screwing calculations over
var dayOfWeekOffset = dayOfWeekFirstOfJanuary == DayOfWeek.Sunday ? -6 : DayOfWeek.Monday - dayOfWeekFirstOfJanuary;
var daysOffset = (7 * (week - 1)) + dayOfWeekOffset;
return firstOfJanuary.AddDays(daysOffset);
}