OSDN Git Service

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