获取给定日期的正确星期数


222

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

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

4
年底的第一周怎么样?我的意思是,我知道您从哪里得到。但是53对我来说很有意义。
benjer3 2012年

1
在代码片段中,我获得了CultureInfo和其他内容。我以为当时我的程序就知道我正在使用什么压延机。(在德国,2012年12月31日在2013年的第1周)
Amberlamps 2012年

该代码无法正常运行,因为它应尝试例如日期为2016年12月31日或2016
Cavej03

@ cavej03 2016年12月31日是第52周,GetIso8601WeekOfYear返回52,所以我想它可以正常工作。
Muflix '16

Answers:


310

如该MSDN页面所述,ISO8601周和.Net周编号之间存在细微差异。

您可以在MSDN博客中参考本文以获得更好的解释:“ Microsoft .Net中的ISO 8601年度格式

简而言之,.Net允许将数年的时间分成数年,而ISO标准则不允许。在文章中,还有一个简单的函数可以为一年的最后一周获取正确的ISO 8601周编号。

更新下面的方法实际上返回1,2012-12-31在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);
} 

1
@il_guru如果我希望一周从星期日开始,该怎么办?我是否将所有“星期一”替换为“星期日”?
User2012384 2013年

2
@ User2012384 fyi,如果要遵循ISO8601标准,则firstDayOfWeek应该始终为星期一。
Starceaker '16

1
@il_guru如果不需要,您甚至不需要此“作弊”功能?据我所知,“ GetWeekOfYear(时间,CalendarWeekRule.FirstFourDayWeek,DayOfWeek.Monday);” 效果很好。
Starceaker '16

2
如果您查找第二个链接(Microsoft .Net中的ISO 8601年度格式),则可以直接从Microsoft找到问题的答案。例如,2007
12

3
我已经手动验证了2012年至2018年的正确性(假设epochconverter.com是正确的)。今年我们都应该为2018年第1周实际上从1月1日开始感到高兴!
艾丹(Aidan)

32

一年可能超过52周。每年有52个整周+ 1或+2(le年)天。他们组成了第53周。

  • 52周* 7天= 364天。

因此,每年您至少要多留一天。for年两次。这些额外的日子是否算作自己单独的几周?

真正有几周取决于您一周的开始日期。让我们考虑一下2012年。

  • 美国(周日->星期六):52周+ 2012年12月30日和2012年12月31日的1天2天。结果是总共53周。今年的最后两天(周日+周一)构成了自己的短期一周。

检查您当前的文化设置,以了解一周中第一天使用的文化。

如您所见,得到53是正常的。

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

甚至有可能有第54周的时间。将1月1日和12月31日视为单独的一周时,每28年发生一次。它也必须是a年。

例如,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周的一部分?
Amberlamps 2012年

我从未见过第54周!
Amberlamps 2012年

某些财务软件包确实具有它们。
Christophe Geers,2012年

好的,我发现美国压延机有可能,但德国压延机则不会发生。
Amberlamps 2012年

1
+1是您对此问题的深入思考,但博客答案中提供的代码段以不同的答案实际上解决了我的问题。
Amberlamps 2012年

22

这是这样的:

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


3
“有几个人注意到,通过CalendarWeekRule.FirstFourDayWeek和DayOfWeek.Monday时,Calendar.GetWeekOfYear()几乎类似于ISO 8601星期。但是,它有点不同。特别是ISO 8601始终有7天工作周。” blogs.msdn。 microsoft.com/shawnste/2006/01/24/...
尤哈Palomäki

1
您的链接是意大利语的
radbyx

对于新的DateTime(2000,12,31),它仅返回52?
Leszek P

18

好消息!一个拉请求加入System.Globalization.ISOWeek到.NET的核心刚刚合并,目前内定为3.0版本。希望它将在不远的将来传播到其他.NET平台。

该类型具有以下签名,应满足大多数ISO周的需求:

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

您可以在此处找到源代码。

更新:这些API也已包含在2.1版本的.NET Standard中


知道何时发布v 3.0吗?
雅克

“我们计划在今年晚些时候发布.NET Core 3的第一个预览版,并在2019年发布最终版本。” (摘自路线图博客文章
khellang,

@khellang如何根据日期时间获得一个月的第几周?我不想看到第33周,而是希望看到第2周(因为第33周是8月的第2周)。stackoverflow.com/questions/58039103/…–
Roxy'Pro

11

由于似乎没有能够产生正确的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#中,整数计算结果隐式舍入。


1
这是迄今为止最好的解决方案,但我会指出一种稍有不同的方法: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;
Adrian S

5

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

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


3

从上面的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
}

1
您能解释一下您的代码吗?仅代码答案通常在StackOverflow上是不可接受的,因此很容易被删除。
Wai Ha Lee

@Wai Ha Lee代码已被解释。请参见il_guru上的帖子。我将他的代码移植到了powershell中,因此其他人可以在powershell中使用它,因为到目前为止在powershell中还没有好的解决方案。
雷纳

如果那是您所做的,则应在答案本身中注明原始作者。您移植的答案具有更好的注释,可能已将其删除。此外,这也不是问题的答案,因为该问题未提及PowerShell。
Wai Ha Lee

1
我现在做了@Wai Ha Lee。该问题已被标记为已回答,因此这将是另一种语言编写的相同解决方案。我为正在使用Powershell语言的解决方案的人们提供了帮助(我一直在寻找Powershell语言的解决方案,但是我发现的是C#解决方案)。这个想法保持不变,作者为此得到了认可。
雷纳

2

使用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;

1
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也将是正确的,因为俄罗斯周始于星期一。


1

这是il_guru答案的扩展版本和可为空的版本。

延期:

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

0

问题是:您如何定义一周是在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年1月1日这一周说1
Amberlamps

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

0

根据il_guru的回答,我根据自己的需要创建了该版本,该版本还返回了year组件。

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

0

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

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

-1

一年有52周零1天,如果是一圈年则为2天(52 x 7 = 364)。2012年12月31日将是第53周,一周将只有2天,因为2012年是一圈。


这是不正确的。一年中的第一天可能在一周中的任何一天,具体取决于您如何计算周数,因此该年可能有第54周。
krowe2 '18

-2
public int GetWeekNumber()
{
   CultureInfo ciCurr = CultureInfo.CurrentCulture;
   int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, 
   CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
   return weekNum;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.