Update concepts branch to revision 131834
[official-gcc.git] / gcc / ada / a-calend-vms.adb
blob6cbc7288d237ab5a95a8a8e408c782e1bcdde087
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-2008, 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, 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 ------------------------------------------------------------------------------
34 -- This is the Alpha/VMS version
36 with Ada.Unchecked_Conversion;
38 with System.Aux_DEC; use System.Aux_DEC;
39 with System.OS_Primitives; use System.OS_Primitives;
41 package body Ada.Calendar is
43 --------------------------
44 -- Implementation Notes --
45 --------------------------
47 -- Variables of type Ada.Calendar.Time have suffix _S or _M to denote
48 -- units of seconds or milis.
50 -- Because time is measured in different units and from different origins
51 -- on various targets, a system independent model is incorporated into
52 -- Ada.Calendar. The idea behind the design is to encapsulate all target
53 -- dependent machinery in a single package, thus providing a uniform
54 -- interface to all existing and any potential children.
56 -- package Ada.Calendar
57 -- procedure Split (5 parameters) -------+
58 -- | Call from local routine
59 -- private |
60 -- package Formatting_Operations |
61 -- procedure Split (11 parameters) <--+
62 -- end Formatting_Operations |
63 -- end Ada.Calendar |
64 -- |
65 -- package Ada.Calendar.Formatting | Call from child routine
66 -- procedure Split (9 or 10 parameters) -+
67 -- end Ada.Calendar.Formatting
69 -- The behaviour of the interfacing routines is controlled via various
70 -- flags. All new Ada 2005 types from children of Ada.Calendar are
71 -- emulated by a similar type. For instance, type Day_Number is replaced
72 -- by Integer in various routines. One ramification of this model is that
73 -- the caller site must perform validity checks on returned results.
74 -- The end result of this model is the lack of target specific files per
75 -- child of Ada.Calendar (a-calfor, a-calfor-vms, a-calfor-vxwors, etc).
77 -----------------------
78 -- Local Subprograms --
79 -----------------------
81 procedure Check_Within_Time_Bounds (T : OS_Time);
82 -- Ensure that a time representation value falls withing the bounds of Ada
83 -- time. Leap seconds support is taken into account.
85 procedure Cumulative_Leap_Seconds
86 (Start_Date : OS_Time;
87 End_Date : OS_Time;
88 Elapsed_Leaps : out Natural;
89 Next_Leap_Sec : out OS_Time);
90 -- Elapsed_Leaps is the sum of the leap seconds that have occurred on or
91 -- after Start_Date and before (strictly before) End_Date. Next_Leap_Sec
92 -- represents the next leap second occurrence on or after End_Date. If
93 -- there are no leaps seconds after End_Date, End_Of_Time is returned.
94 -- End_Of_Time can be used as End_Date to count all the leap seconds that
95 -- have occurred on or after Start_Date.
97 -- Note: Any sub seconds of Start_Date and End_Date are discarded before
98 -- the calculations are done. For instance: if 113 seconds is a leap
99 -- second (it isn't) and 113.5 is input as an End_Date, the leap second
100 -- at 113 will not be counted in Leaps_Between, but it will be returned
101 -- as Next_Leap_Sec. Thus, if the caller wants to know if the End_Date is
102 -- a leap second, the comparison should be:
104 -- End_Date >= Next_Leap_Sec;
106 -- After_Last_Leap is designed so that this comparison works without
107 -- having to first check if Next_Leap_Sec is a valid leap second.
109 function To_Duration (T : Time) return Duration;
110 function To_Relative_Time (D : Duration) return Time;
111 -- It is important to note that duration's fractional part denotes nano
112 -- seconds while the units of Time are 100 nanoseconds. If a regular
113 -- Unchecked_Conversion was employed, the resulting values would be off
114 -- by 100.
116 --------------------------
117 -- Leap seconds control --
118 --------------------------
120 Flag : Integer;
121 pragma Import (C, Flag, "__gl_leap_seconds_support");
122 -- This imported value is used to determine whether the compilation had
123 -- binder flag "-y" present which enables leap seconds. A value of zero
124 -- signifies no leap seconds support while a value of one enables the
125 -- support.
127 Leap_Support : constant Boolean := Flag = 1;
128 -- The above flag controls the usage of leap seconds in all Ada.Calendar
129 -- routines.
131 Leap_Seconds_Count : constant Natural := 23;
133 ---------------------
134 -- Local Constants --
135 ---------------------
137 -- The range of Ada time expressed as milis since the VMS Epoch
139 Ada_Low : constant OS_Time := (10 * 366 + 32 * 365 + 45) * Milis_In_Day;
140 Ada_High : constant OS_Time := (131 * 366 + 410 * 365 + 45) * Milis_In_Day;
142 -- Even though the upper bound of time is 2399-12-31 23:59:59.9999999
143 -- UTC, it must be increased to include all leap seconds.
145 Ada_High_And_Leaps : constant OS_Time :=
146 Ada_High + OS_Time (Leap_Seconds_Count) * Mili;
148 -- Two constants used in the calculations of elapsed leap seconds.
149 -- End_Of_Time is later than Ada_High in time zone -28. Start_Of_Time
150 -- is earlier than Ada_Low in time zone +28.
152 End_Of_Time : constant OS_Time := Ada_High + OS_Time (3) * Milis_In_Day;
153 Start_Of_Time : constant OS_Time := Ada_Low - OS_Time (3) * Milis_In_Day;
155 -- The following table contains the hard time values of all existing leap
156 -- seconds. The values are produced by the utility program xleaps.adb.
158 Leap_Second_Times : constant array (1 .. Leap_Seconds_Count) of OS_Time :=
159 (35855136000000000,
160 36014112010000000,
161 36329472020000000,
162 36644832030000000,
163 36960192040000000,
164 37276416050000000,
165 37591776060000000,
166 37907136070000000,
167 38222496080000000,
168 38695104090000000,
169 39010464100000000,
170 39325824110000000,
171 39957408120000000,
172 40747104130000000,
173 41378688140000000,
174 41694048150000000,
175 42166656160000000,
176 42482016170000000,
177 42797376180000000,
178 43271712190000000,
179 43744320200000000,
180 44218656210000000,
181 46427904220000000);
183 ---------
184 -- "+" --
185 ---------
187 function "+" (Left : Time; Right : Duration) return Time is
188 pragma Unsuppress (Overflow_Check);
189 begin
190 return Left + To_Relative_Time (Right);
191 exception
192 when Constraint_Error =>
193 raise Time_Error;
194 end "+";
196 function "+" (Left : Duration; Right : Time) return Time is
197 pragma Unsuppress (Overflow_Check);
198 begin
199 return Right + Left;
200 exception
201 when Constraint_Error =>
202 raise Time_Error;
203 end "+";
205 ---------
206 -- "-" --
207 ---------
209 function "-" (Left : Time; Right : Duration) return Time is
210 pragma Unsuppress (Overflow_Check);
211 begin
212 return Left - To_Relative_Time (Right);
213 exception
214 when Constraint_Error =>
215 raise Time_Error;
216 end "-";
218 function "-" (Left : Time; Right : Time) return Duration is
219 pragma Unsuppress (Overflow_Check);
221 -- The bound of type Duration expressed as time
223 Dur_High : constant OS_Time :=
224 OS_Time (To_Relative_Time (Duration'Last));
225 Dur_Low : constant OS_Time :=
226 OS_Time (To_Relative_Time (Duration'First));
228 Res_M : OS_Time;
230 begin
231 Res_M := OS_Time (Left) - OS_Time (Right);
233 -- Due to the extended range of Ada time, "-" is capable of producing
234 -- results which may exceed the range of Duration. In order to prevent
235 -- the generation of bogus values by the Unchecked_Conversion, we apply
236 -- the following check.
238 if Res_M < Dur_Low
239 or else Res_M >= Dur_High
240 then
241 raise Time_Error;
243 -- Normal case, result fits
245 else
246 return To_Duration (Time (Res_M));
247 end if;
249 exception
250 when Constraint_Error =>
251 raise Time_Error;
252 end "-";
254 ---------
255 -- "<" --
256 ---------
258 function "<" (Left, Right : Time) return Boolean is
259 begin
260 return OS_Time (Left) < OS_Time (Right);
261 end "<";
263 ----------
264 -- "<=" --
265 ----------
267 function "<=" (Left, Right : Time) return Boolean is
268 begin
269 return OS_Time (Left) <= OS_Time (Right);
270 end "<=";
272 ---------
273 -- ">" --
274 ---------
276 function ">" (Left, Right : Time) return Boolean is
277 begin
278 return OS_Time (Left) > OS_Time (Right);
279 end ">";
281 ----------
282 -- ">=" --
283 ----------
285 function ">=" (Left, Right : Time) return Boolean is
286 begin
287 return OS_Time (Left) >= OS_Time (Right);
288 end ">=";
290 ------------------------------
291 -- Check_Within_Time_Bounds --
292 ------------------------------
294 procedure Check_Within_Time_Bounds (T : OS_Time) is
295 begin
296 if Leap_Support then
297 if T < Ada_Low or else T > Ada_High_And_Leaps then
298 raise Time_Error;
299 end if;
300 else
301 if T < Ada_Low or else T > Ada_High then
302 raise Time_Error;
303 end if;
304 end if;
305 end Check_Within_Time_Bounds;
307 -----------
308 -- Clock --
309 -----------
311 function Clock return Time is
312 Elapsed_Leaps : Natural;
313 Next_Leap_M : OS_Time;
314 Res_M : constant OS_Time := OS_Clock;
316 begin
317 -- Note that on other targets a soft-link is used to get a different
318 -- clock depending whether tasking is used or not. On VMS this isn't
319 -- needed since all clock calls end up using SYS$GETTIM, so call the
320 -- OS_Primitives version for efficiency.
322 -- If the target supports leap seconds, determine the number of leap
323 -- seconds elapsed until this moment.
325 if Leap_Support then
326 Cumulative_Leap_Seconds
327 (Start_Of_Time, Res_M, Elapsed_Leaps, Next_Leap_M);
329 -- The system clock may fall exactly on a leap second
331 if Res_M >= Next_Leap_M then
332 Elapsed_Leaps := Elapsed_Leaps + 1;
333 end if;
335 -- The target does not support leap seconds
337 else
338 Elapsed_Leaps := 0;
339 end if;
341 return Time (Res_M + OS_Time (Elapsed_Leaps) * Mili);
342 end Clock;
344 -----------------------------
345 -- Cumulative_Leap_Seconds --
346 -----------------------------
348 procedure Cumulative_Leap_Seconds
349 (Start_Date : OS_Time;
350 End_Date : OS_Time;
351 Elapsed_Leaps : out Natural;
352 Next_Leap_Sec : out OS_Time)
354 End_Index : Positive;
355 End_T : OS_Time := End_Date;
356 Start_Index : Positive;
357 Start_T : OS_Time := Start_Date;
359 begin
360 pragma Assert (Leap_Support and then End_Date >= Start_Date);
362 Next_Leap_Sec := End_Of_Time;
364 -- Make sure that the end date does not exceed the upper bound
365 -- of Ada time.
367 if End_Date > Ada_High then
368 End_T := Ada_High;
369 end if;
371 -- Remove the sub seconds from both dates
373 Start_T := Start_T - (Start_T mod Mili);
374 End_T := End_T - (End_T mod Mili);
376 -- Some trivial cases:
377 -- Leap 1 . . . Leap N
378 -- ---+========+------+############+-------+========+-----
379 -- Start_T End_T Start_T End_T
381 if End_T < Leap_Second_Times (1) then
382 Elapsed_Leaps := 0;
383 Next_Leap_Sec := Leap_Second_Times (1);
384 return;
386 elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
387 Elapsed_Leaps := 0;
388 Next_Leap_Sec := End_Of_Time;
389 return;
390 end if;
392 -- Perform the calculations only if the start date is within the leap
393 -- second occurrences table.
395 if Start_T <= Leap_Second_Times (Leap_Seconds_Count) then
397 -- 1 2 N - 1 N
398 -- +----+----+-- . . . --+-------+---+
399 -- | T1 | T2 | | N - 1 | N |
400 -- +----+----+-- . . . --+-------+---+
401 -- ^ ^
402 -- | Start_Index | End_Index
403 -- +-------------------+
404 -- Leaps_Between
406 -- The idea behind the algorithm is to iterate and find two closest
407 -- dates which are after Start_T and End_T. Their corresponding
408 -- index difference denotes the number of leap seconds elapsed.
410 Start_Index := 1;
411 loop
412 exit when Leap_Second_Times (Start_Index) >= Start_T;
413 Start_Index := Start_Index + 1;
414 end loop;
416 End_Index := Start_Index;
417 loop
418 exit when End_Index > Leap_Seconds_Count
419 or else Leap_Second_Times (End_Index) >= End_T;
420 End_Index := End_Index + 1;
421 end loop;
423 if End_Index <= Leap_Seconds_Count then
424 Next_Leap_Sec := Leap_Second_Times (End_Index);
425 end if;
427 Elapsed_Leaps := End_Index - Start_Index;
429 else
430 Elapsed_Leaps := 0;
431 end if;
432 end Cumulative_Leap_Seconds;
434 ---------
435 -- Day --
436 ---------
438 function Day (Date : Time) return Day_Number is
439 Y : Year_Number;
440 M : Month_Number;
441 D : Day_Number;
442 S : Day_Duration;
443 pragma Unreferenced (Y, M, S);
444 begin
445 Split (Date, Y, M, D, S);
446 return D;
447 end Day;
449 -------------
450 -- Is_Leap --
451 -------------
453 function Is_Leap (Year : Year_Number) return Boolean is
454 begin
455 -- Leap centennial years
457 if Year mod 400 = 0 then
458 return True;
460 -- Non-leap centennial years
462 elsif Year mod 100 = 0 then
463 return False;
465 -- Regular years
467 else
468 return Year mod 4 = 0;
469 end if;
470 end Is_Leap;
472 -----------
473 -- Month --
474 -----------
476 function Month (Date : Time) return Month_Number is
477 Y : Year_Number;
478 M : Month_Number;
479 D : Day_Number;
480 S : Day_Duration;
481 pragma Unreferenced (Y, D, S);
482 begin
483 Split (Date, Y, M, D, S);
484 return M;
485 end Month;
487 -------------
488 -- Seconds --
489 -------------
491 function Seconds (Date : Time) return Day_Duration is
492 Y : Year_Number;
493 M : Month_Number;
494 D : Day_Number;
495 S : Day_Duration;
496 pragma Unreferenced (Y, M, D);
497 begin
498 Split (Date, Y, M, D, S);
499 return S;
500 end Seconds;
502 -----------
503 -- Split --
504 -----------
506 procedure Split
507 (Date : Time;
508 Year : out Year_Number;
509 Month : out Month_Number;
510 Day : out Day_Number;
511 Seconds : out Day_Duration)
513 H : Integer;
514 M : Integer;
515 Se : Integer;
516 Ss : Duration;
517 Le : Boolean;
519 begin
520 -- Use UTC as the local time zone on VMS, the status of flag Is_Ada_05
521 -- is irrelevant in this case.
523 Formatting_Operations.Split
524 (Date => Date,
525 Year => Year,
526 Month => Month,
527 Day => Day,
528 Day_Secs => Seconds,
529 Hour => H,
530 Minute => M,
531 Second => Se,
532 Sub_Sec => Ss,
533 Leap_Sec => Le,
534 Is_Ada_05 => False,
535 Time_Zone => 0);
537 -- Validity checks
539 if not Year'Valid
540 or else not Month'Valid
541 or else not Day'Valid
542 or else not Seconds'Valid
543 then
544 raise Time_Error;
545 end if;
546 end Split;
548 -------------
549 -- Time_Of --
550 -------------
552 function Time_Of
553 (Year : Year_Number;
554 Month : Month_Number;
555 Day : Day_Number;
556 Seconds : Day_Duration := 0.0) return Time
558 -- The values in the following constants are irrelevant, they are just
559 -- placeholders; the choice of constructing a Day_Duration value is
560 -- controlled by the Use_Day_Secs flag.
562 H : constant Integer := 1;
563 M : constant Integer := 1;
564 Se : constant Integer := 1;
565 Ss : constant Duration := 0.1;
567 begin
568 if not Year'Valid
569 or else not Month'Valid
570 or else not Day'Valid
571 or else not Seconds'Valid
572 then
573 raise Time_Error;
574 end if;
576 -- Use UTC as the local time zone on VMS, the status of flag Is_Ada_05
577 -- is irrelevant in this case.
579 return
580 Formatting_Operations.Time_Of
581 (Year => Year,
582 Month => Month,
583 Day => Day,
584 Day_Secs => Seconds,
585 Hour => H,
586 Minute => M,
587 Second => Se,
588 Sub_Sec => Ss,
589 Leap_Sec => False,
590 Use_Day_Secs => True,
591 Is_Ada_05 => False,
592 Time_Zone => 0);
593 end Time_Of;
595 -----------------
596 -- To_Duration --
597 -----------------
599 function To_Duration (T : Time) return Duration is
600 function Time_To_Duration is
601 new Ada.Unchecked_Conversion (Time, Duration);
602 begin
603 return Time_To_Duration (T * 100);
604 end To_Duration;
606 ----------------------
607 -- To_Relative_Time --
608 ----------------------
610 function To_Relative_Time (D : Duration) return Time is
611 function Duration_To_Time is
612 new Ada.Unchecked_Conversion (Duration, Time);
613 begin
614 return Duration_To_Time (D / 100.0);
615 end To_Relative_Time;
617 ----------
618 -- Year --
619 ----------
621 function Year (Date : Time) return Year_Number is
622 Y : Year_Number;
623 M : Month_Number;
624 D : Day_Number;
625 S : Day_Duration;
626 pragma Unreferenced (M, D, S);
627 begin
628 Split (Date, Y, M, D, S);
629 return Y;
630 end Year;
632 -- The following packages assume that Time is a Long_Integer, the units
633 -- are 100 nanoseconds and the starting point in the VMS Epoch.
635 ---------------------------
636 -- Arithmetic_Operations --
637 ---------------------------
639 package body Arithmetic_Operations is
641 ---------
642 -- Add --
643 ---------
645 function Add (Date : Time; Days : Long_Integer) return Time is
646 pragma Unsuppress (Overflow_Check);
647 Date_M : constant OS_Time := OS_Time (Date);
648 begin
649 return Time (Date_M + OS_Time (Days) * Milis_In_Day);
650 exception
651 when Constraint_Error =>
652 raise Time_Error;
653 end Add;
655 ----------------
656 -- Difference --
657 ----------------
659 procedure Difference
660 (Left : Time;
661 Right : Time;
662 Days : out Long_Integer;
663 Seconds : out Duration;
664 Leap_Seconds : out Integer)
666 Diff_M : OS_Time;
667 Diff_S : OS_Time;
668 Earlier : OS_Time;
669 Elapsed_Leaps : Natural;
670 Later : OS_Time;
671 Negate : Boolean := False;
672 Next_Leap : OS_Time;
673 Sub_Seconds : Duration;
675 begin
676 -- This classification is necessary in order to avoid a Time_Error
677 -- being raised by the arithmetic operators in Ada.Calendar.
679 if Left >= Right then
680 Later := OS_Time (Left);
681 Earlier := OS_Time (Right);
682 else
683 Later := OS_Time (Right);
684 Earlier := OS_Time (Left);
685 Negate := True;
686 end if;
688 -- If the target supports leap seconds, process them
690 if Leap_Support then
691 Cumulative_Leap_Seconds
692 (Earlier, Later, Elapsed_Leaps, Next_Leap);
694 if Later >= Next_Leap then
695 Elapsed_Leaps := Elapsed_Leaps + 1;
696 end if;
698 -- The target does not support leap seconds
700 else
701 Elapsed_Leaps := 0;
702 end if;
704 Diff_M := Later - Earlier - OS_Time (Elapsed_Leaps) * Mili;
706 -- Sub second processing
708 Sub_Seconds := Duration (Diff_M mod Mili) / Mili_F;
710 -- Convert to seconds. Note that his action eliminates the sub
711 -- seconds automatically.
713 Diff_S := Diff_M / Mili;
715 Days := Long_Integer (Diff_S / Secs_In_Day);
716 Seconds := Duration (Diff_S mod Secs_In_Day) + Sub_Seconds;
717 Leap_Seconds := Integer (Elapsed_Leaps);
719 if Negate then
720 Days := -Days;
721 Seconds := -Seconds;
723 if Leap_Seconds /= 0 then
724 Leap_Seconds := -Leap_Seconds;
725 end if;
726 end if;
727 end Difference;
729 --------------
730 -- Subtract --
731 --------------
733 function Subtract (Date : Time; Days : Long_Integer) return Time is
734 pragma Unsuppress (Overflow_Check);
735 Date_M : constant OS_Time := OS_Time (Date);
736 begin
737 return Time (Date_M - OS_Time (Days) * Milis_In_Day);
738 exception
739 when Constraint_Error =>
740 raise Time_Error;
741 end Subtract;
742 end Arithmetic_Operations;
744 ---------------------------
745 -- Conversion_Operations --
746 ---------------------------
748 package body Conversion_Operations is
750 Epoch_Offset : constant OS_Time := 35067168000000000;
751 -- The difference between 1970-1-1 UTC and 1858-11-17 UTC expressed in
752 -- 100 nanoseconds.
754 -----------------
755 -- To_Ada_Time --
756 -----------------
758 function To_Ada_Time (Unix_Time : Long_Integer) return Time is
759 pragma Unsuppress (Overflow_Check);
760 Unix_Rep : constant OS_Time := OS_Time (Unix_Time) * Mili;
761 begin
762 return Time (Unix_Rep + Epoch_Offset);
763 exception
764 when Constraint_Error =>
765 raise Time_Error;
766 end To_Ada_Time;
768 -----------------
769 -- To_Ada_Time --
770 -----------------
772 function To_Ada_Time
773 (tm_year : Integer;
774 tm_mon : Integer;
775 tm_day : Integer;
776 tm_hour : Integer;
777 tm_min : Integer;
778 tm_sec : Integer;
779 tm_isdst : Integer) return Time
781 pragma Unsuppress (Overflow_Check);
783 Year_Shift : constant Integer := 1900;
784 Month_Shift : constant Integer := 1;
786 Year : Year_Number;
787 Month : Month_Number;
788 Day : Day_Number;
789 Second : Integer;
790 Leap : Boolean;
791 Result : OS_Time;
793 begin
794 -- Input processing
796 Year := Year_Number (Year_Shift + tm_year);
797 Month := Month_Number (Month_Shift + tm_mon);
798 Day := Day_Number (tm_day);
800 -- Step 1: Validity checks of input values
802 if not Year'Valid
803 or else not Month'Valid
804 or else not Day'Valid
805 or else tm_hour not in 0 .. 24
806 or else tm_min not in 0 .. 59
807 or else tm_sec not in 0 .. 60
808 or else tm_isdst not in -1 .. 1
809 then
810 raise Time_Error;
811 end if;
813 -- Step 2: Potential leap second
815 if tm_sec = 60 then
816 Leap := True;
817 Second := 59;
818 else
819 Leap := False;
820 Second := tm_sec;
821 end if;
823 -- Step 3: Calculate the time value
825 Result :=
826 OS_Time
827 (Formatting_Operations.Time_Of
828 (Year => Year,
829 Month => Month,
830 Day => Day,
831 Day_Secs => 0.0, -- Time is given in h:m:s
832 Hour => tm_hour,
833 Minute => tm_min,
834 Second => Second,
835 Sub_Sec => 0.0, -- No precise sub second given
836 Leap_Sec => Leap,
837 Use_Day_Secs => False, -- Time is given in h:m:s
838 Is_Ada_05 => True, -- Force usage of explicit time zone
839 Time_Zone => 0)); -- Place the value in UTC
840 -- Step 4: Daylight Savings Time
842 if tm_isdst = 1 then
843 Result := Result + OS_Time (3_600) * Mili;
844 end if;
846 return Time (Result);
847 exception
848 when Constraint_Error =>
849 raise Time_Error;
850 end To_Ada_Time;
852 -----------------
853 -- To_Duration --
854 -----------------
856 function To_Duration
857 (tv_sec : Long_Integer;
858 tv_nsec : Long_Integer) return Duration
860 pragma Unsuppress (Overflow_Check);
861 begin
862 return Duration (tv_sec) + Duration (tv_nsec) / Mili_F;
863 end To_Duration;
865 ------------------------
866 -- To_Struct_Timespec --
867 ------------------------
869 procedure To_Struct_Timespec
870 (D : Duration;
871 tv_sec : out Long_Integer;
872 tv_nsec : out Long_Integer)
874 pragma Unsuppress (Overflow_Check);
875 Secs : Duration;
876 Nano_Secs : Duration;
878 begin
879 -- Seconds extraction, avoid potential rounding errors
881 Secs := D - 0.5;
882 tv_sec := Long_Integer (Secs);
884 -- 100 Nanoseconds extraction
886 Nano_Secs := D - Duration (tv_sec);
887 tv_nsec := Long_Integer (Nano_Secs * Mili);
888 end To_Struct_Timespec;
890 ------------------
891 -- To_Struct_Tm --
892 ------------------
894 procedure To_Struct_Tm
895 (T : Time;
896 tm_year : out Integer;
897 tm_mon : out Integer;
898 tm_day : out Integer;
899 tm_hour : out Integer;
900 tm_min : out Integer;
901 tm_sec : out Integer)
903 pragma Unsuppress (Overflow_Check);
904 Year : Year_Number;
905 Month : Month_Number;
906 Second : Integer;
907 Day_Secs : Day_Duration;
908 Sub_Sec : Duration;
909 Leap_Sec : Boolean;
911 begin
912 -- Step 1: Split the input time
914 Formatting_Operations.Split
915 (T, Year, Month, tm_day, Day_Secs,
916 tm_hour, tm_min, Second, Sub_Sec, Leap_Sec, True, 0);
918 -- Step 2: Correct the year and month
920 tm_year := Year - 1900;
921 tm_mon := Month - 1;
923 -- Step 3: Handle leap second occurrences
925 if Leap_Sec then
926 tm_sec := 60;
927 else
928 tm_sec := Second;
929 end if;
930 end To_Struct_Tm;
932 ------------------
933 -- To_Unix_Time --
934 ------------------
936 function To_Unix_Time (Ada_Time : Time) return Long_Integer is
937 pragma Unsuppress (Overflow_Check);
938 Ada_OS_Time : constant OS_Time := OS_Time (Ada_Time);
939 begin
940 return Long_Integer ((Ada_OS_Time - Epoch_Offset) / Mili);
941 exception
942 when Constraint_Error =>
943 raise Time_Error;
944 end To_Unix_Time;
945 end Conversion_Operations;
947 ---------------------------
948 -- Formatting_Operations --
949 ---------------------------
951 package body Formatting_Operations is
953 -----------------
954 -- Day_Of_Week --
955 -----------------
957 function Day_Of_Week (Date : Time) return Integer is
958 Y : Year_Number;
959 M : Month_Number;
960 D : Day_Number;
961 S : Day_Duration;
963 Day_Count : Long_Integer;
964 Midday_Date_S : Time;
966 begin
967 Split (Date, Y, M, D, S);
969 -- Build a time value in the middle of the same day and convert the
970 -- time value to seconds.
972 Midday_Date_S := Time_Of (Y, M, D, 43_200.0) / Mili;
974 -- Count the number of days since the start of VMS time. 1858-11-17
975 -- was a Wednesday.
977 Day_Count := Long_Integer (Midday_Date_S / Secs_In_Day) + 2;
979 return Integer (Day_Count mod 7);
980 end Day_Of_Week;
982 -----------
983 -- Split --
984 -----------
986 procedure Split
987 (Date : Time;
988 Year : out Year_Number;
989 Month : out Month_Number;
990 Day : out Day_Number;
991 Day_Secs : out Day_Duration;
992 Hour : out Integer;
993 Minute : out Integer;
994 Second : out Integer;
995 Sub_Sec : out Duration;
996 Leap_Sec : out Boolean;
997 Is_Ada_05 : Boolean;
998 Time_Zone : Long_Integer)
1000 -- The flag Is_Ada_05 is present for interfacing purposes
1002 pragma Unreferenced (Is_Ada_05);
1004 procedure Numtim
1005 (Status : out Unsigned_Longword;
1006 Timbuf : out Unsigned_Word_Array;
1007 Timadr : Time);
1009 pragma Interface (External, Numtim);
1011 pragma Import_Valued_Procedure
1012 (Numtim, "SYS$NUMTIM",
1013 (Unsigned_Longword, Unsigned_Word_Array, Time),
1014 (Value, Reference, Reference));
1016 Status : Unsigned_Longword;
1017 Timbuf : Unsigned_Word_Array (1 .. 7);
1019 Ada_Min_Year : constant := 1901;
1020 Ada_Max_Year : constant := 2399;
1022 Date_M : OS_Time;
1023 Elapsed_Leaps : Natural;
1024 Next_Leap_M : OS_Time;
1026 begin
1027 Date_M := OS_Time (Date);
1029 -- Step 1: Leap seconds processing
1031 if Leap_Support then
1032 Cumulative_Leap_Seconds
1033 (Start_Of_Time, Date_M, Elapsed_Leaps, Next_Leap_M);
1035 Leap_Sec := Date_M >= Next_Leap_M;
1037 if Leap_Sec then
1038 Elapsed_Leaps := Elapsed_Leaps + 1;
1039 end if;
1041 -- The target does not support leap seconds
1043 else
1044 Elapsed_Leaps := 0;
1045 Leap_Sec := False;
1046 end if;
1048 Date_M := Date_M - OS_Time (Elapsed_Leaps) * Mili;
1050 -- Step 2: Time zone processing
1052 if Time_Zone /= 0 then
1053 Date_M := Date_M + OS_Time (Time_Zone) * 60 * Mili;
1054 end if;
1056 -- After the leap seconds and time zone have been accounted for,
1057 -- the date should be within the bounds of Ada time.
1059 if Date_M < Ada_Low
1060 or else Date_M > Ada_High
1061 then
1062 raise Time_Error;
1063 end if;
1065 -- Step 3: Sub second processing
1067 Sub_Sec := Duration (Date_M mod Mili) / Mili_F;
1069 -- Drop the sub seconds
1071 Date_M := Date_M - (Date_M mod Mili);
1073 -- Step 4: VMS system call
1075 Numtim (Status, Timbuf, Time (Date_M));
1077 if Status mod 2 /= 1
1078 or else Timbuf (1) not in Ada_Min_Year .. Ada_Max_Year
1079 then
1080 raise Time_Error;
1081 end if;
1083 -- Step 5: Time components processing
1085 Year := Year_Number (Timbuf (1));
1086 Month := Month_Number (Timbuf (2));
1087 Day := Day_Number (Timbuf (3));
1088 Hour := Integer (Timbuf (4));
1089 Minute := Integer (Timbuf (5));
1090 Second := Integer (Timbuf (6));
1092 Day_Secs := Day_Duration (Hour * 3_600) +
1093 Day_Duration (Minute * 60) +
1094 Day_Duration (Second) +
1095 Sub_Sec;
1096 end Split;
1098 -------------
1099 -- Time_Of --
1100 -------------
1102 function Time_Of
1103 (Year : Year_Number;
1104 Month : Month_Number;
1105 Day : Day_Number;
1106 Day_Secs : Day_Duration;
1107 Hour : Integer;
1108 Minute : Integer;
1109 Second : Integer;
1110 Sub_Sec : Duration;
1111 Leap_Sec : Boolean := False;
1112 Use_Day_Secs : Boolean := False;
1113 Is_Ada_05 : Boolean := False;
1114 Time_Zone : Long_Integer := 0) return Time
1116 procedure Cvt_Vectim
1117 (Status : out Unsigned_Longword;
1118 Input_Time : Unsigned_Word_Array;
1119 Resultant_Time : out Time);
1121 pragma Interface (External, Cvt_Vectim);
1123 pragma Import_Valued_Procedure
1124 (Cvt_Vectim, "LIB$CVT_VECTIM",
1125 (Unsigned_Longword, Unsigned_Word_Array, Time),
1126 (Value, Reference, Reference));
1128 Status : Unsigned_Longword;
1129 Timbuf : Unsigned_Word_Array (1 .. 7);
1131 Y : Year_Number := Year;
1132 Mo : Month_Number := Month;
1133 D : Day_Number := Day;
1134 H : Integer := Hour;
1135 Mi : Integer := Minute;
1136 Se : Integer := Second;
1137 Su : Duration := Sub_Sec;
1139 Elapsed_Leaps : Natural;
1140 Int_Day_Secs : Integer;
1141 Next_Leap_M : OS_Time;
1142 Res : Time;
1143 Res_M : OS_Time;
1144 Rounded_Res_M : OS_Time;
1146 begin
1147 -- No validity checks are performed on the input values since it is
1148 -- assumed that the called has already performed them.
1150 -- Step 1: Hour, minute, second and sub second processing
1152 if Use_Day_Secs then
1154 -- A day seconds value of 86_400 designates a new day
1156 if Day_Secs = 86_400.0 then
1157 declare
1158 Adj_Year : Year_Number := Year;
1159 Adj_Month : Month_Number := Month;
1160 Adj_Day : Day_Number := Day;
1162 begin
1163 if Day < Days_In_Month (Month)
1164 or else (Month = 2
1165 and then Is_Leap (Year))
1166 then
1167 Adj_Day := Day + 1;
1169 -- The day adjustment moves the date to a new month
1171 else
1172 Adj_Day := 1;
1174 if Month < 12 then
1175 Adj_Month := Month + 1;
1177 -- The month adjustment moves the date to a new year
1179 else
1180 Adj_Month := 1;
1181 Adj_Year := Year + 1;
1182 end if;
1183 end if;
1185 Y := Adj_Year;
1186 Mo := Adj_Month;
1187 D := Adj_Day;
1188 H := 0;
1189 Mi := 0;
1190 Se := 0;
1191 Su := 0.0;
1192 end;
1194 -- Normal case (not exactly one day)
1196 else
1197 -- Sub second extraction
1199 if Day_Secs > 0.0 then
1200 Int_Day_Secs := Integer (Day_Secs - 0.5);
1201 else
1202 Int_Day_Secs := Integer (Day_Secs);
1203 end if;
1205 H := Int_Day_Secs / 3_600;
1206 Mi := (Int_Day_Secs / 60) mod 60;
1207 Se := Int_Day_Secs mod 60;
1208 Su := Day_Secs - Duration (Int_Day_Secs);
1209 end if;
1210 end if;
1212 -- Step 2: System call to VMS
1214 Timbuf (1) := Unsigned_Word (Y);
1215 Timbuf (2) := Unsigned_Word (Mo);
1216 Timbuf (3) := Unsigned_Word (D);
1217 Timbuf (4) := Unsigned_Word (H);
1218 Timbuf (5) := Unsigned_Word (Mi);
1219 Timbuf (6) := Unsigned_Word (Se);
1220 Timbuf (7) := 0;
1222 Cvt_Vectim (Status, Timbuf, Res);
1224 if Status mod 2 /= 1 then
1225 raise Time_Error;
1226 end if;
1228 -- Step 3: Sub second adjustment
1230 Res_M := OS_Time (Res) + OS_Time (Su * Mili_F);
1232 -- Step 4: Bounds check
1234 Check_Within_Time_Bounds (Res_M);
1236 -- Step 5: Time zone processing
1238 if Time_Zone /= 0 then
1239 Res_M := Res_M - OS_Time (Time_Zone) * 60 * Mili;
1240 end if;
1242 -- Step 6: Leap seconds processing
1244 if Leap_Support then
1245 Cumulative_Leap_Seconds
1246 (Start_Of_Time, Res_M, Elapsed_Leaps, Next_Leap_M);
1248 Res_M := Res_M + OS_Time (Elapsed_Leaps) * Mili;
1250 -- An Ada 2005 caller requesting an explicit leap second or an
1251 -- Ada 95 caller accounting for an invisible leap second.
1253 if Leap_Sec
1254 or else Res_M >= Next_Leap_M
1255 then
1256 Res_M := Res_M + OS_Time (1) * Mili;
1257 end if;
1259 -- Leap second validity check
1261 Rounded_Res_M := Res_M - (Res_M mod Mili);
1263 if Is_Ada_05
1264 and then Leap_Sec
1265 and then Rounded_Res_M /= Next_Leap_M
1266 then
1267 raise Time_Error;
1268 end if;
1269 end if;
1271 return Time (Res_M);
1272 end Time_Of;
1273 end Formatting_Operations;
1275 ---------------------------
1276 -- Time_Zones_Operations --
1277 ---------------------------
1279 package body Time_Zones_Operations is
1281 ---------------------
1282 -- UTC_Time_Offset --
1283 ---------------------
1285 function UTC_Time_Offset (Date : Time) return Long_Integer is
1286 -- Formal parameter Date is here for interfacing, but is never
1287 -- actually used.
1289 pragma Unreferenced (Date);
1291 function get_gmtoff return Long_Integer;
1292 pragma Import (C, get_gmtoff, "get_gmtoff");
1294 begin
1295 -- VMS is not capable of determining the time zone in some past or
1296 -- future point in time denoted by Date, thus the current time zone
1297 -- is retrieved.
1299 return get_gmtoff;
1300 end UTC_Time_Offset;
1301 end Time_Zones_Operations;
1302 end Ada.Calendar;