Several people have noticed that Calendar.GetWeekOfYear() is almost like the ISO 8601 week when passed CalendarWeekRule.FirstFourDayWeek and DayOfWeek.Monday, however it is a little bit different. Specifically ISO 8601 always has 7 day weeks. If the first partial week of a year doesn't contain Thursday, then it is counted as the last week of the previous year. Likewise, if the last week of the previous year doesn't contain Thursday then its treated like the first week of the next year. GetWeekOfYear() has the first behavior, but not the second. Ie:

Date | Day | GetWeekOfYear | ISO 8601 Week |
---|---|---|---|

12/31/2000 | Sunday | 52 of 2000 | 52 of 2000 |

1/1/2001 | Monday | 1 of 2001 | 1 of 2001 |

1/1/2005 | Saturday | 53 of 2004 | 53 of 2004 |

12/31/2007 | Monday | 53 of 2007 | 1 of 2008 |

Notice that the ISO 8601 week at the end of 2007 is different than the GetWeekOfYear() week. For GetWeekOfYear(), both the last week and the first week have fewer than 7 days. Also notice that even though its a 2007 date, its considered the first week of 2008. Similarly the first day of 2005 is considered to be the last week of 2004 by either method.

A simple workaround to consistently get the ISO 8601 week is to realize that consecutive days Monday through Sunday in ISO 8601 weeks all have the same week #. So Monday has the same week # as Thursday. Since Thursday is the critical day for determining when the week starts each year my solution is to add 3 days if the day is Monday, Tuesday or Wednesday. The adjusted days are still in the same week, and use values that GetWeekOfYear and ISO 8601 agree on.

Note that if the requirement is to compute a date in the 2004W536 form, the code will still have to detect that a week 53 in January means that we need to decrement the year by 1, and a week 1 in December requires incrementing the year by 1.

Here's my example. I made this a complete program, however GetIso8601WeekOfYear() is the worker function. I used a static calendar, which probably isn't necessary. Bala pointed out that one could derive a class from GregorianCalendar and override GetWeekOfYear(), but I'm not sure what the repercussions of using such a calendar elsewhere would be. You can try it if you want, but for now this sample is just a simple static method. Main is just here to run through a bunch of days and show when the week calculations differ.

using System;

using System.Globalization;

class Test

{

// Need a calendar. Culture's irrelevent since we specify start day of week

private static Calendar cal = CultureInfo.InvariantCulture.Calendar;

// 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 = cal.GetDayOfWeek(time);

if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)

{

time = time.AddDays(3);

}

// Return the week of our adjusted day

return cal.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

}

static void Main(string[] args)

{

// 1/1/1990 starts on a Monday

DateTime dt = new DateTime(1990, 1, 1);

Console.WriteLine("Starting at " + dt + " day: " + cal.GetDayOfWeek(dt) + " Week: " +GetIso8601WeekOfYear(dt));

for (int i = 0; i < 100000; i++)

{

for (int days = 0; days < 7; dt=dt.AddDays(1), days++)

{

if (GetIso8601WeekOfYear(dt) != cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday))

{

Console.WriteLine("Iso Week " + GetIso8601WeekOfYear(dt) +

" GetWeekOfYear: " + cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday) +

" Date: " + dt + " Day: " + cal.GetDayOfWeek(dt));

}

}

}

}

}

The other day, colleague Shawn Steele posted in his blog about the ISO 8601 Week of Year format in Microsoft…

Hello, just an extended version with the year out parameter …

Neat, and simple! But how would one go about this the other way? taking an ISO8601 week date into a .NET/CLR DateTime struct again.

Regards,

hello sir/madam

i have seen u r code and MS

just im want to know which is right

i want the number of week year for a particular year

which code i wil use

/// <summary>

/// Get the last WeekNumber of the Current Year.

/// </summary>

/// <returns>

/// LastWeek Number

/// </returns>

public int LastWeekOfYear()

{

// Last day of year

DateTime dt = new DateTime(DateTime.Now.Year, 12, 31);

// Last week of year

int lastWeek = GetIso8601WeekOfYear(dt);

if (lastWeek == 1)

{

/// 31-12 is week 1 set the date a week back and get the last week.

dt = dt.AddDays(-7);

lastWeek = GetIso8601WeekOfYear(dt);

}

return lastWeek;

}

Wijnand : The last ISO week number of a year number is that of YYYY-12-28; no need to fumble.

shawnste : The check in the code should be for at least 365.2425*400 days, say 150000 days. MS VBS DatePart for WN gives two types of error, one being once in 400 years.

My cited index links to a number of date pages.

Jerome B : Right. An ISO WN routine needs to give also YN, and might as well give DN too. It should be easy enough to write such *without* using GetWeekOfYear. Algorithm is on my site, but I don’t have or know Powershell/.NET.

John : my site includes reverse algorithm, in JavaScript.

