【问题标题】:Get the correct week number of a given date获取给定日期的正确周数
【发布时间】:2012-06-24 15:54:00
【问题描述】:

我在 Google 上搜索了很多并找到了很多解决方案,但没有一个能提供 2012-12-31 的正确周数。甚至 MSDN (link) 上的示例也失败了。

2012-12-31 是星期一,因此应该是第 1 周,但我尝试的每个方法都给了我 53。以下是我尝试过的一些方法:

来自 MDSN 库:

DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
Calendar cal = dfi.Calendar;

return cal.GetWeekOfYear(date, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);

解决方案 2:

return new GregorianCalendar(GregorianCalendarTypes.Localized).GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

解决方案 3:

CultureInfo ciCurr = CultureInfo.CurrentCulture;
int weekNum = ciCurr.Calendar.GetWeekOfYear(dtPassed, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return weekNum;

更新

当日期为 2012-12-31 时,以下方法实际上返回 1。换句话说,我的问题是我的方法没有遵循 ISO-8601 标准。

// This presumes that weeks start with Monday.
// Week 1 is the 1st week of the year with a Thursday in it.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // 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
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

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

【问题讨论】:

  • 年底第一周怎么样?我的意思是,我知道你从哪里得到它。但 53 对我来说很有意义。
  • 在我的代码 sn-ps 中,我得到了 CultureInfo 和其他东西。我以为我的程序到那时就知道我正在使用什么日历了。 (在德国,2012 年 12 月 31 日是 2013 年的第 1 周)
  • 此代码不能正常工作,因为它应该尝试日期 31-dec-2016 或 1-jan-2016
  • @cavej03 2016 年 12 月 31 日是第 52 周,GetIso8601WeekOfYear 返回 52,所以我猜它工作正常。

标签: c# asp.net .net date


【解决方案1】:

一年有 52 周零 1 天或 2 天(52 x 7 = 364)。 2012-12-31 将是第 53 周,这一周只有 2 天,因为 2012 年是一圈年。

【讨论】:

  • 这是不正确的。一年中一周的第一天可能是一周中的任何一天,这取决于您如何计算周数,一年可能有第 54 周。
【解决方案2】:

问题是:您如何定义一周是在 2012 年还是在 2013 年? 我猜你的假设是,由于一周中有 6 天是在 2013 年,所以这周应该被标记为 2013 年的第一周。

不确定这是否是正确的方法。 该周从 2012 年开始(12 月 31 日星期一),因此应标记为 2012 年的最后一周,因此应为 2012 年 53 日。2013 年的第一周应从 7 日星期一开始。

现在,您可以使用星期几信息来处理边缘周(一年中的第一周和最后一周)的特殊情况。这完全取决于你的逻辑。

【讨论】:

  • 好的,我明白你的意思了。问题是当我在任何方法中使用 2013/01/07 时,它会显示第 2 周。因此,2012/12/31 是第 53 周,2013/01/07 是第 2 周。您可能认为没有第 1 周2013 年。但是当我尝试 2013/01/01 时,它显示的是第 1 周。
【解决方案3】:

一年可以有超过 52 周。每年有 52 个整周 + 1 或 +2 天(闰年)。他们弥补了第 53 周。

  • 52 周 * 7 天 = 364 天。

因此,您每年至少有一个额外的一天。两个闰年。这些额外的天数算作各自的独立周吗?

实际上有多少周取决于您一周的开始日期。让我们考虑一下 2012 年的情况。

  • 美国(星期日 -> 星期六):52 周 + 2012-12-30 和 2012-12-31 一个短的 2 天一周。这总共需要 53 周。今年的最后两天(周日 + 周一)组成了他们自己的短周。

检查您当前的文化设置,看看它使用什么作为一周的第一天。

如您所见,结果是 53 是正常的。

  • 欧洲(星期一 -> 星期日):1 月 2 日 (2012-1-2) 是第一个星期一,所以这是第一周的第一天。询问 1 月 1 日的周数,您会得到 52,因为它被认为是 2011 年最后一周的一部分。

甚至可能有第 54 周。每 28 年发生一次,将 1 月 1 日和 12 月 31 日视为不同的周。也应该是闰年。

例如,2000 年有 54 周。 1 月 1 日(星期六)是第一个工作日,12 月 31 日(星期日)是第二个工作日。

var d = new DateTime(2012, 12, 31);
CultureInfo cul = CultureInfo.CurrentCulture;

var firstDayWeek = cul.Calendar.GetWeekOfYear(
    d,
    CalendarWeekRule.FirstDay,
    DayOfWeek.Monday);

int weekNum = cul.Calendar.GetWeekOfYear(
    d,
    CalendarWeekRule.FirstDay,
    DayOfWeek.Monday);

int year = weekNum == 52 && d.Month == 1 ? d.Year - 1 : d.Year;
Console.WriteLine("Year: {0} Week: {1}", year, weekNum);

打印出来:年份:2012 周:54

将上面示例中的 CalendarWeekRule 更改为 FirstFullWeek 或 FirstFourDayWeek,您将返回 53。我们将开始日期保留在星期一,因为我们正在处理德国。

所以第 53 周从 2012 年 12 月 31 日星期一开始,持续一天然后停止。

53 是正确答案。如果想尝试,请将文化更改为德国。

CultureInfo cul = CultureInfo.GetCultureInfo("de-DE");

【讨论】:

  • 我知道可能有 53 周。但是当你有星期天 (12/30) 和星期一 (12/31) 时,你不把星期二 (01/01) 算作美国第 53 周的一部分吗?
  • 我从未见过第 54 周!
  • 某些财务软件包确实有。
  • 好的,我发现在美国压延机中可以,但在德国压延机中不会发生。
  • +1 表示您对该问题的深入思考,但在不同答案中提供的博客条目中的代码 sn-p 实际上解决了我的问题。
【解决方案4】:

正如MSDN page 中所述,ISO8601 周数和 .Net 周数之间存在细微差别。

你可以参考MSDN博客中的这篇文章以获得更好的解释:“ISO 8601 Week of Year format in Microsoft .Net

简单地说,.Net 允许将数周划分为数年,而 ISO 标准则不允许。 在文章中,还有一个简单的函数可以获取一年中最后一周的正确 ISO 8601 周数。

更新以下方法实际上为2012-12-31 返回 1,这在 ISO 8601(例如德国)中是正确的。

// This presumes that weeks start with Monday.
// Week 1 is the 1st week of the year with a Thursday in it.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // 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
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

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

【讨论】:

  • @il_guru 如果我希望一周从星期日开始怎么办?我是否将所有“星期一”都替换为“星期日”??
  • @User2012384 仅供参考,如果您想遵循 ISO8601 标准,firstDayOfWeek 应该始终是星期一。
  • @il_guru 你甚至不需要这个“作弊”,对吧?据我所知“GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);”工作得很好。
  • 如果您查看第二个链接(Microsoft .Net 中的 ISO 8601 Week of Year 格式),您可以直接从 Microsoft 找到问题的答案。例如 2007 年 12 月 31 日星期一:.Net 函数将返回 2007 年的第 53 周,而对于 ISO 标准,它将返回 2008 年的第 1 周
  • 我已经手动验证了这在 2012 年到 2018 年期间是正确的(假设 epochconverter.com 是正确的)。今年我们都应该为 2018 年的第 1 周实际上从 1 月 1 日开始而感到高兴!
