您如何像 Outlook 一样获取日期的日历周数?

How do you get a date's calendar week number like Outlook does?

我必须使用 Outlook 中使用的相同编号显示日期的日历周。
编号必须遵循意大利 (it-IT) 文化。
Outlook 日历配置为使用星期一作为一周的第一天,并且“1 月 1 日始终在第 1 周”。
使用相同编号逻辑 is here 的另一个日历示例(也许您需要打开周数)。

我尝试了不同的方法,但我没有得到正确的星期数和一些日期。
特别是,我尝试使用:

我试过的一些日期是:

我发现获得正确周数的唯一可靠方法是自己计算,但我不太喜欢这个必须依赖于我的日期计算代码的想法(我不能 100% 确定找到正确的逻辑我无法测试每个可能的日期)。

下面有一个程序显示了我尝试过的每种方法的各种结果以及我如何手动计算周数。
You can also try it online here.

示例的输出(作为显示各种颜色的图像):

所以问题是:是否有一种内置方法可以使用 Outlook 使用的相同编号逻辑从日期中获取周数?

编辑:

我已经尝试使用 CalendarWeekRule.FirstFourDayWeekCalendarWeekRule.FirstFullWeek

我提到的示例程序:

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

我改编了上面链接的答案以满足您的要求。结果与您在上面提供的所有手动计算相匹配。

using System;
using System.Globalization;

public class Program
{
    public static void Main()
    {
        var today = new DateTime(2000, 12, 31);
        Console.WriteLine(string.Format("{0} {1} {2} {3}", today, today.GetWeekOfYear("it-it"), today.DayOfWeek, today.DayOfYear));
        today = new DateTime(2021, 3, 29);
        Console.WriteLine(string.Format("{0} {1} {2} {3}", today, today.GetWeekOfYear("it-it"), today.DayOfWeek, today.DayOfYear));
        today = new DateTime(2012, 12, 31);
        Console.WriteLine(string.Format("{0} {1} {2} {3}", today, today.GetWeekOfYear("it-it"), today.DayOfWeek, today.DayOfYear));
        today = new DateTime(1992, 12, 21);
        Console.WriteLine(string.Format("{0} {1} {2} {3}", today, today.GetWeekOfYear("it-it"), today.DayOfWeek, today.DayOfYear));
        today = new DateTime(2009, 12, 30);
        Console.WriteLine(string.Format("{0} {1} {2} {3}", today, today.GetWeekOfYear("it-it"), today.DayOfWeek, today.DayOfYear));
        today = new DateTime(2013, 1, 1);
        Console.WriteLine(string.Format("{0} {1} {2} {3}", today, today.GetWeekOfYear("it-it"), today.DayOfWeek, today.DayOfYear));
    }
}

static class DateTimeExtensions
{
    public static int GetWeekOfYear(this DateTime time, string languageTag)
    {
        // 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
        var culture = CultureInfo.GetCultureInfoByIetfLanguageTag(languageTag);
        var day = culture.Calendar.GetDayOfWeek(time);
        if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
        {
            time = time.AddDays(3);
        }

        // Return the week of our adjusted day
        return culture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
    }
}