Can this method be used to get weeks where first day of week is sunday as well, or is it just to be used where monday is first day of week?

You could do the same thing with Sunday (if you wanted the week number to roll over to the next year so there weren’t partial week numbers), but then it wouldn’t be ISO 8601. The ISO standard starts with Mondays.

This didn’t work for me for friday, saturday and sundays at the start of this year.

based on ISO-Week numbering (a tad different from ISO 8601, or so i’m told).

i came up with the idea that calculating from thursdays should always work.

i’ve modified your code to this:

protected void Selected(DateTime time)

{

System.Globalization.Calendar cal = CultureInfo.InvariantCulture.Calendar;

DateTime calculatedate = time;

DayOfWeek day = cal.GetDayOfWeek(time);

if (day == DayOfWeek.Monday) { calculatedate = calculatedate.AddDays(3); }

if (day == DayOfWeek.Tuesday) { calculatedate = calculatedate.AddDays(2); }

if (day == DayOfWeek.Wednesday) { calculatedate = calculatedate.AddDays(1); }

if (day == DayOfWeek.Friday) { calculatedate = calculatedate.AddDays(-1); }

if (day == DayOfWeek.Saturday) { calculatedate = calculatedate.AddDays(-2); }

if (day == DayOfWeek.Sunday) { calculatedate = calculatedate.AddDays(-3); }

// Return the week of our adjusted day

int i = cal.GetWeekOfYear(calculatedate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

string s = string.Format(“Date : {0:d}t Day : {1}t Week Number: {2}”,time,time.DayOfWeek.ToString(),i);

listBox1.Items.Add(s);

}

in VB it would be:

Dim cal As System.Globalization.Calendar = CultureInfo.InvariantCulture.Calendar

Dim time As DateTime = Now

Dim day As DayOfWeek = time.DayOfWeek()

Dim CalculateDate As Date = time

If day = DayOfWeek.Monday Then CalculateDate = CalculateDate.AddDays(3)

If day = DayOfWeek.Tuesday Then CalculateDate = CalculateDate.AddDays(2)

If day = DayOfWeek.Wednesday Then CalculateDate = CalculateDate.AddDays(1)

If day = DayOfWeek.Friday Then CalculateDate = CalculateDate.AddDays(-1)

If day = DayOfWeek.Saturday Then CalculateDate = CalculateDate.AddDays(-2)

If day = DayOfWeek.Sunday Then CalculateDate = CalculateDate.AddDays(-3)

Dim i As Integer = cal.GetWeekOfYear(CalculateDate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)

Thanks Keno, this helps

//variant of code posted by Keno (Jan 06, 2010)

//C#

private int weekOfYear(DateTime date, DayOfWeek firstDayOfWeek)

{

Calendar cal = CultureInfo.InvariantCulture.Calendar;

DayOfWeek day = cal.GetDayOfWeek(date);

date = date.AddDays(4 – ((int)day == 0 ? 7 : (int)day));

return cal.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, firstDayOfWeek);

}

// Provides for variable first day of week and smaller footprint

I haven't even looked at the workaround. My simple question is: Why? Why is there any difference? Who benefits from it? Why bother to *slightly* change an ISO standard?

It isn't an ISO standard rule, but people want the ISO standard rule, so this discusses ways to do that.

Adding a rule for ISO 8601 would be quite useful though. Although given the signature of the GetWeekOfYear() method I suspect you'd need a method explicitly for ISO 8601, as the first day of the week isn't a user option, it's defined by the standard.

Jerome B: Your method that returns the year accounts for the last few days of the year being included in the first week of the next year, but not the other case of the first few days of a year being included in the last week of the previous year:

…

else if (weekNumber == 1 && date.Month == 12)

++year;

To Cary -> as the year is extracted from the modified date (days + 3), the few days of last year included in week 1 will have their year adjusted already.

You could also:

///

/// Converts a date to a week number.

/// ISO 8601 week 1 is the week that contains the first Thursday that year.

///

public static int ToIso8601Weeknumber(this DateTime date)

{

var thursday = date.AddDays(3 – date.DayOfWeek.DayOffset());

return (thursday.DayOfYear – 1) / 7 + 1;

}

///

/// 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.

///

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

}

///

/// Iso8601 weeks start on Monday. This returns 0 for Monday.

///

private static int DayOffset(this DayOfWeek weekDay)

{

return ((int)weekDay + 6) % 7;

}

Shouldn’t this be going the other way, with Microsoft actually making this a standard part of the build, rather than having to mangle a solution? The same with being forced into having Sunday as the start of the week.

FWIW, DateTimeFormatInfo has a FirstDayOfWeek.

Yes, the ISO8601 calculation is problematic. “GetWeekOfYear()” cannot really change because that would break everyone that has used it the way it has been for over a decade. It might be reasonable to add a “GetIsoWeekOfYear()” or some such.