【问题标题】:How do you get a date's calendar week number like Outlook does?您如何像 Outlook 一样获得日期的日历周数?
【发布时间】:2020-03-14 16:55:45
【问题描述】:

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

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

  • CalendarCultureInfo.GetCulture("it-IT")返回
  • GregorianCalendar
  • ISOWeek

我尝试的一些日期是:

  • 200 年 12 月 31 日
  • 2021 年 3 月 29 日
  • 2012 年 12 月 31 日
  • 1992 年 12 月 21 日
  • 2019 年 12 月 30 日

我发现获得正确周数的唯一可靠方法是自己计算,但我真的不喜欢必须依赖我的日期计算代码的想法(我不能 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);
        }

【问题讨论】:

  • 您阅读stackoverflow.com/questions/11154673/… 的答案了吗?我认为该页面上扩展方法的输出符合您的要求。
  • @PranavNegandhi 似乎它使用的是由静态类ISOWeek 实现的ISO8601 周编号,我尝试过但无济于事。此外,ISO 周编号不允许在一年中拆分周,这似乎是我的 Outlook 日历和意大利日历正在做的事情
  • 您是否尝试过使用CalendarWeekRule.FirstFourDayWeek
  • @AimusSage 是的,同样没有多大成功。我已更新问题以添加此详细信息

标签: c# date .net-core calendar


【解决方案1】:

我根据您的要求调整了上面链接的答案。结果与您在上面提供的所有手动计算相匹配。

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

【讨论】:

  • 您所做的基本上是计算星期四、星期五等。如果日期不在这些日子之一,并获取新日期的周数?据您所知,这是否有特殊原因,以及为什么 Calendar 类还没有这样做?
  • 我自己还不是很清楚。这段代码是从另一个答案中抄袭而来的,我所做的只是将 GetWeekOfYear 的参数更改为 CalendarWeekRule.FirstDay。
  • 标记为答案,作为参考,这里有一篇博文解释并显示了您在此处提供的相同代码:docs.microsoft.com/en-us/archive/blogs/shawnste/…
猜你喜欢
  • 1970-01-01
  • 2020-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-12
  • 2021-12-31
  • 2022-01-05
  • 1970-01-01
相关资源
最近更新 更多