PR target/16201
[official-gcc.git] / gcc / ada / a-calend.adb
blobe5788a473e2ed6acc7cdfb3e986a7d8735774cb5
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 ------------------------------------------------------------------------------
34 with Unchecked_Conversion;
36 with System.OS_Primitives;
37 -- used for Clock
39 package body Ada.Calendar is
41 ------------------------------
42 -- Use of Pragma Unsuppress --
43 ------------------------------
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.
53 ------------------------
54 -- Local Declarations --
55 ------------------------
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.
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;
77 type tm_Pointer is access all tm;
79 subtype time_t is long;
81 type time_t_Pointer is access all time_t;
83 procedure localtime_r (C : time_t_Pointer; res : tm_Pointer);
84 pragma Import (C, localtime_r, "__gnat_localtime_r");
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.
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.
104 Unix_Year_Min : constant := 1970;
105 Unix_Year_Max : constant := 2026;
107 Ada_Year_Min : constant := 1901;
108 Ada_Year_Max : constant := 2099;
110 -- Some basic constants used throughout
112 Days_In_Month : constant array (Month_Number) of Day_Number :=
113 (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
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);
120 ---------
121 -- "+" --
122 ---------
124 function "+" (Left : Time; Right : Duration) return Time is
125 pragma Unsuppress (Overflow_Check);
126 begin
127 return (Left + Time (Right));
129 exception
130 when Constraint_Error =>
131 raise Time_Error;
132 end "+";
134 function "+" (Left : Duration; Right : Time) return Time is
135 pragma Unsuppress (Overflow_Check);
136 begin
137 return (Time (Left) + Right);
139 exception
140 when Constraint_Error =>
141 raise Time_Error;
142 end "+";
144 ---------
145 -- "-" --
146 ---------
148 function "-" (Left : Time; Right : Duration) return Time is
149 pragma Unsuppress (Overflow_Check);
150 begin
151 return Left - Time (Right);
153 exception
154 when Constraint_Error =>
155 raise Time_Error;
156 end "-";
158 function "-" (Left : Time; Right : Time) return Duration is
159 pragma Unsuppress (Overflow_Check);
160 begin
161 return Duration (Left) - Duration (Right);
163 exception
164 when Constraint_Error =>
165 raise Time_Error;
166 end "-";
168 ---------
169 -- "<" --
170 ---------
172 function "<" (Left, Right : Time) return Boolean is
173 begin
174 return Duration (Left) < Duration (Right);
175 end "<";
177 ----------
178 -- "<=" --
179 ----------
181 function "<=" (Left, Right : Time) return Boolean is
182 begin
183 return Duration (Left) <= Duration (Right);
184 end "<=";
186 ---------
187 -- ">" --
188 ---------
190 function ">" (Left, Right : Time) return Boolean is
191 begin
192 return Duration (Left) > Duration (Right);
193 end ">";
195 ----------
196 -- ">=" --
197 ----------
199 function ">=" (Left, Right : Time) return Boolean is
200 begin
201 return Duration (Left) >= Duration (Right);
202 end ">=";
204 -----------
205 -- Clock --
206 -----------
208 function Clock return Time is
209 begin
210 return Time (System.OS_Primitives.Clock);
211 end Clock;
213 ---------
214 -- Day --
215 ---------
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;
223 begin
224 Split (Date, DY, DM, DD, DS);
225 return DD;
226 end Day;
228 -----------
229 -- Month --
230 -----------
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;
238 begin
239 Split (Date, DY, DM, DD, DS);
240 return DM;
241 end Month;
243 -------------
244 -- Seconds --
245 -------------
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;
253 begin
254 Split (Date, DY, DM, DD, DS);
255 return DS;
256 end Seconds;
258 -----------
259 -- Split --
260 -----------
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)
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.
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;
277 LowD : constant Duration := Duration (Low);
278 HighD : constant Duration := Duration (High);
280 -- Finally the actual variables used in the computation
282 D : Duration;
283 Frac_Sec : Duration;
284 Year_Val : Integer;
285 Adjusted_Seconds : aliased time_t;
286 Tm_Val : aliased tm;
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.
292 D := Duration (Date);
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.
300 if D < LowD or else D > HighD then
301 raise Time_Error;
302 end if;
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.
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.
315 Year_Val := 0;
317 while D < 0.0 loop
318 D := D + Seconds_In_56_YearsD;
319 Year_Val := Year_Val - 56;
320 end loop;
322 while D >= Seconds_In_56_YearsD loop
323 D := D - Seconds_In_56_YearsD;
324 Year_Val := Year_Val + 56;
325 end loop;
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).
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
336 declare
337 type D_Int is range 0 .. 2 ** (Duration'Size - 1) - 1;
338 for D_Int'Size use Duration'Size;
340 Small_Div : constant D_Int := D_Int (1.0 / Duration'Small);
341 D_As_Int : D_Int;
343 function To_D_As_Int is new Unchecked_Conversion (Duration, D_Int);
344 function To_Duration is new Unchecked_Conversion (D_Int, Duration);
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;
352 localtime_r (Adjusted_Seconds'Unchecked_Access, Tm_Val'Unchecked_Access);
354 Year_Val := Tm_Val.tm_year + 1900 + Year_Val;
355 Month := Tm_Val.tm_mon + 1;
356 Day := Tm_Val.tm_mday;
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.
363 Seconds := Duration (Tm_Val.tm_hour * 3600 +
364 Tm_Val.tm_min * 60 +
365 Tm_Val.tm_sec)
366 + Frac_Sec;
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???.
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.
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;
386 -------------
387 -- Time_Of --
388 -------------
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
397 Result_Secs : aliased time_t;
398 TM_Val : aliased tm;
399 Int_Secs : constant Integer := Integer (Seconds);
401 Year_Val : Integer := Year;
402 Duration_Adjust : Duration := 0.0;
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).
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;
419 -- Check for Day value too large (one might expect mktime to do this
420 -- check, as well as the basic checks we did with 'Valid, but it seems
421 -- that at least on some systems, this built-in check is too weak).
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;
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;
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.
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;
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;
451 TM_Val.tm_year := Year_Val - 1900;
453 -- Since we do not have information on daylight savings,
454 -- rely on the default information.
456 TM_Val.tm_isdst := -1;
457 Result_Secs := mktime (TM_Val'Unchecked_Access);
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.
465 return
466 Time (Duration (Result_Secs) +
467 Duration_Adjust +
468 (Seconds - Duration (Int_Secs)));
470 end Time_Of;
472 ----------
473 -- Year --
474 ----------
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;
482 begin
483 Split (Date, DY, DM, DD, DS);
484 return DY;
485 end Year;
487 end Ada.Calendar;