OSDN Git Service

optimize
[pf3gnuchains/gcc-fork.git] / gcc / ada / a-calend.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT RUN-TIME COMPONENTS                         --
4 --                                                                          --
5 --                         A D A . C A L E N D A R                          --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1992-2004 Free Software Foundation, Inc.          --
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,  59 Temple Place - Suite 330,  Boston, --
20 -- MA 02111-1307, 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 with Unchecked_Conversion;
35
36 with System.OS_Primitives;
37 --  used for Clock
38
39 package body Ada.Calendar is
40
41    ------------------------------
42    -- Use of Pragma Unsuppress --
43    ------------------------------
44
45    --  This implementation of Calendar takes advantage of the permission in
46    --  Ada 95 of using arithmetic overflow checks to check for out of bounds
47    --  time values. This means that we must catch the constraint error that
48    --  results from arithmetic overflow, so we use pragma Unsuppress to make
49    --  sure that overflow is enabled, using software overflow checking if
50    --  necessary. That way, compiling Calendar with options to suppress this
51    --  checking will not affect its correctness.
52
53    ------------------------
54    -- Local Declarations --
55    ------------------------
56
57    type Char_Pointer is access Character;
58    subtype int  is Integer;
59    subtype long is Long_Integer;
60    --  Synonyms for C types. We don't want to get them from Interfaces.C
61    --  because there is no point in loading that unit just for calendar.
62
63    type tm is record
64       tm_sec    : int;           -- seconds after the minute (0 .. 60)
65       tm_min    : int;           -- minutes after the hour (0 .. 59)
66       tm_hour   : int;           -- hours since midnight (0 .. 24)
67       tm_mday   : int;           -- day of the month (1 .. 31)
68       tm_mon    : int;           -- months since January (0 .. 11)
69       tm_year   : int;           -- years since 1900
70       tm_wday   : int;           -- days since Sunday (0 .. 6)
71       tm_yday   : int;           -- days since January 1 (0 .. 365)
72       tm_isdst  : int;           -- Daylight Savings Time flag (-1 .. +1)
73       tm_gmtoff : long;          -- offset from CUT in seconds
74       tm_zone   : Char_Pointer;  -- timezone abbreviation
75    end record;
76
77    type tm_Pointer is access all tm;
78
79    subtype time_t is long;
80
81    type time_t_Pointer is access all time_t;
82
83    procedure localtime_r (C : time_t_Pointer; res : tm_Pointer);
84    pragma Import (C, localtime_r, "__gnat_localtime_r");
85
86    function mktime (TM : tm_Pointer) return time_t;
87    pragma Import (C, mktime);
88    --  mktime returns -1 in case the calendar time given by components of
89    --  TM.all cannot be represented.
90
91    --  The following constants are used in adjusting Ada dates so that they
92    --  fit into a 56 year range that can be handled by Unix (1970 included -
93    --  2026 excluded). Dates that are not in this 56 year range are shifted
94    --  by multiples of 56 years to fit in this range
95    --  The trick is that the number of days in any four year period in the Ada
96    --  range of years (1901 - 2099) has a constant number of days. This is
97    --  because we have the special case of 2000 which, contrary to the normal
98    --  exception for centuries, is a leap year after all.
99    --  56 has been chosen, because it is not only a multiple of 4, but also
100    --  a multiple of 7. Thus two dates 56 years apart fall on the same day of
101    --  the week, and the Daylight Saving Time change dates are usually the same
102    --  for these two years.
103
104    Unix_Year_Min : constant := 1970;
105    Unix_Year_Max : constant := 2026;
106
107    Ada_Year_Min : constant := 1901;
108    Ada_Year_Max : constant := 2099;
109
110    --  Some basic constants used throughout
111
112    Days_In_Month : constant array (Month_Number) of Day_Number :=
113                      (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
114
115    Days_In_4_Years      : constant := 365 * 3 + 366;
116    Seconds_In_4_Years   : constant := 86_400 * Days_In_4_Years;
117    Seconds_In_56_Years  : constant := Seconds_In_4_Years * 14;
118    Seconds_In_56_YearsD : constant := Duration (Seconds_In_56_Years);
119
120    ---------
121    -- "+" --
122    ---------
123
124    function "+" (Left : Time; Right : Duration) return Time is
125       pragma Unsuppress (Overflow_Check);
126    begin
127       return (Left + Time (Right));
128
129    exception
130       when Constraint_Error =>
131          raise Time_Error;
132    end "+";
133
134    function "+" (Left : Duration; Right : Time) return Time is
135       pragma Unsuppress (Overflow_Check);
136    begin
137       return (Time (Left) + Right);
138
139    exception
140       when Constraint_Error =>
141          raise Time_Error;
142    end "+";
143
144    ---------
145    -- "-" --
146    ---------
147
148    function "-" (Left : Time; Right : Duration)  return Time is
149       pragma Unsuppress (Overflow_Check);
150    begin
151       return Left - Time (Right);
152
153    exception
154       when Constraint_Error =>
155          raise Time_Error;
156    end "-";
157
158    function "-" (Left : Time; Right : Time) return Duration is
159       pragma Unsuppress (Overflow_Check);
160    begin
161       return Duration (Left) - Duration (Right);
162
163    exception
164       when Constraint_Error =>
165          raise Time_Error;
166    end "-";
167
168    ---------
169    -- "<" --
170    ---------
171
172    function "<" (Left, Right : Time) return Boolean is
173    begin
174       return Duration (Left) < Duration (Right);
175    end "<";
176
177    ----------
178    -- "<=" --
179    ----------
180
181    function "<=" (Left, Right : Time) return Boolean is
182    begin
183       return Duration (Left) <= Duration (Right);
184    end "<=";
185
186    ---------
187    -- ">" --
188    ---------
189
190    function ">" (Left, Right : Time) return Boolean is
191    begin
192       return Duration (Left) > Duration (Right);
193    end ">";
194
195    ----------
196    -- ">=" --
197    ----------
198
199    function ">=" (Left, Right : Time) return Boolean is
200    begin
201       return Duration (Left) >= Duration (Right);
202    end ">=";
203
204    -----------
205    -- Clock --
206    -----------
207
208    function Clock return Time is
209    begin
210       return Time (System.OS_Primitives.Clock);
211    end Clock;
212
213    ---------
214    -- Day --
215    ---------
216
217    function Day (Date : Time) return Day_Number is
218       DY : Year_Number;
219       DM : Month_Number;
220       DD : Day_Number;
221       DS : Day_Duration;
222
223    begin
224       Split (Date, DY, DM, DD, DS);
225       return DD;
226    end Day;
227
228    -----------
229    -- Month --
230    -----------
231
232    function Month (Date : Time) return Month_Number is
233       DY : Year_Number;
234       DM : Month_Number;
235       DD : Day_Number;
236       DS : Day_Duration;
237
238    begin
239       Split (Date, DY, DM, DD, DS);
240       return DM;
241    end Month;
242
243    -------------
244    -- Seconds --
245    -------------
246
247    function Seconds (Date : Time) return Day_Duration is
248       DY : Year_Number;
249       DM : Month_Number;
250       DD : Day_Number;
251       DS : Day_Duration;
252
253    begin
254       Split (Date, DY, DM, DD, DS);
255       return DS;
256    end Seconds;
257
258    -----------
259    -- Split --
260    -----------
261
262    procedure Split
263      (Date    : Time;
264       Year    : out Year_Number;
265       Month   : out Month_Number;
266       Day     : out Day_Number;
267       Seconds : out Day_Duration)
268    is
269       --  The following declare bounds for duration that are comfortably
270       --  wider than the maximum allowed output result for the Ada range
271       --  of representable split values. These are used for a quick check
272       --  that the value is not wildly out of range.
273
274       Low  : constant := (Ada_Year_Min - Unix_Year_Min - 2) * 365 * 86_400;
275       High : constant := (Ada_Year_Max - Unix_Year_Min + 2) * 365 * 86_400;
276
277       LowD  : constant Duration := Duration (Low);
278       HighD : constant Duration := Duration (High);
279
280       --  Finally the actual variables used in the computation
281
282       D                : Duration;
283       Frac_Sec         : Duration;
284       Year_Val         : Integer;
285       Adjusted_Seconds : aliased time_t;
286       Tm_Val           : aliased tm;
287
288    begin
289       --  For us a time is simply a signed duration value, so we work with
290       --  this duration value directly. Note that it can be negative.
291
292       D := Duration (Date);
293
294       --  First of all, filter out completely ludicrous values. Remember
295       --  that we use the full stored range of duration values, which may
296       --  be significantly larger than the allowed range of Ada times. Note
297       --  that these checks are wider than required to make absolutely sure
298       --  that there are no end effects from time zone differences.
299
300       if D < LowD or else D > HighD then
301          raise Time_Error;
302       end if;
303
304       --  The unix localtime_r function is more or less exactly what we need
305       --  here. The less comes from the fact that it does not support the
306       --  required range of years (the guaranteed range available is only
307       --  EPOCH through EPOCH + N seconds). N is in practice 2 ** 31 - 1.
308
309       --  If we have a value outside this range, then we first adjust it
310       --  to be in the required range by adding multiples of 56 years.
311       --  For the range we are interested in, the number of days in any
312       --  consecutive 56 year period is constant. Then we do the split
313       --  on the adjusted value, and readjust the years value accordingly.
314
315       Year_Val := 0;
316
317       while D < 0.0 loop
318          D := D + Seconds_In_56_YearsD;
319          Year_Val := Year_Val - 56;
320       end loop;
321
322       while D >= Seconds_In_56_YearsD loop
323          D := D - Seconds_In_56_YearsD;
324          Year_Val := Year_Val + 56;
325       end loop;
326
327       --  Now we need to take the value D, which is now non-negative, and
328       --  break it down into seconds (to pass to the localtime_r function)
329       --  and fractions of seconds (for the adjustment below).
330
331       --  Surprisingly there is no easy way to do this in Ada, and certainly
332       --  no easy way to do it and generate efficient code. Therefore we
333       --  do it at a low level, knowing that it is really represented as
334       --  an integer with units of Small
335
336       declare
337          type D_Int is range 0 .. 2 ** (Duration'Size - 1) - 1;
338          for D_Int'Size use Duration'Size;
339
340          Small_Div : constant D_Int := D_Int (1.0 / Duration'Small);
341          D_As_Int  : D_Int;
342
343          function To_D_As_Int is new Unchecked_Conversion (Duration, D_Int);
344          function To_Duration is new Unchecked_Conversion (D_Int, Duration);
345
346       begin
347          D_As_Int := To_D_As_Int (D);
348          Adjusted_Seconds := time_t (D_As_Int / Small_Div);
349          Frac_Sec := To_Duration (D_As_Int rem Small_Div);
350       end;
351
352       localtime_r (Adjusted_Seconds'Unchecked_Access, Tm_Val'Unchecked_Access);
353
354       Year_Val := Tm_Val.tm_year + 1900 + Year_Val;
355       Month    := Tm_Val.tm_mon + 1;
356       Day      := Tm_Val.tm_mday;
357
358       --  The Seconds value is a little complex. The localtime function
359       --  returns the integral number of seconds, which is what we want,
360       --  but we want to retain the fractional part from the original
361       --  Time value, since this is typically stored more accurately.
362
363       Seconds := Duration (Tm_Val.tm_hour * 3600 +
364                            Tm_Val.tm_min  * 60 +
365                            Tm_Val.tm_sec)
366                    + Frac_Sec;
367
368       --  Note: the above expression is pretty horrible, one of these days
369       --  we should stop using time_of and do everything ourselves to avoid
370       --  these unnecessary divides and multiplies???.
371
372       --  The Year may still be out of range, since our entry test was
373       --  deliberately crude. Trying to make this entry test accurate is
374       --  tricky due to time zone adjustment issues affecting the exact
375       --  boundary. It is interesting to note that whether or not a given
376       --  Calendar.Time value gets Time_Error when split depends on the
377       --  current time zone setting.
378
379       if Year_Val not in Ada_Year_Min .. Ada_Year_Max then
380          raise Time_Error;
381       else
382          Year := Year_Val;
383       end if;
384    end Split;
385
386    -------------
387    -- Time_Of --
388    -------------
389
390    function Time_Of
391      (Year    : Year_Number;
392       Month   : Month_Number;
393       Day     : Day_Number;
394       Seconds : Day_Duration := 0.0)
395       return    Time
396    is
397       Result_Secs : aliased time_t;
398       TM_Val      : aliased tm;
399       Int_Secs    : constant Integer := Integer (Seconds);
400
401       Year_Val        : Integer := Year;
402       Duration_Adjust : Duration := 0.0;
403
404    begin
405       --  The following checks are redundant with respect to the constraint
406       --  error checks that should normally be made on parameters, but we
407       --  decide to raise Constraint_Error in any case if bad values come
408       --  in (as a result of checks being off in the caller, or for other
409       --  erroneous or bounded error cases).
410
411       if        not Year   'Valid
412         or else not Month  'Valid
413         or else not Day    'Valid
414         or else not Seconds'Valid
415       then
416          raise Constraint_Error;
417       end if;
418
419       --  Check for Day value too large (one might expect mktime to do this
420       --  check, as well as the basi checks we did with 'Valid, but it seems
421       --  that at least on some systems, this built-in check is too weak).
422
423       if Day > Days_In_Month (Month)
424         and then (Day /= 29 or Month /= 2 or Year mod 4 /= 0)
425       then
426          raise Time_Error;
427       end if;
428
429       TM_Val.tm_sec  := Int_Secs mod 60;
430       TM_Val.tm_min  := (Int_Secs / 60) mod 60;
431       TM_Val.tm_hour := (Int_Secs / 60) / 60;
432       TM_Val.tm_mday := Day;
433       TM_Val.tm_mon  := Month - 1;
434
435       --  For the year, we have to adjust it to a year that Unix can handle.
436       --  We do this in 56 year steps, since the number of days in 56 years
437       --  is constant, so the timezone effect on the conversion from local
438       --  time to GMT is unaffected; also the DST change dates are usually
439       --  not modified.
440
441       while Year_Val < Unix_Year_Min loop
442          Year_Val := Year_Val + 56;
443          Duration_Adjust := Duration_Adjust - Seconds_In_56_YearsD;
444       end loop;
445
446       while Year_Val >= Unix_Year_Max loop
447          Year_Val := Year_Val - 56;
448          Duration_Adjust := Duration_Adjust + Seconds_In_56_YearsD;
449       end loop;
450
451       TM_Val.tm_year := Year_Val - 1900;
452
453       --  Since we do not have information on daylight savings,
454       --  rely on the default information.
455
456       TM_Val.tm_isdst := -1;
457       Result_Secs := mktime (TM_Val'Unchecked_Access);
458
459       --  That gives us the basic value in seconds. Two adjustments are
460       --  needed. First we must undo the year adjustment carried out above.
461       --  Second we put back the fraction seconds value since in general the
462       --  Day_Duration value we received has additional precision which we
463       --  do not want to lose in the constructed result.
464
465       return
466         Time (Duration (Result_Secs) +
467               Duration_Adjust +
468               (Seconds - Duration (Int_Secs)));
469
470    end Time_Of;
471
472    ----------
473    -- Year --
474    ----------
475
476    function Year (Date : Time) return Year_Number is
477       DY : Year_Number;
478       DM : Month_Number;
479       DD : Day_Number;
480       DS : Day_Duration;
481
482    begin
483       Split (Date, DY, DM, DD, DS);
484       return DY;
485    end Year;
486
487 end Ada.Calendar;