+ -- According to ISO 8601, the first week of year Y is the week that
+ -- contains the first Thursday in year Y. The following table contains
+ -- all possible combinations of years and weekdays along with examples.
+
+ -- +-------+------+-------+---------+
+ -- | Jan 1 | Leap | Weeks | Example |
+ -- +-------+------+-------+---------+
+ -- | Mon | No | 52 | 2007 |
+ -- +-------+------+-------+---------+
+ -- | Mon | Yes | 52 | 1996 |
+ -- +-------+------+-------+---------+
+ -- | Tue | No | 52 | 2002 |
+ -- +-------+------+-------+---------+
+ -- | Tue | Yes | 52 | 1980 |
+ -- +-------+------+-------+---------+
+ -- | Wed | No | 52 | 2003 |
+ -- +-------+------#########---------+
+ -- | Wed | Yes # 53 # 1992 |
+ -- +-------+------#-------#---------+
+ -- | Thu | No # 53 # 1998 |
+ -- +-------+------#-------#---------+
+ -- | Thu | Yes # 53 # 2004 |
+ -- +-------+------#########---------+
+ -- | Fri | No | 52 | 1999 |
+ -- +-------+------+-------+---------+
+ -- | Fri | Yes | 52 | 1988 |
+ -- +-------+------+-------+---------+
+ -- | Sat | No | 52 | 1994 |
+ -- +-------+------+-------+---------+
+ -- | Sat | Yes | 52 | 1972 |
+ -- +-------+------+-------+---------+
+ -- | Sun | No | 52 | 1995 |
+ -- +-------+------+-------+---------+
+ -- | Sun | Yes | 52 | 1956 |
+ -- +-------+------+-------+---------+
+
+ -- A small optimization, the input date is January 1. Note that this
+ -- is a key day since it determines the number of weeks and is used
+ -- when special casing the first week of January and the last week of
+ -- December.
+
+ Jan_1 := Day_Of_Week (if Day = 1 and then Month = 1
+ then Date
+ else (Time_Of (Year, 1, 1, 0.0)));
+
+ -- Special cases for January
+
+ if Month = 1 then
+
+ -- Special case 1: January 1, 2 and 3. These three days may belong
+ -- to last year's last week which can be week number 52 or 53.
+
+ -- +-----+-----+-----+=====+-----+-----+-----+
+ -- | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 26 | 27 | 28 # 29 # 30 | 31 | 1 |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 27 | 28 | 29 # 30 # 31 | 1 | 2 |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 28 | 29 | 30 # 31 # 1 | 2 | 3 |
+ -- +-----+-----+-----+=====+-----+-----+-----+
+
+ if (Day = 1 and then Jan_1 in Friday .. Sunday)
+ or else
+ (Day = 2 and then Jan_1 in Friday .. Saturday)
+ or else
+ (Day = 3 and then Jan_1 = Friday)
+ then
+ Week := (if Last_Year_Has_53_Weeks (Jan_1, Year) then 53 else 52);
+
+ -- January 1, 2 and 3 belong to the previous year
+
+ Year := Year - 1;
+ return;
+
+ -- Special case 2: January 1, 2, 3, 4, 5, 6 and 7 of the first week
+
+ -- +-----+-----+-----+=====+-----+-----+-----+
+ -- | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 29 | 30 | 31 # 1 # 2 | 3 | 4 |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 30 | 31 | 1 # 2 # 3 | 4 | 5 |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 31 | 1 | 2 # 3 # 4 | 5 | 6 |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 1 | 2 | 3 # 4 # 5 | 6 | 7 |
+ -- +-----+-----+-----+=====+-----+-----+-----+
+
+ elsif (Day <= 4 and then Jan_1 in Monday .. Thursday)
+ or else
+ (Day = 5 and then Jan_1 in Monday .. Wednesday)
+ or else
+ (Day = 6 and then Jan_1 in Monday .. Tuesday)
+ or else
+ (Day = 7 and then Jan_1 = Monday)
+ then
+ Week := 1;
+ return;
+ end if;
+
+ -- Month other than 1
+
+ -- Special case 3: December 29, 30 and 31. These days may belong to
+ -- next year's first week.
+
+ -- +-----+-----+-----+=====+-----+-----+-----+
+ -- | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 29 | 30 | 31 # 1 # 2 | 3 | 4 |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 30 | 31 | 1 # 2 # 3 | 4 | 5 |
+ -- +-----+-----+-----+-----+-----+-----+-----+
+ -- | 31 | 1 | 2 # 3 # 4 | 5 | 6 |
+ -- +-----+-----+-----+=====+-----+-----+-----+
+
+ elsif Month = 12 and then Day > 28 then
+ declare
+ Next_Jan_1 : constant Day_Name :=
+ Jan_1_Day_Of_Week (Jan_1, Year, Next_Year => True);
+ begin
+ if (Day = 29 and then Next_Jan_1 = Thursday)
+ or else
+ (Day = 30 and then Next_Jan_1 in Wednesday .. Thursday)
+ or else
+ (Day = 31 and then Next_Jan_1 in Tuesday .. Thursday)
+ then
+ Year := Year + 1;
+ Week := 1;
+ return;
+ end if;
+ end;
+ end if;
+
+ -- Determine the week from which to start counting. If January 1 does
+ -- not belong to the first week of the input year, then the next week
+ -- is the first week.