【解决方案5】:
var cultureInfo = CultureInfo.CurrentCulture;
var calendar = cultureInfo.Calendar;

var calendarWeekRule = cultureInfo.DateTimeFormat.CalendarWeekRule;
var firstDayOfWeek = cultureInfo.DateTimeFormat.FirstDayOfWeek;
var lastDayOfWeek = cultureInfo.LCID == 1033 //En-us
                    ? DayOfWeek.Saturday
                    : DayOfWeek.Sunday;

var lastDayOfYear = new DateTime(date.Year, 12, 31);

var weekNumber = calendar.GetWeekOfYear(date, calendarWeekRule, firstDayOfWeek);

 //Check if this is the last week in the year and it doesn`t occupy the whole week
return weekNumber == 53 && lastDayOfYear.DayOfWeek != lastDayOfWeek 
       ? 1  
       : weekNumber;

它适用于美国和俄罗斯文化。 ISO 8601 也是正确的,因为俄罗斯周从星期一开始。

【讨论】:

    【解决方案6】:

    由于似乎没有产生正确 ISO-8601 周数的 .Net 文化,我宁愿完全绕过内置的周确定,并手动进行计算,而不是尝试更正部分正确的结果。

    我最终得到的是以下扩展方法:

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

    首先,((int)date.DayOfWeek + 6) % 7) 确定工作日编号,0=星期一,6=星期日。

    date.AddDays(-((int)date.DayOfWeek + 6) % 7) 确定请求周数之前的星期一的日期。

    三天后是目标星期四,它决定了一周是哪一年。

    如果您将一年中的(从零开始的)天数除以七(向下舍入),您将得到一年中的(从零开始的)周数。

    在c#中,整数计算结果隐式向下舍入。

    【讨论】:

    • 这是迄今为止最好的解决方案,但我会指出一种稍微不同的方法:DateTime 基于预测的公历 - 即它将公历延长到 1 年。 0001 年 1 月 1 日恰好是星期一。 Ticks 是自 0001 年 1 月 1 日以来 100 纳秒增量的数量。假设一天有 86,400 秒,我们可以这样写:var thursday = date.AddDays(3 - date.Ticks / 86400 / 10_000_000 % 7); return (thursday.DayOfYear - 1) / 7 + 1;
    【解决方案7】:

    il_guru 的代码中将 C# 移植到 Powershell:

    function GetWeekOfYear([datetime] $inputDate)
    {
       $day = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetDayOfWeek($inputDate)
       if (($day -ge [System.DayOfWeek]::Monday) -and ($day -le [System.DayOfWeek]::Wednesday))
       {
          $inputDate = $inputDate.AddDays(3)
       }
    
       # Return the week of our adjusted day
       $weekofYear = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetWeekOfYear($inputDate, [System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [System.DayOfWeek]::Monday)
       return $weekofYear
    }
    

    【讨论】:

    • 您能解释一下您的代码吗? StackOverflow 上通常不接受纯代码答案,因此可能会被删除。
    • @Wai Ha Lee 代码已经解释过了。请参阅上面来自 il_guru 的帖子。我把他的代码移植到了powershell,所以其他人可以在powershell中使用,因为powershell目前还没有好的解决方案。
    • 如果您这样做了,您应该在答案本身中注明原作者。您移植的答案具有更好的 cmets,您可能已将其删除。此外,这不是问题的答案,因为问题没有提到 PowerShell。
    • 我现在做到了@Wai Ha Lee。该问题已被标记为已回答,因此这将是用其他语言编写的相同解决方案。我为正在寻找 Powershell 语言解决方案的人们提供了帮助(我一直在努力寻找 Powershell 中的解决方案,但我发现的是 C# 中的解决方案)。这个想法保持不变,作者因此获得了荣誉。
    【解决方案8】:

    就是这样:

    public int GetWeekNumber()
    {
        CultureInfo ciCurr = CultureInfo.CurrentCulture;
        int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        return weekNum;
    }
    

    最重要的是CalendarWeekRule参数。

    请看这里: https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=IT-IT&k=k(System.Globalization.CalendarWeekRule);k(TargetFrameworkMoniker-.NETFramework

    【讨论】:

    • "有几个人注意到 Calendar.GetWeekOfYear() 与通过 CalendarWeekRule.FirstFourDayWeek 和 DayOfWeek.Monday 时的 ISO 8601 周几乎一样,但是有点不同。特别是 ISO 8601 总是有 7星期几。" blogs.msdn.microsoft.com/shawnste/2006/01/24/…
    • 您的链接是意大利语
    • 对于 new DateTime(2000,12,31) 它只返回 52?
    【解决方案9】:
      DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
      DateTime date1 = new DateTime(2011, 1, 1);
      Calendar cal = dfi.Calendar;
    
      Console.WriteLine("{0:d}: Week {1} ({2})", date1, 
                        cal.GetWeekOfYear(date1, dfi.CalendarWeekRule, 
                                          dfi.FirstDayOfWeek),
                        cal.ToString().Substring(cal.ToString().LastIndexOf(".") + 1));      
    

    【讨论】:

      【解决方案10】:

      使用 c# 和 DateTime 类确定周数 ISO 8601 样式的最简单方法。

      问这个:一年中的多少个星期四是本周的星期四。 答案等于所需的周数。

      var dayOfWeek = (int)moment.DayOfWeek;
      // Make monday the first day of the week
      if (--dayOfWeek < 0)
          dayOfWeek = 6;
      // The whole nr of weeks before this thursday plus one is the week number
      var weekNumber = (moment.AddDays(3 - dayOfWeek).DayOfYear - 1) / 7 + 1;
      

      【讨论】:

        【解决方案11】:

        好消息!将 System.Globalization.ISOWeek 添加到 .NET Core 的 pull request 刚刚合并,目前计划发布 3.0。希望它会在不久的将来传播到其他 .NET 平台。

        该类型有以下签名,应该能覆盖ISO week 的大部分需求:

        namespace System.Globalization
        {
            public static class ISOWeek
            {
                public static int GetWeekOfYear(DateTime date);
                public static int GetWeeksInYear(int year);
                public static int GetYear(DateTime date);
                public static DateTime GetYearEnd(int year);
                public static DateTime GetYearStart(int year);
                public static DateTime ToDateTime(int year, int week, DayOfWeek dayOfWeek);
            }
        }
        

        你可以找到源代码here

        更新:这些 API 也是 included in the 2.1 version of .NET Standard

        【讨论】:

        • 知道 v 3.0 何时发布吗?
        • “我们计划在今年晚些时候发布 .NET Core 3 的第一个预览版,并在 2019 年发布最终版本。” (来自the roadmap blog post
        • 请注意,与 Calendar 相比,ISOWeek 是 ISO 8601 的特殊情况答案,因为它仅适用于 CalendarWeekRule.FirstFourDayWeekFirstDayOfWeek = DayOfWeek.Monday
        • @McX 是的,该类型被称为 ISOWeek 的事实可能表明它正在与ISO Weeks 一起使用?
        • 这应该是新的答案。
        【解决方案12】:

        这是il_guruanswer的扩展版本和可空版本。

        扩展:

        public static int GetIso8601WeekOfYear(this DateTime time)
        {
            var day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
            if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
            {
                time = time.AddDays(3);
            }
        
            return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        }
        

        可以为空:

        public static int? GetIso8601WeekOfYear(this DateTime? time)
        {
            return time?.GetIso8601WeekOfYear();
        }
        

        用法:

        new DateTime(2019, 03, 15).GetIso8601WeekOfYear(); //returns 11
        ((DateTime?) new DateTime(2019, 03, 15)).GetIso8601WeekOfYear(); //returns 11
        ((DateTime?) null).GetIso8601WeekOfYear(); //returns null
        

        【讨论】:

          【解决方案13】:

          在 .NET 3.0 及更高版本中,您可以使用 ISOWeek.GetWeekOfDate-Method

          请注意,年份 + 周数格式中的年份可能与 DateTime 的年份不同,因为周跨越了年份边界。

          【讨论】:

            【解决方案14】:

            根据 il_guru 的回答,我根据自己的需要创建了这个版本,它还返回了年份组件。

                /// <summary>
                /// This presumes that weeks start with Monday.
                /// Week 1 is the 1st week of the year with a Thursday in it.
                /// </summary>
                /// <param name="time">The date to calculate the weeknumber for.</param>
                /// <returns>The year and weeknumber</returns>
                /// <remarks>
                /// Based on Stack Overflow Answer: https://stackoverflow.com/a/11155102
                /// </remarks>
                public static (short year, byte week) GetIso8601WeekOfYear(DateTime time)
                {
                    // 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
                    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
                    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
                    {
                        time = time.AddDays(3);
                    }
                    // Return the week of our adjusted day
                    var week = (byte)CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
                    return ((short)(week >= 52 & time.Month == 1 ? time.Year - 1 : time.Year), week);
                }
            

            【讨论】:

              【解决方案15】:
              public int GetWeekNumber()
              {
                 CultureInfo ciCurr = CultureInfo.CurrentCulture;
                 int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, 
                 CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
                 return weekNum;
              }
              

              【讨论】:

                【解决方案16】:

                这两种方法会有所帮助,假设我们的一周从星期一开始

                /// <summary>
                    /// Returns the weekId
                    /// </summary>
                    /// <param name="DateTimeReference"></param>
                    /// <returns>Returns the current week id</returns>
                    public static DateTime GetDateFromWeek(int WeekReference)
                    {
                        //365 leap
                        int DaysOffset = 0;
                        if (WeekReference > 1)
                        {
                            DaysOffset = 7;
                            WeekReference = WeekReference - 1;
                        }
                        DateTime DT = new DateTime(DateTime.Now.Year, 1, 1);
                        int CurrentYear = DT.Year;
                        DateTime SelectedDateTime = DateTime.MinValue;
                
                        while (CurrentYear == DT.Year)
                        {
                            int TheWeek = WeekReportData.GetWeekId(DT);
                            if (TheWeek == WeekReference)
                            {
                                SelectedDateTime = DT;
                                break;
                            }
                            DT = DT.AddDays(1.0D);
                        }
                
                        if (SelectedDateTime == DateTime.MinValue)
                        {
                            throw new Exception("Please check week");
                        }
                
                        return SelectedDateTime.AddDays(DaysOffset);
                    }
                /// <summary>
                    /// Returns the weekId
                    /// </summary>
                    /// <param name="DateTimeReference"></param>
                    /// <returns>Returns the current week id</returns>
                    public static int GetWeekId(DateTime DateTimeReference)
                    {
                        CultureInfo ciCurr = CultureInfo.InvariantCulture;
                        int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTimeReference,
                        CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
                        return weekNum;
                    }
                

                【讨论】:

                  【解决方案17】:

                  如果您没有 .NET 5.0,请扩展 DateTime 类以包含周数。

                  public static class Extension {
                      public static int Week(this DateTime date) {
                          var day = (int)CultureInfo.CurrentCulture.Calendar.GetDayOfWeek(date);
                          return CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date.AddDays(4 - (day == 0 ? 7 : day)), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
                      }
                  }
                  

                  【讨论】:

                    【解决方案18】:

                    在 PowerShell 7.x.y 中: 如果您需要匹配的 WeekYear,您将需要以下两个代码行。

                    [System.Globalization.ISOWeek]::GetWeekOfYear((get-date))
                    [System.Globalization.ISOWeek]::GetYear((get-date))
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2016-03-13
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-11-04
                      • 2017-07-19
                      • 1970-01-01
                      相关资源
                      最近更新 更多