Get the correct week number of a given date

2019-01-01 02:21发布

I have Googled a lot and found a lot of solutions, but none of them give me the correct week number for the 2012-12-31. Even the example on MSDN (link) fails.

2012-12-31 is Monday, therefore it should be Week 1, but every method I tried gives me 53. Here are some of the methods, that I have tried:

From the MDSN Library:

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

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

Solution 2:

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

Solution 3:

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

Update

The following method actually returns 1 when date is 2012-12-31. In other words, my problem was that my methods were not following the ISO-8601 standard.

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

11条回答
余生无你
2楼-- · 2019-01-01 02:36

I'm going to play Necromancer here :-)

Since there doesn't seem to be a .Net-culture that yields the correct ISO-8601 week number, I'd rather bypass the built-in week determination altogether, and do the calculation manually, instead of attempting to correct a partially correct result.

What I ended up with is the following extension method:

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

First of all, ((int)date.DayOfWeek + 6) % 7) determines the weekday number, 0=monday, 6=sunday.

date.AddDays(-((int)date.DayOfWeek + 6) % 7) determines the date of the monday preceiding the requested week number.

Three days later is the target thursday, which determines what year the week is in.

If you divide the (zero based) day-number within the year by seven (round down), you get the (zero based) week number in the year.

In c#, integer calculation results are round down implicitly.

查看更多
忆尘夕之涩
3楼-- · 2019-01-01 02:37

As noted in this MSDN page there is a slight difference between ISO8601 week and .Net week numbering.

You can refer to this article in MSDN Blog for a better explanation: "ISO 8601 Week of Year format in Microsoft .Net"

Simply put, .Net allow weeks to be split across years while the ISO standard does not. In the article there is also a simple function to get the correct ISO 8601 week number for the last week of the year.

Update The following method actually returns 1 for 2012-12-31 which is correct in ISO 8601 (e.g. Germany).

// 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);
} 
查看更多
伤终究还是伤i
4楼-- · 2019-01-01 02:45
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;

It works well both for US and Russian cultures. ISO 8601 also will be correct, `cause Russian week starts at Monday.

查看更多
残风、尘缘若梦
5楼-- · 2019-01-01 02:46

This is the way:

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

Most important for is the CalendarWeekRule parameter.

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

查看更多
伤终究还是伤i
6楼-- · 2019-01-01 02:51

A year has 52 weeks and 1 day or 2 in case of a lap year (52 x 7 = 364). 2012-12-31 would be week 53, a week that would only have 2 days because 2012 is a lap year.

查看更多
登录 后发表回答