A function to calculate the number of the week, ISO 8601, VB.Net, C# and MFC VC++

Tuesday, June 11, 2013

A lot of functions are posted online, with varying degrees of accuracy, that calculate the number of the week of the year. Having looked around and not finding any simple, yet general, algorithm that would fit our needs, we decided to develop our own.

As a starting point we looked at the Calendar.GetWeekOfYear function that is included in the .NET framework and decided it was a good fit. This function takes three arguments: the date, a rule that controls the first week of the year and the starting day of the week.

Visual Basic .NET and C# do not need a function that calculates the number of the week because you can use the Calendar.GetWeekOfYear function that is already included. But if you intend on having this function in another programming language (Java, SQL, etc.) you might find the source code useful and easier to port. Also we included it if you want to avoid using System.Globalization or further customize the functions.

You will have to define two enumerations, one for the days of the week:

```
//C++ code

typedef enum E_WEEKDAY
{
WD_SUNDAY = 0,
WD_MONDAY = 1,
WD_TUESDAY = 2,
WD_WEDNESDAY = 3,
WD_THURSDAY = 4,
WD_FRIDAY = 5,
WD_SATURDAY = 6
}E_WEEKDAY;

```
```
//C# code

public enum E_WEEKDAY
{
WD_SUNDAY = 0,
WD_MONDAY = 1,
WD_TUESDAY = 2,
WD_WEDNESDAY = 3,
WD_THURSDAY = 4,
WD_FRIDAY = 5,
WD_SATURDAY = 6
}

```
```
'Visual Basic .NET code

Public Enum E_WEEKDAY
WD_SUNDAY = 0
WD_MONDAY = 1
WD_TUESDAY = 2
WD_WEDNESDAY = 3
WD_THURSDAY = 4
WD_FRIDAY = 5
WD_SATURDAY = 6
End Enum

```

And another for the rules that govern the starting week of the year:

```
//C++ code

typedef enum E_CALENDARWEEKRULES
{
CWR_FIRSTDAY = 0,
CWR_FIRSTFULLWEEK = 1,
CWR_FIRSTFOURDAYWEEK = 2,
}E_CALENDARWEEKRULES;

```
```
//C# code

public enum E_CALENDARWEEKRULES
{
CWR_FIRSTDAY = 0,
CWR_FIRSTFULLWEEK = 1,
CWR_FIRSTFOURDAYWEEK = 2
}

```
```
'Visual Basic .NET code

Public Enum E_CALENDARWEEKRULES
CWR_FIRSTDAY = 0
CWR_FIRSTFULLWEEK = 1
CWR_FIRSTFOURDAYWEEK = 2
End Enum

```

These two enumerations map directly to the System.DayOfWeek and System.Globalization.CalendarWeekRule enumerations that are contained in the .NET framework.

Now according to the .NET framework documentation the definition for the CalendarWeekRule enumeration is the following:

Member name Description
FirstDay Indicates that the first week of the year starts on the first day of the year and ends before the following designated first day of the week. The value is 0.
FirstFullWeek Indicates that the first week of the year begins on the first occurrence of the designated first day of the week on or after the first day of the year. The value is 1.
FirstFourDayWeek Indicates that the first week of the year is the first week with four or more days before the designated first day of the week. The value is 2.

The code for calculating the number of the week is:

