## Calculate the date from an ISO date

###### published: Fri, 24-Feb-2006   |   updated: Fri, 24-Feb-2006

One of the most popular pages on my website is the one where I discuss how to calculate the ISO week number of a date. Today out of the blue I was asked, if given an ISO week, how would you do the reverse calculation and find the original date.

The thing is, my original code only calculated the week number and the year for a given date. Using those two pieces of information is insufficient to calculate the original date; indeed, you could only really return the date of the Monday of that week as a predictable answer.

So to properly answer the question I'd have to alter the original code so that the full ISO date was returned: the year, the week, and the day of the week. OK, no problem.

Then I started thinking. The original code I'd written was procedural (or, since it was written in C#, it used static methods): when presented with a date it would return the ISO week. Given that I'd been ranting recently about procedural code and that we should endeavor to design so as not to use it, I didn't want to just write yet another static method. No, it had to be a proper class. Much better. For fun I then decided to use Chrome instead of C# (Chrome is an Object Pascal derivative hosted in Visual Studio 2005).

So first of all, here's the original code, tidied up (some of my original identifiers were not that good), and converted to Chrome as a class. I also changed it so that the class deals with ISO dates (it returns the ISO date as an integer in the form YYYYWWD, where YYYY is the year, WW is the week number from 01 to 53, and D is the day of the week with 1 for Monday, 2 for Tuesday, all the way to 7 for Sunday). The `ToString()` method returns the formatted version of the ISO date with a constant character W in between the year and the week (2005123, the Wednesday in the 12th week of 2005, would be converted to '2005W123').

Anyway here's the code:

```namespace IsoWeek;

interface

type
IsoDate = public class
private
FValue : integer;
method get_Day: integer;
method get_Week: integer;
method get_Year: integer;

method getIsoDayNumber(date : DateTime) : integer;
method getMondayOf1stWeek(year : integer) : DateTime;
method convertDateToIsoDate(date : DateTime) : integer;
protected
public
constructor(date : DateTime);
constructor(year : Integer; month : Integer; day : integer);

method ToString : string; override;

property Value : integer read FValue;
property Year : integer read get_Year;
property Week : integer read get_Week;
property Day : integer read get_Day;
end;

implementation

{===IsoDate==========================================================}
constructor IsoDate(date : DateTime);
begin
FValue := convertDateToIsoDate(date);
end;
{--------}
constructor IsoDate(year : Integer; month : Integer; day : integer);
begin
FValue := convertDateToIsoDate(new DateTime(year, month, day));
end;
{--------}
method IsoDate.get_Year: integer;
begin
Result := FValue div 1000;
end;
{--------}
method IsoDate.get_Week: integer;
begin
Result := (FValue div 10) mod 100;
end;
{--------}
method IsoDate.get_Day: integer;
begin
Result := FValue mod 10;
end;
{--------}
method IsoDate.getIsoDayNumber(date : DateTime) : integer;
begin
// the ISO day number has 1==Monday, ..., 7==Sunday
Result := integer(date.DayOfWeek); // 0==Sunday, 6==Saturday
if (Result = 0) then
Result := Result + 7;
end;
{--------}
method IsoDate.getMondayOf1stWeek(year : integer) : DateTime;
var
dt : DateTime;
begin
// get the date for the 4-Jan for this year
dt := new DateTime(Year, 1, 4);

// return the date of the Monday that is less than or equal
// to this date
Result := dt.AddDays(1 - getIsoDayNumber(dt));
end;
{--------}
method IsoDate.convertDateToIsoDate(date : DateTime) : integer;
var
mondayOf1stWeek : DateTime;
isoYear : Integer;
begin
isoYear := date.Year;
if (date >= new DateTime(isoYear, 12, 29)) then begin
mondayOf1stWeek := getMondayOf1stWeek(isoYear + 1);
if (date < mondayOf1stWeek) then
mondayOf1stWeek := getMondayOf1stWeek(isoYear)
else
inc(IsoYear);
end
else begin
mondayOf1stWeek := getMondayOf1stWeek(isoYear);
if (date < mondayOf1stWeek) then begin
dec(isoYear);
mondayOf1stWeek := getMondayOf1stWeek(isoYear);
end;
end;

Result := (isoYear * 1000) +
(((date - mondayOf1stWeek).Days / 7 + 1) * 10) +
getIsoDayNumber(date);
end;
{--------}
method IsoDate.ToString : string;
begin
Result := string.Format('{0:0000}W{1:000}', FValue div 1000, FValue mod 1000);
end;
{====================================================================}

end.
```

Now, to calculate the original date from an ISO date is simple: extract the year part, calculate the date of the Monday of the first week for that year, and then add the number of days in the week part of the ISO date and the day of the week.

```namespace IsoWeek;

interface

type
IsoDate = public class
private
...
method get_Date: DateTime;
...
protected
public
...
property Date : DateTime read get_Date;
end;

implementation

{===IsoDate==========================================================}
...
method IsoDate.get_Date: DateTime;
begin
Result := getMondayOf1stWeek(Year).AddDays((Week - 1) * 7 + (Day - 1));
end;
...
{====================================================================}
```

Not to hard, I think you'll agree. And it certainly looks better as a class.