OSDN Git Service

* gcc-interface/decl.c (gnat_to_gnu_entity) <E_Array_Type>: Factor out
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-calend.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT RUN-TIME COMPONENTS                         --
4 --                                                                          --
5 --                         G N A T . C A L E N D A R                        --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --                     Copyright (C) 1999-2009, AdaCore                     --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 package body GNAT.Calendar is
35
36    use Ada.Calendar;
37    use Interfaces;
38
39    -----------------
40    -- Day_In_Year --
41    -----------------
42
43    function Day_In_Year (Date : Time) return Day_In_Year_Number is
44       Year     : Year_Number;
45       Month    : Month_Number;
46       Day      : Day_Number;
47       Day_Secs : Day_Duration;
48       pragma Unreferenced (Day_Secs);
49    begin
50       Split (Date, Year, Month, Day, Day_Secs);
51       return Julian_Day (Year, Month, Day) - Julian_Day (Year, 1, 1) + 1;
52    end Day_In_Year;
53
54    -----------------
55    -- Day_Of_Week --
56    -----------------
57
58    function Day_Of_Week (Date : Time) return Day_Name is
59       Year     : Year_Number;
60       Month    : Month_Number;
61       Day      : Day_Number;
62       Day_Secs : Day_Duration;
63       pragma Unreferenced (Day_Secs);
64    begin
65       Split (Date, Year, Month, Day, Day_Secs);
66       return Day_Name'Val ((Julian_Day (Year, Month, Day)) mod 7);
67    end Day_Of_Week;
68
69    ----------
70    -- Hour --
71    ----------
72
73    function Hour (Date : Time) return Hour_Number is
74       Year       : Year_Number;
75       Month      : Month_Number;
76       Day        : Day_Number;
77       Hour       : Hour_Number;
78       Minute     : Minute_Number;
79       Second     : Second_Number;
80       Sub_Second : Second_Duration;
81       pragma Unreferenced (Year, Month, Day, Minute, Second, Sub_Second);
82    begin
83       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
84       return Hour;
85    end Hour;
86
87    ----------------
88    -- Julian_Day --
89    ----------------
90
91    --  Julian_Day is used to by Day_Of_Week and Day_In_Year. Note that this
92    --  implementation is not expensive.
93
94    function Julian_Day
95      (Year  : Year_Number;
96       Month : Month_Number;
97       Day   : Day_Number) return Integer
98    is
99       Internal_Year  : Integer;
100       Internal_Month : Integer;
101       Internal_Day   : Integer;
102       Julian_Date    : Integer;
103       C              : Integer;
104       Ya             : Integer;
105
106    begin
107       Internal_Year  := Integer (Year);
108       Internal_Month := Integer (Month);
109       Internal_Day   := Integer (Day);
110
111       if Internal_Month > 2 then
112          Internal_Month := Internal_Month - 3;
113       else
114          Internal_Month := Internal_Month + 9;
115          Internal_Year  := Internal_Year - 1;
116       end if;
117
118       C  := Internal_Year / 100;
119       Ya := Internal_Year - (100 * C);
120
121       Julian_Date := (146_097 * C) / 4 +
122         (1_461 * Ya) / 4 +
123         (153 * Internal_Month + 2) / 5 +
124         Internal_Day + 1_721_119;
125
126       return Julian_Date;
127    end Julian_Day;
128
129    ------------
130    -- Minute --
131    ------------
132
133    function Minute (Date : Time) return Minute_Number is
134       Year       : Year_Number;
135       Month      : Month_Number;
136       Day        : Day_Number;
137       Hour       : Hour_Number;
138       Minute     : Minute_Number;
139       Second     : Second_Number;
140       Sub_Second : Second_Duration;
141       pragma Unreferenced (Year, Month, Day, Hour, Second, Sub_Second);
142    begin
143       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
144       return Minute;
145    end Minute;
146
147    ------------
148    -- Second --
149    ------------
150
151    function Second (Date : Time) return Second_Number is
152       Year       : Year_Number;
153       Month      : Month_Number;
154       Day        : Day_Number;
155       Hour       : Hour_Number;
156       Minute     : Minute_Number;
157       Second     : Second_Number;
158       Sub_Second : Second_Duration;
159       pragma Unreferenced (Year, Month, Day, Hour, Minute, Sub_Second);
160    begin
161       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
162       return Second;
163    end Second;
164
165    -----------
166    -- Split --
167    -----------
168
169    procedure Split
170      (Date       : Time;
171       Year       : out Year_Number;
172       Month      : out Month_Number;
173       Day        : out Day_Number;
174       Hour       : out Hour_Number;
175       Minute     : out Minute_Number;
176       Second     : out Second_Number;
177       Sub_Second : out Second_Duration)
178    is
179       Day_Secs : Day_Duration;
180       Secs     : Natural;
181
182    begin
183       Split (Date, Year, Month, Day, Day_Secs);
184
185       Secs       := (if Day_Secs = 0.0 then 0 else Natural (Day_Secs - 0.5));
186       Sub_Second := Second_Duration (Day_Secs - Day_Duration (Secs));
187       Hour       := Hour_Number (Secs / 3_600);
188       Secs       := Secs mod 3_600;
189       Minute     := Minute_Number (Secs / 60);
190       Second     := Second_Number (Secs mod 60);
191    end Split;
192
193    ----------------
194    -- Sub_Second --
195    ----------------
196
197    function Sub_Second (Date : Time) return Second_Duration is
198       Year       : Year_Number;
199       Month      : Month_Number;
200       Day        : Day_Number;
201       Hour       : Hour_Number;
202       Minute     : Minute_Number;
203       Second     : Second_Number;
204       Sub_Second : Second_Duration;
205       pragma Unreferenced (Year, Month, Day, Hour, Minute, Second);
206    begin
207       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
208       return Sub_Second;
209    end Sub_Second;
210
211    -------------
212    -- Time_Of --
213    -------------
214
215    function Time_Of
216      (Year       : Year_Number;
217       Month      : Month_Number;
218       Day        : Day_Number;
219       Hour       : Hour_Number;
220       Minute     : Minute_Number;
221       Second     : Second_Number;
222       Sub_Second : Second_Duration := 0.0) return Time
223    is
224
225       Day_Secs : constant Day_Duration :=
226                    Day_Duration (Hour   * 3_600) +
227                    Day_Duration (Minute *    60) +
228                    Day_Duration (Second)         +
229                                  Sub_Second;
230    begin
231       return Time_Of (Year, Month, Day, Day_Secs);
232    end Time_Of;
233
234    -----------------
235    -- To_Duration --
236    -----------------
237
238    function To_Duration (T : not null access timeval) return Duration is
239
240       procedure timeval_to_duration
241         (T    : not null access timeval;
242          sec  : not null access C.long;
243          usec : not null access C.long);
244       pragma Import (C, timeval_to_duration, "__gnat_timeval_to_duration");
245
246       Micro : constant := 10**6;
247       sec   : aliased C.long;
248       usec  : aliased C.long;
249
250    begin
251       timeval_to_duration (T, sec'Access, usec'Access);
252       return Duration (sec) + Duration (usec) / Micro;
253    end To_Duration;
254
255    ----------------
256    -- To_Timeval --
257    ----------------
258
259    function To_Timeval (D : Duration) return timeval is
260
261       procedure duration_to_timeval
262         (Sec  : C.long;
263          Usec : C.long;
264          T : not null access timeval);
265       pragma Import (C, duration_to_timeval, "__gnat_duration_to_timeval");
266
267       Micro  : constant := 10**6;
268       Result : aliased timeval;
269       sec    : C.long;
270       usec   : C.long;
271
272    begin
273       if D = 0.0 then
274          sec  := 0;
275          usec := 0;
276       else
277          sec  := C.long (D - 0.5);
278          usec := C.long ((D - Duration (sec)) * Micro - 0.5);
279       end if;
280
281       duration_to_timeval (sec, usec, Result'Access);
282
283       return Result;
284    end To_Timeval;
285
286    ------------------
287    -- Week_In_Year --
288    ------------------
289
290    function Week_In_Year (Date : Time) return Week_In_Year_Number is
291       Year : Year_Number;
292       Week : Week_In_Year_Number;
293       pragma Unreferenced (Year);
294    begin
295       Year_Week_In_Year (Date, Year, Week);
296       return Week;
297    end Week_In_Year;
298
299    -----------------------
300    -- Year_Week_In_Year --
301    -----------------------
302
303    procedure Year_Week_In_Year
304      (Date : Time;
305       Year : out Year_Number;
306       Week : out Week_In_Year_Number)
307    is
308       Month      : Month_Number;
309       Day        : Day_Number;
310       Hour       : Hour_Number;
311       Minute     : Minute_Number;
312       Second     : Second_Number;
313       Sub_Second : Second_Duration;
314       Jan_1      : Day_Name;
315       Shift      : Week_In_Year_Number;
316       Start_Week : Week_In_Year_Number;
317
318       pragma Unreferenced (Hour, Minute, Second, Sub_Second);
319
320       function Is_Leap (Year : Year_Number) return Boolean;
321       --  Return True if Year denotes a leap year. Leap centennial years are
322       --  properly handled.
323
324       function Jan_1_Day_Of_Week
325         (Jan_1     : Day_Name;
326          Year      : Year_Number;
327          Last_Year : Boolean := False;
328          Next_Year : Boolean := False) return Day_Name;
329       --  Given the weekday of January 1 in Year, determine the weekday on
330       --  which January 1 fell last year or will fall next year as set by
331       --  the two flags. This routine does not call Time_Of or Split.
332
333       function Last_Year_Has_53_Weeks
334         (Jan_1 : Day_Name;
335          Year  : Year_Number) return Boolean;
336       --  Given the weekday of January 1 in Year, determine whether last year
337       --  has 53 weeks. A False value implies that the year has 52 weeks.
338
339       -------------
340       -- Is_Leap --
341       -------------
342
343       function Is_Leap (Year : Year_Number) return Boolean is
344       begin
345          if Year mod 400 = 0 then
346             return True;
347          elsif Year mod 100 = 0 then
348             return False;
349          else
350             return Year mod 4 = 0;
351          end if;
352       end Is_Leap;
353
354       -----------------------
355       -- Jan_1_Day_Of_Week --
356       -----------------------
357
358       function Jan_1_Day_Of_Week
359         (Jan_1     : Day_Name;
360          Year      : Year_Number;
361          Last_Year : Boolean := False;
362          Next_Year : Boolean := False) return Day_Name
363       is
364          Shift : Integer := 0;
365
366       begin
367          if Last_Year then
368             Shift := (if Is_Leap (Year - 1) then -2 else -1);
369          elsif Next_Year then
370             Shift := (if Is_Leap (Year) then 2 else 1);
371          end if;
372
373          return Day_Name'Val ((Day_Name'Pos (Jan_1) + Shift) mod 7);
374       end Jan_1_Day_Of_Week;
375
376       ----------------------------
377       -- Last_Year_Has_53_Weeks --
378       ----------------------------
379
380       function Last_Year_Has_53_Weeks
381         (Jan_1 : Day_Name;
382          Year  : Year_Number) return Boolean
383       is
384          Last_Jan_1 : constant Day_Name :=
385                         Jan_1_Day_Of_Week (Jan_1, Year, Last_Year => True);
386
387       begin
388          --  These two cases are illustrated in the table below
389
390          return
391            Last_Jan_1 = Thursday
392              or else (Last_Jan_1 = Wednesday and then Is_Leap (Year - 1));
393       end Last_Year_Has_53_Weeks;
394
395    --  Start of processing for Week_In_Year
396
397    begin
398       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
399
400       --  According to ISO 8601, the first week of year Y is the week that
401       --  contains the first Thursday in year Y. The following table contains
402       --  all possible combinations of years and weekdays along with examples.
403
404       --    +-------+------+-------+---------+
405       --    | Jan 1 | Leap | Weeks | Example |
406       --    +-------+------+-------+---------+
407       --    |  Mon  |  No  |  52   |  2007   |
408       --    +-------+------+-------+---------+
409       --    |  Mon  | Yes  |  52   |  1996   |
410       --    +-------+------+-------+---------+
411       --    |  Tue  |  No  |  52   |  2002   |
412       --    +-------+------+-------+---------+
413       --    |  Tue  | Yes  |  52   |  1980   |
414       --    +-------+------+-------+---------+
415       --    |  Wed  |  No  |  52   |  2003   |
416       --    +-------+------#########---------+
417       --    |  Wed  | Yes  #  53   #  1992   |
418       --    +-------+------#-------#---------+
419       --    |  Thu  |  No  #  53   #  1998   |
420       --    +-------+------#-------#---------+
421       --    |  Thu  | Yes  #  53   #  2004   |
422       --    +-------+------#########---------+
423       --    |  Fri  |  No  |  52   |  1999   |
424       --    +-------+------+-------+---------+
425       --    |  Fri  | Yes  |  52   |  1988   |
426       --    +-------+------+-------+---------+
427       --    |  Sat  |  No  |  52   |  1994   |
428       --    +-------+------+-------+---------+
429       --    |  Sat  | Yes  |  52   |  1972   |
430       --    +-------+------+-------+---------+
431       --    |  Sun  |  No  |  52   |  1995   |
432       --    +-------+------+-------+---------+
433       --    |  Sun  | Yes  |  52   |  1956   |
434       --    +-------+------+-------+---------+
435
436       --  A small optimization, the input date is January 1. Note that this
437       --  is a key day since it determines the number of weeks and is used
438       --  when special casing the first week of January and the last week of
439       --  December.
440
441       Jan_1 := Day_Of_Week (if Day = 1 and then Month = 1
442                             then Date
443                             else (Time_Of (Year, 1, 1, 0.0)));
444
445       --  Special cases for January
446
447       if Month = 1 then
448
449          --  Special case 1: January 1, 2 and 3. These three days may belong
450          --  to last year's last week which can be week number 52 or 53.
451
452          --    +-----+-----+-----+=====+-----+-----+-----+
453          --    | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
454          --    +-----+-----+-----+-----+-----+-----+-----+
455          --    | 26  | 27  | 28  # 29  # 30  | 31  |  1  |
456          --    +-----+-----+-----+-----+-----+-----+-----+
457          --    | 27  | 28  | 29  # 30  # 31  |  1  |  2  |
458          --    +-----+-----+-----+-----+-----+-----+-----+
459          --    | 28  | 29  | 30  # 31  #  1  |  2  |  3  |
460          --    +-----+-----+-----+=====+-----+-----+-----+
461
462          if (Day = 1 and then Jan_1 in Friday .. Sunday)
463                or else
464             (Day = 2 and then Jan_1 in Friday .. Saturday)
465                or else
466             (Day = 3 and then Jan_1 = Friday)
467          then
468             Week := (if Last_Year_Has_53_Weeks (Jan_1, Year) then 53 else 52);
469
470             --  January 1, 2 and 3 belong to the previous year
471
472             Year := Year - 1;
473             return;
474
475          --  Special case 2: January 1, 2, 3, 4, 5, 6 and 7 of the first week
476
477          --    +-----+-----+-----+=====+-----+-----+-----+
478          --    | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
479          --    +-----+-----+-----+-----+-----+-----+-----+
480          --    | 29  | 30  | 31  #  1  #  2  |  3  |  4  |
481          --    +-----+-----+-----+-----+-----+-----+-----+
482          --    | 30  | 31  |  1  #  2  #  3  |  4  |  5  |
483          --    +-----+-----+-----+-----+-----+-----+-----+
484          --    | 31  |  1  |  2  #  3  #  4  |  5  |  6  |
485          --    +-----+-----+-----+-----+-----+-----+-----+
486          --    |  1  |  2  |  3  #  4  #  5  |  6  |  7  |
487          --    +-----+-----+-----+=====+-----+-----+-----+
488
489          elsif (Day <= 4 and then Jan_1 in Monday .. Thursday)
490                   or else
491                (Day = 5  and then Jan_1 in Monday .. Wednesday)
492                   or else
493                (Day = 6  and then Jan_1 in Monday ..  Tuesday)
494                   or else
495                (Day = 7  and then Jan_1 = Monday)
496          then
497             Week := 1;
498             return;
499          end if;
500
501       --  Month other than 1
502
503       --  Special case 3: December 29, 30 and 31. These days may belong to
504       --  next year's first week.
505
506       --    +-----+-----+-----+=====+-----+-----+-----+
507       --    | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
508       --    +-----+-----+-----+-----+-----+-----+-----+
509       --    | 29  | 30  | 31  #  1  #  2  |  3  |  4  |
510       --    +-----+-----+-----+-----+-----+-----+-----+
511       --    | 30  | 31  |  1  #  2  #  3  |  4  |  5  |
512       --    +-----+-----+-----+-----+-----+-----+-----+
513       --    | 31  |  1  |  2  #  3  #  4  |  5  |  6  |
514       --    +-----+-----+-----+=====+-----+-----+-----+
515
516       elsif Month = 12 and then Day > 28 then
517          declare
518             Next_Jan_1 : constant Day_Name :=
519                            Jan_1_Day_Of_Week (Jan_1, Year, Next_Year => True);
520          begin
521             if (Day = 29 and then Next_Jan_1 = Thursday)
522                   or else
523                (Day = 30 and then Next_Jan_1 in Wednesday .. Thursday)
524                   or else
525                (Day = 31 and then Next_Jan_1 in Tuesday .. Thursday)
526             then
527                Year := Year + 1;
528                Week := 1;
529                return;
530             end if;
531          end;
532       end if;
533
534       --  Determine the week from which to start counting. If January 1 does
535       --  not belong to the first week of the input year, then the next week
536       --  is the first week.
537
538       Start_Week := (if Jan_1 in Friday .. Sunday then 1 else 2);
539
540       --  At this point all special combinations have been accounted for and
541       --  the proper start week has been found. Since January 1 may not fall
542       --  on a Monday, shift 7 - Day_Name'Pos (Jan_1). This action ensures an
543       --  origin which falls on Monday.
544
545       Shift := 7 - Day_Name'Pos (Jan_1);
546       Week  := Start_Week + (Day_In_Year (Date) - Shift - 1) / 7;
547    end Year_Week_In_Year;
548
549 end GNAT.Calendar;