```
//C++ code

int GetWeekOfYear(COleDateTime time, int rule, int firstDayOfWeek)
{
int lReturn = 0;
COleDateTime dtBase(time.GetYear(), 1, 1, 0, 0, 0);
COleDateTime dtBuff;
int lDay = 0;
int lWeek = 0;
switch (rule)
{
case CWR_FIRSTDAY:
for (lDay = 0; lDay <= 6; lDay++)
{
COleDateTimeSpan oSpan(lDay, 0, 0, 0);
dtBuff = dtBase + oSpan;
if ((dtBuff.GetDayOfWeek() - 1) == firstDayOfWeek)
{
break;
}
}
if ((dtBase.GetDayOfWeek() - 1) == firstDayOfWeek)
{
lReturn = (int)floor(((double)time.GetDayOfYear() - (double)dtBuff.GetDayOfYear()) / 7.0) + 1;
}
else
{
lReturn = (int)floor(((double)time.GetDayOfYear() - (double)dtBuff.GetDayOfYear()) / 7.0) + 2;
}
break;
case CWR_FIRSTFULLWEEK:
for (lDay = 0; lDay <= 6; lDay++)
{
COleDateTimeSpan oSpan(lDay, 0, 0, 0);
dtBuff = dtBase + oSpan;
if ((dtBuff.GetDayOfWeek() - 1) == firstDayOfWeek)
{
break;
}
}
lWeek = (int)floor(((double)time.GetDayOfYear() - (double)dtBuff.GetDayOfYear()) / 7) + 1;
if (lWeek == 0)
{
COleDateTime dtParam(time.GetYear() - 1, 12, 31, 0, 0, 0);
lReturn = GetWeekOfYear(dtParam, CWR_FIRSTFULLWEEK, firstDayOfWeek);
} else
{
lReturn = lWeek;
}
break;
case CWR_FIRSTFOURDAYWEEK:
for (lDay = 0; lDay <= 12; lDay++)
{
COleDateTimeSpan oSpan(lDay, 0, 0, 0);
dtBuff = dtBase + oSpan;
if ((dtBuff.GetDayOfWeek() - 1) == firstDayOfWeek && dtBuff.GetDayOfYear() >= 5)
{
break;
}
}
lWeek = (int)floor(((double)time.GetDayOfYear() - (double)dtBuff.GetDayOfYear()) / 7.0) + 2;
if (lWeek == 0)
{
COleDateTime dtParam(time.GetYear() - 1, 12, 31, 0, 0, 0);
lReturn = GetWeekOfYear(dtParam, CWR_FIRSTFOURDAYWEEK, firstDayOfWeek);
}
else
{
lReturn = lWeek;
}
break;
}
return lReturn;
}

```
```
//C# code

private int GetWeekOfYear(System.DateTime time, E_CALENDARWEEKRULES rule, E_WEEKDAY firstDayOfWeek)
{
int lReturn = 0;
System.DateTime dtBase = new System.DateTime(time.Year, 1, 1);
System.DateTime dtBuff = default(System.DateTime);
int lDay = 0;
int lWeek = 0;
switch (rule)
{
case E_CALENDARWEEKRULES.CWR_FIRSTDAY:
for (lDay = 0; lDay <= 6; lDay++)
{
if ((E_WEEKDAY)dtBuff.DayOfWeek == firstDayOfWeek)
{
break;
}
}

if ((E_WEEKDAY)dtBase.DayOfWeek == firstDayOfWeek)
{
lReturn = (int)System.Math.Floor(((double)time.DayOfYear - (double)dtBuff.DayOfYear) / 7D) + 1;
}
else
{
lReturn = (int)System.Math.Floor(((double)time.DayOfYear - (double)dtBuff.DayOfYear) / 7D) + 2;
}
break;
case E_CALENDARWEEKRULES.CWR_FIRSTFULLWEEK:
for (lDay = 0; lDay <= 6; lDay++)
{
if ((E_WEEKDAY)dtBuff.DayOfWeek == firstDayOfWeek)
{
break;
}
}

lWeek = (int)System.Math.Floor(((double)time.DayOfYear - (double)dtBuff.DayOfYear) / 7D) + 1;
if (lWeek == 0)
{
lReturn = GetWeekOfYear(new System.DateTime(time.Year - 1, 12, 31), E_CALENDARWEEKRULES.CWR_FIRSTFULLWEEK, firstDayOfWeek);
}
else
{
lReturn = lWeek;
}
break;
case E_CALENDARWEEKRULES.CWR_FIRSTFOURDAYWEEK:
for (lDay = 0; lDay <= 12; lDay++)
{
if ((E_WEEKDAY)dtBuff.DayOfWeek == firstDayOfWeek & dtBuff.DayOfYear >= 5)
{
break;
}
}

lWeek = (int)System.Math.Floor(((double)time.DayOfYear - (double)dtBuff.DayOfYear) / 7D) + 2;
if (lWeek == 0)
{
lReturn = GetWeekOfYear(new System.DateTime(time.Year - 1, 12, 31), E_CALENDARWEEKRULES.CWR_FIRSTFOURDAYWEEK, firstDayOfWeek);
}
else
{
lReturn = lWeek;
}
break;
}
return lReturn;
}

```
```
'Visual Basic .NET code

Private Function GetWeekOfYear(ByVal time As System.DateTime, ByVal rule As E_CALENDARWEEKRULES, ByVal firstDayOfWeek As E_WEEKDAY) As Integer
Dim lReturn As Integer = 0
Dim dtBase As System.DateTime = New System.DateTime(time.Year(), 1, 1)
Dim dtBuff As System.DateTime
Dim lDay As Integer
Dim lWeek As Integer = 0
Select Case rule
Case E_CALENDARWEEKRULES.CWR_FIRSTDAY
For lDay = 0 To 6
If dtBuff.DayOfWeek() = firstDayOfWeek Then
Exit For
End If
Next
If dtBase.DayOfWeek = firstDayOfWeek Then
lReturn = System.Math.Floor((time.DayOfYear() - dtBuff.DayOfYear()) / 7) + 1
Else
lReturn = System.Math.Floor((time.DayOfYear() - dtBuff.DayOfYear()) / 7) + 2
End If
Case E_CALENDARWEEKRULES.CWR_FIRSTFULLWEEK
For lDay = 0 To 6
If dtBuff.DayOfWeek() = firstDayOfWeek Then
Exit For
End If
Next
lWeek = System.Math.Floor((time.DayOfYear() - dtBuff.DayOfYear()) / 7) + 1
If lWeek = 0 Then
lReturn = GetWeekOfYear(New System.DateTime(time.Year - 1, 12, 31), E_CALENDARWEEKRULES.CWR_FIRSTFULLWEEK, firstDayOfWeek)
Else
lReturn = lWeek
End If
Case E_CALENDARWEEKRULES.CWR_FIRSTFOURDAYWEEK
For lDay = 0 To 12
If dtBuff.DayOfWeek() = firstDayOfWeek And dtBuff.DayOfYear >= 5 Then
Exit For
End If
Next
lWeek = System.Math.Floor((time.DayOfYear() - dtBuff.DayOfYear()) / 7) + 2
If lWeek = 0 Then
lReturn = GetWeekOfYear(New System.DateTime(time.Year - 1, 12, 31), E_CALENDARWEEKRULES.CWR_FIRSTFOURDAYWEEK, firstDayOfWeek)
Else
lReturn = lWeek
End If
End Select
Return lReturn
End Function

```

