1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . C A L E N D A R --
9 -- Copyright (C) 1992-2001 Free Software Foundation, Inc. --
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. --
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. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
32 ------------------------------------------------------------------------------
34 with Unchecked_Conversion
;
36 with System
.OS_Primitives
;
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.
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
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 the range that can be handled by Unix (1970 - 2038). The trick
93 -- is that the number of days in any four year period in the Ada range of
94 -- years (1901 - 2099) has a constant number of days. This is because we
95 -- have the special case of 2000 which, contrary to the normal exception
96 -- for centuries, is a leap year after all.
98 Unix_Year_Min
: constant := 1970;
99 Unix_Year_Max
: constant := 2038;
101 Ada_Year_Min
: constant := 1901;
102 Ada_Year_Max
: constant := 2099;
104 -- Some basic constants used throughout
106 Days_In_Month
: constant array (Month_Number
) of Day_Number
:=
107 (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
109 Days_In_4_Years
: constant := 365 * 3 + 366;
110 Seconds_In_4_Years
: constant := 86_400
* Days_In_4_Years
;
111 Seconds_In_4_YearsD
: constant Duration := Duration (Seconds_In_4_Years
);
117 function "+" (Left
: Time
; Right
: Duration) return Time
is
118 pragma Unsuppress
(Overflow_Check
);
120 return (Left
+ Time
(Right
));
123 when Constraint_Error
=>
127 function "+" (Left
: Duration; Right
: Time
) return Time
is
128 pragma Unsuppress
(Overflow_Check
);
130 return (Time
(Left
) + Right
);
133 when Constraint_Error
=>
141 function "-" (Left
: Time
; Right
: Duration) return Time
is
142 pragma Unsuppress
(Overflow_Check
);
144 return Left
- Time
(Right
);
147 when Constraint_Error
=>
151 function "-" (Left
: Time
; Right
: Time
) return Duration is
152 pragma Unsuppress
(Overflow_Check
);
154 return Duration (Left
) - Duration (Right
);
157 when Constraint_Error
=>
165 function "<" (Left
, Right
: Time
) return Boolean is
167 return Duration (Left
) < Duration (Right
);
174 function "<=" (Left
, Right
: Time
) return Boolean is
176 return Duration (Left
) <= Duration (Right
);
183 function ">" (Left
, Right
: Time
) return Boolean is
185 return Duration (Left
) > Duration (Right
);
192 function ">=" (Left
, Right
: Time
) return Boolean is
194 return Duration (Left
) >= Duration (Right
);
201 function Clock
return Time
is
203 return Time
(System
.OS_Primitives
.Clock
);
210 function Day
(Date
: Time
) return Day_Number
is
217 Split
(Date
, DY
, DM
, DD
, DS
);
225 function Month
(Date
: Time
) return Month_Number
is
232 Split
(Date
, DY
, DM
, DD
, DS
);
240 function Seconds
(Date
: Time
) return Day_Duration
is
247 Split
(Date
, DY
, DM
, DD
, DS
);
257 Year
: out Year_Number
;
258 Month
: out Month_Number
;
259 Day
: out Day_Number
;
260 Seconds
: out Day_Duration
)
262 -- The following declare bounds for duration that are comfortably
263 -- wider than the maximum allowed output result for the Ada range
264 -- of representable split values. These are used for a quick check
265 -- that the value is not wildly out of range.
267 Low
: constant := (Ada_Year_Min
- Unix_Year_Min
- 2) * 365 * 86_400
;
268 High
: constant := (Ada_Year_Max
- Unix_Year_Min
+ 2) * 365 * 86_400
;
270 LowD
: constant Duration := Duration (Low
);
271 HighD
: constant Duration := Duration (High
);
273 -- The following declare the maximum duration value that can be
274 -- successfully converted to a 32-bit integer suitable for passing
275 -- to the localtime_r function. Note that we cannot assume that the
276 -- localtime_r function expands to accept 64-bit input on a 64-bit
277 -- machine, but we can count on a 32-bit range on all machines.
279 Max_Time
: constant := 2 ** 31 - 1;
280 Max_TimeD
: constant Duration := Duration (Max_Time
);
282 -- Finally the actual variables used in the computation
287 Adjusted_Seconds
: aliased time_t
;
291 -- For us a time is simply a signed duration value, so we work with
292 -- this duration value directly. Note that it can be negative.
294 D
:= Duration (Date
);
296 -- First of all, filter out completely ludicrous values. Remember
297 -- that we use the full stored range of duration values, which may
298 -- be significantly larger than the allowed range of Ada times. Note
299 -- that these checks are wider than required to make absolutely sure
300 -- that there are no end effects from time zone differences.
302 if D
< LowD
or else D
> HighD
then
306 -- The unix localtime_r function is more or less exactly what we need
307 -- here. The less comes from the fact that it does not support the
308 -- required range of years (the guaranteed range available is only
309 -- EPOCH through EPOCH + N seconds). N is in practice 2 ** 31 - 1.
311 -- If we have a value outside this range, then we first adjust it
312 -- to be in the required range by adding multiples of four years.
313 -- For the range we are interested in, the number of days in any
314 -- consecutive four year period is constant. Then we do the split
315 -- on the adjusted value, and readjust the years value accordingly.
320 D
:= D
+ Seconds_In_4_YearsD
;
321 Year_Val
:= Year_Val
- 4;
324 while D
> Max_TimeD
loop
325 D
:= D
- Seconds_In_4_YearsD
;
326 Year_Val
:= Year_Val
+ 4;
329 -- Now we need to take the value D, which is now non-negative, and
330 -- break it down into seconds (to pass to the localtime_r function)
331 -- and fractions of seconds (for the adjustment below).
333 -- Surprisingly there is no easy way to do this in Ada, and certainly
334 -- no easy way to do it and generate efficient code. Therefore we
335 -- do it at a low level, knowing that it is really represented as
336 -- an integer with units of Small
339 type D_Int
is range 0 .. 2 ** (Duration'Size - 1) - 1;
340 for D_Int
'Size use Duration'Size;
342 Small_Div
: constant D_Int
:= D_Int
(1.0 / Duration'Small);
345 function To_D_As_Int
is new Unchecked_Conversion
(Duration, D_Int
);
346 function To_Duration
is new Unchecked_Conversion
(D_Int
, Duration);
349 D_As_Int
:= To_D_As_Int
(D
);
350 Adjusted_Seconds
:= time_t
(D_As_Int
/ Small_Div
);
351 Frac_Sec
:= To_Duration
(D_As_Int
rem Small_Div
);
354 localtime_r
(Adjusted_Seconds
'Unchecked_Access, Tm_Val
'Unchecked_Access);
356 Year_Val
:= Tm_Val
.tm_year
+ 1900 + Year_Val
;
357 Month
:= Tm_Val
.tm_mon
+ 1;
358 Day
:= Tm_Val
.tm_mday
;
360 -- The Seconds value is a little complex. The localtime function
361 -- returns the integral number of seconds, which is what we want,
362 -- but we want to retain the fractional part from the original
363 -- Time value, since this is typically stored more accurately.
365 Seconds
:= Duration (Tm_Val
.tm_hour
* 3600 +
370 -- Note: the above expression is pretty horrible, one of these days
371 -- we should stop using time_of and do everything ourselves to avoid
372 -- these unnecessary divides and multiplies???.
374 -- The Year may still be out of range, since our entry test was
375 -- deliberately crude. Trying to make this entry test accurate is
376 -- tricky due to time zone adjustment issues affecting the exact
377 -- boundary. It is interesting to note that whether or not a given
378 -- Calendar.Time value gets Time_Error when split depends on the
379 -- current time zone setting.
381 if Year_Val
not in Ada_Year_Min
.. Ada_Year_Max
then
394 Month
: Month_Number
;
396 Seconds
: Day_Duration
:= 0.0)
399 Result_Secs
: aliased time_t
;
401 Int_Secs
: constant Integer := Integer (Seconds
);
403 Year_Val
: Integer := Year
;
404 Duration_Adjust
: Duration := 0.0;
407 -- The following checks are redundant with respect to the constraint
408 -- error checks that should normally be made on parameters, but we
409 -- decide to raise Constraint_Error in any case if bad values come
410 -- in (as a result of checks being off in the caller, or for other
411 -- erroneous or bounded error cases).
414 or else not Month 'Valid
415 or else not Day
'Valid
416 or else not Seconds'Valid
418 raise Constraint_Error;
421 -- Check for Day value too large (one might expect mktime to do this
422 -- check, as well as the basi checks we did with 'Valid
, but it seems
423 -- that at least on some systems, this built-in check is too weak).
425 if Day
> Days_In_Month
(Month
)
426 and then (Day
/= 29 or Month
/= 2 or Year
mod 4 /= 0)
431 TM_Val
.tm_sec
:= Int_Secs
mod 60;
432 TM_Val
.tm_min
:= (Int_Secs
/ 60) mod 60;
433 TM_Val
.tm_hour
:= (Int_Secs
/ 60) / 60;
434 TM_Val
.tm_mday
:= Day
;
435 TM_Val
.tm_mon
:= Month
- 1;
437 -- For the year, we have to adjust it to a year that Unix can handle.
438 -- We do this in four year steps, since the number of days in four
439 -- years is constant, so the timezone effect on the conversion from
440 -- local time to GMT is unaffected.
442 while Year_Val
<= Unix_Year_Min
loop
443 Year_Val
:= Year_Val
+ 4;
444 Duration_Adjust
:= Duration_Adjust
- Seconds_In_4_YearsD
;
447 while Year_Val
>= Unix_Year_Max
loop
448 Year_Val
:= Year_Val
- 4;
449 Duration_Adjust
:= Duration_Adjust
+ Seconds_In_4_YearsD
;
452 TM_Val
.tm_year
:= Year_Val
- 1900;
454 -- Since we do not have information on daylight savings,
455 -- rely on the default information.
457 TM_Val
.tm_isdst
:= -1;
458 Result_Secs
:= mktime
(TM_Val
'Unchecked_Access);
460 -- That gives us the basic value in seconds. Two adjustments are
461 -- needed. First we must undo the year adjustment carried out above.
462 -- Second we put back the fraction seconds value since in general the
463 -- Day_Duration value we received has additional precision which we
464 -- do not want to lose in the constructed result.
467 Time
(Duration (Result_Secs
) +
469 (Seconds
- Duration (Int_Secs
)));
477 function Year
(Date
: Time
) return Year_Number
is
484 Split
(Date
, DY
, DM
, DD
, DS
);