This code has been tested extensively and produces results that are consistent with the Calendar.GetWeekOfYear function.

In MFC the COleDateTime::GetDayOfWeek() function defines Sunday as 1, and that is why we have to subtract 1 when using this function.

Some programmers use the Calendar.GetWeekOfYear function with a FirstFourDayWeek rule and set the starting date to Monday and this does not exactly map to ISO 8601 standards.

But there is a simple workaround and it is contained in the following method:

```
//C++ code

int GetIso8601WeekOfYear(COleDateTime time)
{
int yDay = time.GetDayOfWeek() - 1;
COleDateTime dtBuff = time;
if (yDay >= WD_MONDAY && yDay <= WD_WEDNESDAY)
{
COleDateTimeSpan oSpan(3, 0, 0, 0);
dtBuff = time + oSpan;
}
return GetWeekOfYear(dtBuff, CWR_FIRSTFOURDAYWEEK, WD_MONDAY);
}

```
```
//C# code

private int GetIso8601WeekOfYear(System.DateTime time)
{
E_WEEKDAY yDay = (E_WEEKDAY)time.DayOfWeek;
System.DateTime dtBuff = time;
if (yDay >= E_WEEKDAY.WD_MONDAY & yDay <= E_WEEKDAY.WD_WEDNESDAY)
{
}
return GetWeekOfYear(dtBuff, E_CALENDARWEEKRULES.CWR_FIRSTFOURDAYWEEK, E_WEEKDAY.WD_MONDAY);
}

```
```
'Visual Basic .NET code

Private Function GetIso8601WeekOfYear(ByVal time As System.DateTime) As Integer
Dim yDay As E_WEEKDAY = time.DayOfWeek
Dim dtBuff As System.DateTime = time
If yDay >= E_WEEKDAY.WD_MONDAY And yDay <= E_WEEKDAY.WD_WEDNESDAY Then