2010-07-22 Andi Kleen <ak@linux.intel.com>
[official-gcc.git] / gcc / ada / a-calend-vms.adb
blob788ff28a4d0145fb80b82dfda5fa4816963854db
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-2009, 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 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 ------------------------------------------------------------------------------
32 -- This is the Alpha/VMS version
34 with Ada.Unchecked_Conversion;
36 with System.Aux_DEC; use System.Aux_DEC;
37 with System.OS_Primitives; use System.OS_Primitives;
39 package body Ada.Calendar is
41 --------------------------
42 -- Implementation Notes --
43 --------------------------
45 -- Variables of type Ada.Calendar.Time have suffix _S or _M to denote
46 -- units of seconds or milis.
48 -- Because time is measured in different units and from different origins
49 -- on various targets, a system independent model is incorporated into
50 -- Ada.Calendar. The idea behind the design is to encapsulate all target
51 -- dependent machinery in a single package, thus providing a uniform
52 -- interface to all existing and any potential children.
54 -- package Ada.Calendar
55 -- procedure Split (5 parameters) -------+
56 -- | Call from local routine
57 -- private |
58 -- package Formatting_Operations |
59 -- procedure Split (11 parameters) <--+
60 -- end Formatting_Operations |
61 -- end Ada.Calendar |
62 -- |
63 -- package Ada.Calendar.Formatting | Call from child routine
64 -- procedure Split (9 or 10 parameters) -+
65 -- end Ada.Calendar.Formatting
67 -- The behaviour of the interfacing routines is controlled via various
68 -- flags. All new Ada 2005 types from children of Ada.Calendar are
69 -- emulated by a similar type. For instance, type Day_Number is replaced
70 -- by Integer in various routines. One ramification of this model is that
71 -- the caller site must perform validity checks on returned results.
72 -- The end result of this model is the lack of target specific files per
73 -- child of Ada.Calendar (a-calfor, a-calfor-vms, a-calfor-vxwors, etc).
75 -----------------------
76 -- Local Subprograms --
77 -----------------------
79 procedure Check_Within_Time_Bounds (T : OS_Time);
80 -- Ensure that a time representation value falls withing the bounds of Ada
81 -- time. Leap seconds support is taken into account.
83 procedure Cumulative_Leap_Seconds
84 (Start_Date : OS_Time;
85 End_Date : OS_Time;
86 Elapsed_Leaps : out Natural;
87 Next_Leap_Sec : out OS_Time);
88 -- Elapsed_Leaps is the sum of the leap seconds that have occurred on or
89 -- after Start_Date and before (strictly before) End_Date. Next_Leap_Sec
90 -- represents the next leap second occurrence on or after End_Date. If
91 -- there are no leaps seconds after End_Date, End_Of_Time is returned.
92 -- End_Of_Time can be used as End_Date to count all the leap seconds that
93 -- have occurred on or after Start_Date.
95 -- Note: Any sub seconds of Start_Date and End_Date are discarded before
96 -- the calculations are done. For instance: if 113 seconds is a leap
97 -- second (it isn't) and 113.5 is input as an End_Date, the leap second
98 -- at 113 will not be counted in Leaps_Between, but it will be returned
99 -- as Next_Leap_Sec. Thus, if the caller wants to know if the End_Date is
100 -- a leap second, the comparison should be:
102 -- End_Date >= Next_Leap_Sec;
104 -- After_Last_Leap is designed so that this comparison works without
105 -- having to first check if Next_Leap_Sec is a valid leap second.
107 function To_Duration (T : Time) return Duration;
108 function To_Relative_Time (D : Duration) return Time;
109 -- It is important to note that duration's fractional part denotes nano
110 -- seconds while the units of Time are 100 nanoseconds. If a regular
111 -- Unchecked_Conversion was employed, the resulting values would be off
112 -- by 100.
114 --------------------------
115 -- Leap seconds control --
116 --------------------------
118 Flag : Integer;
119 pragma Import (C, Flag, "__gl_leap_seconds_support");
120 -- This imported value is used to determine whether the compilation had
121 -- binder flag "-y" present which enables leap seconds. A value of zero
122 -- signifies no leap seconds support while a value of one enables the
123 -- support.
125 Leap_Support : constant Boolean := Flag = 1;
126 -- The above flag controls the usage of leap seconds in all Ada.Calendar
127 -- routines.
129 Leap_Seconds_Count : constant Natural := 24;
131 ---------------------
132 -- Local Constants --
133 ---------------------
135 -- The range of Ada time expressed as milis since the VMS Epoch
137 Ada_Low : constant OS_Time := (10 * 366 + 32 * 365 + 45) * Milis_In_Day;
138 Ada_High : constant OS_Time := (131 * 366 + 410 * 365 + 45) * Milis_In_Day;
140 -- Even though the upper bound of time is 2399-12-31 23:59:59.9999999
141 -- UTC, it must be increased to include all leap seconds.
143 Ada_High_And_Leaps : constant OS_Time :=
144 Ada_High + OS_Time (Leap_Seconds_Count) * Mili;
146 -- Two constants used in the calculations of elapsed leap seconds.
147 -- End_Of_Time is later than Ada_High in time zone -28. Start_Of_Time
148 -- is earlier than Ada_Low in time zone +28.
150 End_Of_Time : constant OS_Time := Ada_High + OS_Time (3) * Milis_In_Day;
151 Start_Of_Time : constant OS_Time := Ada_Low - OS_Time (3) * Milis_In_Day;
153 -- The following table contains the hard time values of all existing leap
154 -- seconds. The values are produced by the utility program xleaps.adb.
156 Leap_Second_Times : constant array (1 .. Leap_Seconds_Count) of OS_Time :=
157 (35855136000000000,
158 36014112010000000,
159 36329472020000000,
160 36644832030000000,
161 36960192040000000,
162 37276416050000000,
163 37591776060000000,
164 37907136070000000,
165 38222496080000000,
166 38695104090000000,
167 39010464100000000,
168 39325824110000000,
169 39957408120000000,
170 40747104130000000,
171 41378688140000000,
172 41694048150000000,
173 42166656160000000,
174 42482016170000000,
175 42797376180000000,
176 43271712190000000,
177 43744320200000000,
178 44218656210000000,
179 46427904220000000,
180 47374848230000000);
182 ---------
183 -- "+" --
184 ---------
186 function "+" (Left : Time; Right : Duration) return Time is
187 pragma Unsuppress (Overflow_Check);
188 begin
189 return Left + To_Relative_Time (Right);
190 exception
191 when Constraint_Error =>
192 raise Time_Error;
193 end "+";
195 function "+" (Left : Duration; Right : Time) return Time is
196 pragma Unsuppress (Overflow_Check);
197 begin
198 return Right + Left;
199 exception
200 when Constraint_Error =>
201 raise Time_Error;
202 end "+";
204 ---------
205 -- "-" --
206 ---------
208 function "-" (Left : Time; Right : Duration) return Time is
209 pragma Unsuppress (Overflow_Check);
210 begin
211 return Left - To_Relative_Time (Right);
212 exception
213 when Constraint_Error =>
214 raise Time_Error;
215 end "-";
217 function "-" (Left : Time; Right : Time) return Duration is
218 pragma Unsuppress (Overflow_Check);
220 -- The bound of type Duration expressed as time
222 Dur_High : constant OS_Time :=
223 OS_Time (To_Relative_Time (Duration'Last));
224 Dur_Low : constant OS_Time :=
225 OS_Time (To_Relative_Time (Duration'First));
227 Res_M : OS_Time;
229 begin
230 Res_M := OS_Time (Left) - OS_Time (Right);
232 -- Due to the extended range of Ada time, "-" is capable of producing
233 -- results which may exceed the range of Duration. In order to prevent
234 -- the generation of bogus values by the Unchecked_Conversion, we apply
235 -- the following check.
237 if Res_M < Dur_Low
238 or else Res_M >= Dur_High
239 then
240 raise Time_Error;
242 -- Normal case, result fits
244 else
245 return To_Duration (Time (Res_M));
246 end if;
248 exception
249 when Constraint_Error =>
250 raise Time_Error;
251 end "-";
253 ---------
254 -- "<" --
255 ---------
257 function "<" (Left, Right : Time) return Boolean is
258 begin
259 return OS_Time (Left) < OS_Time (Right);
260 end "<";
262 ----------
263 -- "<=" --
264 ----------
266 function "<=" (Left, Right : Time) return Boolean is
267 begin
268 return OS_Time (Left) <= OS_Time (Right);
269 end "<=";
271 ---------
272 -- ">" --
273 ---------
275 function ">" (Left, Right : Time) return Boolean is
276 begin
277 return OS_Time (Left) > OS_Time (Right);
278 end ">";
280 ----------
281 -- ">=" --
282 ----------
284 function ">=" (Left, Right : Time) return Boolean is
285 begin
286 return OS_Time (Left) >= OS_Time (Right);
287 end ">=";
289 ------------------------------
290 -- Check_Within_Time_Bounds --
291 ------------------------------
293 procedure Check_Within_Time_Bounds (T : OS_Time) is
294 begin
295 if Leap_Support then
296 if T < Ada_Low or else T > Ada_High_And_Leaps then
297 raise Time_Error;
298 end if;
299 else
300 if T < Ada_Low or else T > Ada_High then
301 raise Time_Error;
302 end if;
303 end if;
304 end Check_Within_Time_Bounds;
306 -----------
307 -- Clock --
308 -----------
310 function Clock return Time is
311 Elapsed_Leaps : Natural;
312 Next_Leap_M : OS_Time;
313 Res_M : constant OS_Time := OS_Clock;
315 begin
316 -- Note that on other targets a soft-link is used to get a different
317 -- clock depending whether tasking is used or not. On VMS this isn't
318 -- needed since all clock calls end up using SYS$GETTIM, so call the
319 -- OS_Primitives version for efficiency.
321 -- If the target supports leap seconds, determine the number of leap
322 -- seconds elapsed until this moment.
324 if Leap_Support then
325 Cumulative_Leap_Seconds
326 (Start_Of_Time, Res_M, Elapsed_Leaps, Next_Leap_M);
328 -- The system clock may fall exactly on a leap second
330 if Res_M >= Next_Leap_M then
331 Elapsed_Leaps := Elapsed_Leaps + 1;
332 end if;
334 -- The target does not support leap seconds
336 else
337 Elapsed_Leaps := 0;
338 end if;
340 return Time (Res_M + OS_Time (Elapsed_Leaps) * Mili);
341 end Clock;
343 -----------------------------
344 -- Cumulative_Leap_Seconds --
345 -----------------------------
347 procedure Cumulative_Leap_Seconds
348 (Start_Date : OS_Time;
349 End_Date : OS_Time;
350 Elapsed_Leaps : out Natural;
351 Next_Leap_Sec : out OS_Time)
353 End_Index : Positive;
354 End_T : OS_Time := End_Date;
355 Start_Index : Positive;
356 Start_T : OS_Time := Start_Date;
358 begin
359 pragma Assert (Leap_Support and then End_Date >= Start_Date);
361 Next_Leap_Sec := End_Of_Time;
363 -- Make sure that the end date does not exceed the upper bound
364 -- of Ada time.
366 if End_Date > Ada_High then
367 End_T := Ada_High;
368 end if;
370 -- Remove the sub seconds from both dates
372 Start_T := Start_T - (Start_T mod Mili);
373 End_T := End_T - (End_T mod Mili);
375 -- Some trivial cases:
376 -- Leap 1 . . . Leap N
377 -- ---+========+------+############+-------+========+-----
378 -- Start_T End_T Start_T End_T
380 if End_T < Leap_Second_Times (1) then
381 Elapsed_Leaps := 0;
382 Next_Leap_Sec := Leap_Second_Times (1);
383 return;
385 elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
386 Elapsed_Leaps := 0;
387 Next_Leap_Sec := End_Of_Time;
388 return;
389 end if;
391 -- Perform the calculations only if the start date is within the leap
392 -- second occurrences table.
394 if Start_T <= Leap_Second_Times (Leap_Seconds_Count) then
396 -- 1 2 N - 1 N
397 -- +----+----+-- . . . --+-------+---+
398 -- | T1 | T2 | | N - 1 | N |
399 -- +----+----+-- . . . --+-------+---+
400 -- ^ ^
401 -- | Start_Index | End_Index
402 -- +-------------------+
403 -- Leaps_Between
405 -- The idea behind the algorithm is to iterate and find two closest
406 -- dates which are after Start_T and End_T. Their corresponding
407 -- index difference denotes the number of leap seconds elapsed.
409 Start_Index := 1;
410 loop
411 exit when Leap_Second_Times (Start_Index) >= Start_T;
412 Start_Index := Start_Index + 1;
413 end loop;
415 End_Index := Start_Index;
416 loop
417 exit when End_Index > Leap_Seconds_Count
418 or else Leap_Second_Times (End_Index) >= End_T;
419 End_Index := End_Index + 1;
420 end loop;
422 if End_Index <= Leap_Seconds_Count then
423 Next_Leap_Sec := Leap_Second_Times (End_Index);
424 end if;
426 Elapsed_Leaps := End_Index - Start_Index;
428 else
429 Elapsed_Leaps := 0;
430 end if;
431 end Cumulative_Leap_Seconds;
433 ---------
434 -- Day --
435 ---------
437 function Day (Date : Time) return Day_Number is
438 Y : Year_Number;
439 M : Month_Number;
440 D : Day_Number;
441 S : Day_Duration;
442 pragma Unreferenced (Y, M, S);
443 begin
444 Split (Date, Y, M, D, S);
445 return D;
446 end Day;
448 -------------
449 -- Is_Leap --
450 -------------
452 function Is_Leap (Year : Year_Number) return Boolean is
453 begin
454 -- Leap centennial years
456 if Year mod 400 = 0 then
457 return True;
459 -- Non-leap centennial years
461 elsif Year mod 100 = 0 then
462 return False;
464 -- Regular years
466 else
467 return Year mod 4 = 0;
468 end if;
469 end Is_Leap;
471 -----------
472 -- Month --
473 -----------
475 function Month (Date : Time) return Month_Number is
476 Y : Year_Number;
477 M : Month_Number;
478 D : Day_Number;
479 S : Day_Duration;
480 pragma Unreferenced (Y, D, S);
481 begin
482 Split (Date, Y, M, D, S);
483 return M;
484 end Month;
486 -------------
487 -- Seconds --
488 -------------
490 function Seconds (Date : Time) return Day_Duration is
491 Y : Year_Number;
492 M : Month_Number;
493 D : Day_Number;
494 S : Day_Duration;
495 pragma Unreferenced (Y, M, D);
496 begin
497 Split (Date, Y, M, D, S);
498 return S;
499 end Seconds;
501 -----------
502 -- Split --
503 -----------
505 procedure Split
506 (Date : Time;
507 Year : out Year_Number;
508 Month : out Month_Number;
509 Day : out Day_Number;
510 Seconds : out Day_Duration)
512 H : Integer;
513 M : Integer;
514 Se : Integer;
515 Ss : Duration;
516 Le : Boolean;
518 begin
519 -- Use UTC as the local time zone on VMS, the status of flag Is_Ada_05
520 -- is irrelevant in this case.
522 Formatting_Operations.Split
523 (Date => Date,
524 Year => Year,
525 Month => Month,
526 Day => Day,
527 Day_Secs => Seconds,
528 Hour => H,
529 Minute => M,
530 Second => Se,
531 Sub_Sec => Ss,
532 Leap_Sec => Le,
533 Is_Ada_05 => False,
534 Time_Zone => 0);
536 -- Validity checks
538 if not Year'Valid
539 or else not Month'Valid
540 or else not Day'Valid
541 or else not Seconds'Valid
542 then
543 raise Time_Error;
544 end if;
545 end Split;
547 -------------
548 -- Time_Of --
549 -------------
551 function Time_Of
552 (Year : Year_Number;
553 Month : Month_Number;
554 Day : Day_Number;
555 Seconds : Day_Duration := 0.0) return Time
557 -- The values in the following constants are irrelevant, they are just
558 -- placeholders; the choice of constructing a Day_Duration value is
559 -- controlled by the Use_Day_Secs flag.
561 H : constant Integer := 1;
562 M : constant Integer := 1;
563 Se : constant Integer := 1;
564 Ss : constant Duration := 0.1;
566 begin
567 if not Year'Valid
568 or else not Month'Valid
569 or else not Day'Valid
570 or else not Seconds'Valid
571 then
572 raise Time_Error;
573 end if;
575 -- Use UTC as the local time zone on VMS, the status of flag Is_Ada_05
576 -- is irrelevant in this case.
578 return
579 Formatting_Operations.Time_Of
580 (Year => Year,
581 Month => Month,
582 Day => Day,
583 Day_Secs => Seconds,
584 Hour => H,
585 Minute => M,
586 Second => Se,
587 Sub_Sec => Ss,
588 Leap_Sec => False,
589 Use_Day_Secs => True,
590 Is_Ada_05 => False,
591 Time_Zone => 0);
592 end Time_Of;
594 -----------------
595 -- To_Duration --
596 -----------------
598 function To_Duration (T : Time) return Duration is
599 function Time_To_Duration is
600 new Ada.Unchecked_Conversion (Time, Duration);
601 begin
602 return Time_To_Duration (T * 100);
603 end To_Duration;
605 ----------------------
606 -- To_Relative_Time --
607 ----------------------
609 function To_Relative_Time (D : Duration) return Time is
610 function Duration_To_Time is
611 new Ada.Unchecked_Conversion (Duration, Time);
612 begin
613 return Duration_To_Time (D / 100.0);
614 end To_Relative_Time;
616 ----------
617 -- Year --
618 ----------
620 function Year (Date : Time) return Year_Number is
621 Y : Year_Number;
622 M : Month_Number;
623 D : Day_Number;
624 S : Day_Duration;
625 pragma Unreferenced (M, D, S);
626 begin
627 Split (Date, Y, M, D, S);
628 return Y;
629 end Year;
631 -- The following packages assume that Time is a Long_Integer, the units
632 -- are 100 nanoseconds and the starting point in the VMS Epoch.
634 ---------------------------
635 -- Arithmetic_Operations --
636 ---------------------------
638 package body Arithmetic_Operations is
640 ---------
641 -- Add --
642 ---------
644 function Add (Date : Time; Days : Long_Integer) return Time is
645 pragma Unsuppress (Overflow_Check);
646 Date_M : constant OS_Time := OS_Time (Date);
647 begin
648 return Time (Date_M + OS_Time (Days) * Milis_In_Day);
649 exception
650 when Constraint_Error =>
651 raise Time_Error;
652 end Add;
654 ----------------
655 -- Difference --
656 ----------------
658 procedure Difference
659 (Left : Time;
660 Right : Time;
661 Days : out Long_Integer;
662 Seconds : out Duration;
663 Leap_Seconds : out Integer)
665 Diff_M : OS_Time;
666 Diff_S : OS_Time;
667 Earlier : OS_Time;
668 Elapsed_Leaps : Natural;
669 Later : OS_Time;
670 Negate : Boolean := False;
671 Next_Leap : OS_Time;
672 Sub_Seconds : Duration;
674 begin
675 -- This classification is necessary in order to avoid a Time_Error
676 -- being raised by the arithmetic operators in Ada.Calendar.
678 if Left >= Right then
679 Later := OS_Time (Left);
680 Earlier := OS_Time (Right);
681 else
682 Later := OS_Time (Right);
683 Earlier := OS_Time (Left);
684 Negate := True;
685 end if;
687 -- If the target supports leap seconds, process them
689 if Leap_Support then
690 Cumulative_Leap_Seconds
691 (Earlier, Later, Elapsed_Leaps, Next_Leap);
693 if Later >= Next_Leap then
694 Elapsed_Leaps := Elapsed_Leaps + 1;
695 end if;
697 -- The target does not support leap seconds
699 else
700 Elapsed_Leaps := 0;
701 end if;
703 Diff_M := Later - Earlier - OS_Time (Elapsed_Leaps) * Mili;
705 -- Sub second processing
707 Sub_Seconds := Duration (Diff_M mod Mili) / Mili_F;
709 -- Convert to seconds. Note that his action eliminates the sub
710 -- seconds automatically.
712 Diff_S := Diff_M / Mili;
714 Days := Long_Integer (Diff_S / Secs_In_Day);
715 Seconds := Duration (Diff_S mod Secs_In_Day) + Sub_Seconds;
716 Leap_Seconds := Integer (Elapsed_Leaps);
718 if Negate then
719 Days := -Days;
720 Seconds := -Seconds;
722 if Leap_Seconds /= 0 then
723 Leap_Seconds := -Leap_Seconds;
724 end if;
725 end if;
726 end Difference;
728 --------------
729 -- Subtract --
730 --------------
732 function Subtract (Date : Time; Days : Long_Integer) return Time is
733 pragma Unsuppress (Overflow_Check);
734 Date_M : constant OS_Time := OS_Time (Date);
735 begin
736 return Time (Date_M - OS_Time (Days) * Milis_In_Day);
737 exception
738 when Constraint_Error =>
739 raise Time_Error;
740 end Subtract;
741 end Arithmetic_Operations;
743 ---------------------------
744 -- Conversion_Operations --
745 ---------------------------
747 package body Conversion_Operations is
749 Epoch_Offset : constant OS_Time := 35067168000000000;
750 -- The difference between 1970-1-1 UTC and 1858-11-17 UTC expressed in
751 -- 100 nanoseconds.
753 -----------------
754 -- To_Ada_Time --
755 -----------------
757 function To_Ada_Time (Unix_Time : Long_Integer) return Time is
758 pragma Unsuppress (Overflow_Check);
759 Unix_Rep : constant OS_Time := OS_Time (Unix_Time) * Mili;
760 begin
761 return Time (Unix_Rep + Epoch_Offset);
762 exception
763 when Constraint_Error =>
764 raise Time_Error;
765 end To_Ada_Time;
767 -----------------
768 -- To_Ada_Time --
769 -----------------
771 function To_Ada_Time
772 (tm_year : Integer;
773 tm_mon : Integer;
774 tm_day : Integer;
775 tm_hour : Integer;
776 tm_min : Integer;
777 tm_sec : Integer;
778 tm_isdst : Integer) return Time
780 pragma Unsuppress (Overflow_Check);
782 Year_Shift : constant Integer := 1900;
783 Month_Shift : constant Integer := 1;
785 Year : Year_Number;
786 Month : Month_Number;
787 Day : Day_Number;
788 Second : Integer;
789 Leap : Boolean;
790 Result : OS_Time;
792 begin
793 -- Input processing
795 Year := Year_Number (Year_Shift + tm_year);
796 Month := Month_Number (Month_Shift + tm_mon);
797 Day := Day_Number (tm_day);
799 -- Step 1: Validity checks of input values
801 if not Year'Valid
802 or else not Month'Valid
803 or else not Day'Valid
804 or else tm_hour not in 0 .. 24
805 or else tm_min not in 0 .. 59
806 or else tm_sec not in 0 .. 60
807 or else tm_isdst not in -1 .. 1
808 then
809 raise Time_Error;
810 end if;
812 -- Step 2: Potential leap second
814 if tm_sec = 60 then
815 Leap := True;
816 Second := 59;
817 else
818 Leap := False;
819 Second := tm_sec;
820 end if;
822 -- Step 3: Calculate the time value
824 Result :=
825 OS_Time
826 (Formatting_Operations.Time_Of
827 (Year => Year,
828 Month => Month,
829 Day => Day,
830 Day_Secs => 0.0, -- Time is given in h:m:s
831 Hour => tm_hour,
832 Minute => tm_min,
833 Second => Second,
834 Sub_Sec => 0.0, -- No precise sub second given
835 Leap_Sec => Leap,
836 Use_Day_Secs => False, -- Time is given in h:m:s
837 Is_Ada_05 => True, -- Force usage of explicit time zone
838 Time_Zone => 0)); -- Place the value in UTC
839 -- Step 4: Daylight Savings Time
841 if tm_isdst = 1 then
842 Result := Result + OS_Time (3_600) * Mili;
843 end if;
845 return Time (Result);
846 exception
847 when Constraint_Error =>
848 raise Time_Error;
849 end To_Ada_Time;
851 -----------------
852 -- To_Duration --
853 -----------------
855 function To_Duration
856 (tv_sec : Long_Integer;
857 tv_nsec : Long_Integer) return Duration
859 pragma Unsuppress (Overflow_Check);
860 begin
861 return Duration (tv_sec) + Duration (tv_nsec) / Mili_F;
862 end To_Duration;
864 ------------------------
865 -- To_Struct_Timespec --
866 ------------------------
868 procedure To_Struct_Timespec
869 (D : Duration;
870 tv_sec : out Long_Integer;
871 tv_nsec : out Long_Integer)
873 pragma Unsuppress (Overflow_Check);
874 Secs : Duration;
875 Nano_Secs : Duration;
877 begin
878 -- Seconds extraction, avoid potential rounding errors
880 Secs := D - 0.5;
881 tv_sec := Long_Integer (Secs);
883 -- 100 Nanoseconds extraction
885 Nano_Secs := D - Duration (tv_sec);
886 tv_nsec := Long_Integer (Nano_Secs * Mili);
887 end To_Struct_Timespec;
889 ------------------
890 -- To_Struct_Tm --
891 ------------------
893 procedure To_Struct_Tm
894 (T : Time;
895 tm_year : out Integer;
896 tm_mon : out Integer;
897 tm_day : out Integer;
898 tm_hour : out Integer;
899 tm_min : out Integer;
900 tm_sec : out Integer)
902 pragma Unsuppress (Overflow_Check);
903 Year : Year_Number;
904 Month : Month_Number;
905 Second : Integer;
906 Day_Secs : Day_Duration;
907 Sub_Sec : Duration;
908 Leap_Sec : Boolean;
910 begin
911 -- Step 1: Split the input time
913 Formatting_Operations.Split
914 (T, Year, Month, tm_day, Day_Secs,
915 tm_hour, tm_min, Second, Sub_Sec, Leap_Sec, True, 0);
917 -- Step 2: Correct the year and month
919 tm_year := Year - 1900;
920 tm_mon := Month - 1;
922 -- Step 3: Handle leap second occurrences
924 tm_sec := (if Leap_Sec then 60 else Second);
925 end To_Struct_Tm;
927 ------------------
928 -- To_Unix_Time --
929 ------------------
931 function To_Unix_Time (Ada_Time : Time) return Long_Integer is
932 pragma Unsuppress (Overflow_Check);
933 Ada_OS_Time : constant OS_Time := OS_Time (Ada_Time);
934 begin
935 return Long_Integer ((Ada_OS_Time - Epoch_Offset) / Mili);
936 exception
937 when Constraint_Error =>
938 raise Time_Error;
939 end To_Unix_Time;
940 end Conversion_Operations;
942 ---------------------------
943 -- Formatting_Operations --
944 ---------------------------
946 package body Formatting_Operations is
948 -----------------
949 -- Day_Of_Week --
950 -----------------
952 function Day_Of_Week (Date : Time) return Integer is
953 Y : Year_Number;
954 M : Month_Number;
955 D : Day_Number;
956 S : Day_Duration;
958 Day_Count : Long_Integer;
959 Midday_Date_S : Time;
961 begin
962 Split (Date, Y, M, D, S);
964 -- Build a time value in the middle of the same day and convert the
965 -- time value to seconds.
967 Midday_Date_S := Time_Of (Y, M, D, 43_200.0) / Mili;
969 -- Count the number of days since the start of VMS time. 1858-11-17
970 -- was a Wednesday.
972 Day_Count := Long_Integer (Midday_Date_S / Secs_In_Day) + 2;
974 return Integer (Day_Count mod 7);
975 end Day_Of_Week;
977 -----------
978 -- Split --
979 -----------
981 procedure Split
982 (Date : Time;
983 Year : out Year_Number;
984 Month : out Month_Number;
985 Day : out Day_Number;
986 Day_Secs : out Day_Duration;
987 Hour : out Integer;
988 Minute : out Integer;
989 Second : out Integer;
990 Sub_Sec : out Duration;
991 Leap_Sec : out Boolean;
992 Is_Ada_05 : Boolean;
993 Time_Zone : Long_Integer)
995 -- The flag Is_Ada_05 is present for interfacing purposes
997 pragma Unreferenced (Is_Ada_05);
999 procedure Numtim
1000 (Status : out Unsigned_Longword;
1001 Timbuf : out Unsigned_Word_Array;
1002 Timadr : Time);
1004 pragma Interface (External, Numtim);
1006 pragma Import_Valued_Procedure
1007 (Numtim, "SYS$NUMTIM",
1008 (Unsigned_Longword, Unsigned_Word_Array, Time),
1009 (Value, Reference, Reference));
1011 Status : Unsigned_Longword;
1012 Timbuf : Unsigned_Word_Array (1 .. 7);
1014 Ada_Min_Year : constant := 1901;
1015 Ada_Max_Year : constant := 2399;
1017 Date_M : OS_Time;
1018 Elapsed_Leaps : Natural;
1019 Next_Leap_M : OS_Time;
1021 begin
1022 Date_M := OS_Time (Date);
1024 -- Step 1: Leap seconds processing
1026 if Leap_Support then
1027 Cumulative_Leap_Seconds
1028 (Start_Of_Time, Date_M, Elapsed_Leaps, Next_Leap_M);
1030 Leap_Sec := Date_M >= Next_Leap_M;
1032 if Leap_Sec then
1033 Elapsed_Leaps := Elapsed_Leaps + 1;
1034 end if;
1036 -- The target does not support leap seconds
1038 else
1039 Elapsed_Leaps := 0;
1040 Leap_Sec := False;
1041 end if;
1043 Date_M := Date_M - OS_Time (Elapsed_Leaps) * Mili;
1045 -- Step 2: Time zone processing
1047 if Time_Zone /= 0 then
1048 Date_M := Date_M + OS_Time (Time_Zone) * 60 * Mili;
1049 end if;
1051 -- After the leap seconds and time zone have been accounted for,
1052 -- the date should be within the bounds of Ada time.
1054 if Date_M < Ada_Low
1055 or else Date_M > Ada_High
1056 then
1057 raise Time_Error;
1058 end if;
1060 -- Step 3: Sub second processing
1062 Sub_Sec := Duration (Date_M mod Mili) / Mili_F;
1064 -- Drop the sub seconds
1066 Date_M := Date_M - (Date_M mod Mili);
1068 -- Step 4: VMS system call
1070 Numtim (Status, Timbuf, Time (Date_M));
1072 if Status mod 2 /= 1
1073 or else Timbuf (1) not in Ada_Min_Year .. Ada_Max_Year
1074 then
1075 raise Time_Error;
1076 end if;
1078 -- Step 5: Time components processing
1080 Year := Year_Number (Timbuf (1));
1081 Month := Month_Number (Timbuf (2));
1082 Day := Day_Number (Timbuf (3));
1083 Hour := Integer (Timbuf (4));
1084 Minute := Integer (Timbuf (5));
1085 Second := Integer (Timbuf (6));
1087 Day_Secs := Day_Duration (Hour * 3_600) +
1088 Day_Duration (Minute * 60) +
1089 Day_Duration (Second) +
1090 Sub_Sec;
1091 end Split;
1093 -------------
1094 -- Time_Of --
1095 -------------
1097 function Time_Of
1098 (Year : Year_Number;
1099 Month : Month_Number;
1100 Day : Day_Number;
1101 Day_Secs : Day_Duration;
1102 Hour : Integer;
1103 Minute : Integer;
1104 Second : Integer;
1105 Sub_Sec : Duration;
1106 Leap_Sec : Boolean := False;
1107 Use_Day_Secs : Boolean := False;
1108 Is_Ada_05 : Boolean := False;
1109 Time_Zone : Long_Integer := 0) return Time
1111 procedure Cvt_Vectim
1112 (Status : out Unsigned_Longword;
1113 Input_Time : Unsigned_Word_Array;
1114 Resultant_Time : out Time);
1116 pragma Interface (External, Cvt_Vectim);
1118 pragma Import_Valued_Procedure
1119 (Cvt_Vectim, "LIB$CVT_VECTIM",
1120 (Unsigned_Longword, Unsigned_Word_Array, Time),
1121 (Value, Reference, Reference));
1123 Status : Unsigned_Longword;
1124 Timbuf : Unsigned_Word_Array (1 .. 7);
1126 Y : Year_Number := Year;
1127 Mo : Month_Number := Month;
1128 D : Day_Number := Day;
1129 H : Integer := Hour;
1130 Mi : Integer := Minute;
1131 Se : Integer := Second;
1132 Su : Duration := Sub_Sec;
1134 Elapsed_Leaps : Natural;
1135 Int_Day_Secs : Integer;
1136 Next_Leap_M : OS_Time;
1137 Res : Time;
1138 Res_M : OS_Time;
1139 Rounded_Res_M : OS_Time;
1141 begin
1142 -- No validity checks are performed on the input values since it is
1143 -- assumed that the called has already performed them.
1145 -- Step 1: Hour, minute, second and sub second processing
1147 if Use_Day_Secs then
1149 -- A day seconds value of 86_400 designates a new day
1151 if Day_Secs = 86_400.0 then
1152 declare
1153 Adj_Year : Year_Number := Year;
1154 Adj_Month : Month_Number := Month;
1155 Adj_Day : Day_Number := Day;
1157 begin
1158 if Day < Days_In_Month (Month)
1159 or else (Month = 2
1160 and then Is_Leap (Year))
1161 then
1162 Adj_Day := Day + 1;
1164 -- The day adjustment moves the date to a new month
1166 else
1167 Adj_Day := 1;
1169 if Month < 12 then
1170 Adj_Month := Month + 1;
1172 -- The month adjustment moves the date to a new year
1174 else
1175 Adj_Month := 1;
1176 Adj_Year := Year + 1;
1177 end if;
1178 end if;
1180 Y := Adj_Year;
1181 Mo := Adj_Month;
1182 D := Adj_Day;
1183 H := 0;
1184 Mi := 0;
1185 Se := 0;
1186 Su := 0.0;
1187 end;
1189 -- Normal case (not exactly one day)
1191 else
1192 -- Sub second extraction
1194 Int_Day_Secs :=
1195 (if Day_Secs > 0.0
1196 then Integer (Day_Secs - 0.5)
1197 else Integer (Day_Secs));
1199 H := Int_Day_Secs / 3_600;
1200 Mi := (Int_Day_Secs / 60) mod 60;
1201 Se := Int_Day_Secs mod 60;
1202 Su := Day_Secs - Duration (Int_Day_Secs);
1203 end if;
1204 end if;
1206 -- Step 2: System call to VMS
1208 Timbuf (1) := Unsigned_Word (Y);
1209 Timbuf (2) := Unsigned_Word (Mo);
1210 Timbuf (3) := Unsigned_Word (D);
1211 Timbuf (4) := Unsigned_Word (H);
1212 Timbuf (5) := Unsigned_Word (Mi);
1213 Timbuf (6) := Unsigned_Word (Se);
1214 Timbuf (7) := 0;
1216 Cvt_Vectim (Status, Timbuf, Res);
1218 if Status mod 2 /= 1 then
1219 raise Time_Error;
1220 end if;
1222 -- Step 3: Sub second adjustment
1224 Res_M := OS_Time (Res) + OS_Time (Su * Mili_F);
1226 -- Step 4: Bounds check
1228 Check_Within_Time_Bounds (Res_M);
1230 -- Step 5: Time zone processing
1232 if Time_Zone /= 0 then
1233 Res_M := Res_M - OS_Time (Time_Zone) * 60 * Mili;
1234 end if;
1236 -- Step 6: Leap seconds processing
1238 if Leap_Support then
1239 Cumulative_Leap_Seconds
1240 (Start_Of_Time, Res_M, Elapsed_Leaps, Next_Leap_M);
1242 Res_M := Res_M + OS_Time (Elapsed_Leaps) * Mili;
1244 -- An Ada 2005 caller requesting an explicit leap second or an
1245 -- Ada 95 caller accounting for an invisible leap second.
1247 if Leap_Sec
1248 or else Res_M >= Next_Leap_M
1249 then
1250 Res_M := Res_M + OS_Time (1) * Mili;
1251 end if;
1253 -- Leap second validity check
1255 Rounded_Res_M := Res_M - (Res_M mod Mili);
1257 if Is_Ada_05
1258 and then Leap_Sec
1259 and then Rounded_Res_M /= Next_Leap_M
1260 then
1261 raise Time_Error;
1262 end if;
1263 end if;
1265 return Time (Res_M);
1266 end Time_Of;
1267 end Formatting_Operations;
1269 ---------------------------
1270 -- Time_Zones_Operations --
1271 ---------------------------
1273 package body Time_Zones_Operations is
1275 ---------------------
1276 -- UTC_Time_Offset --
1277 ---------------------
1279 function UTC_Time_Offset (Date : Time) return Long_Integer is
1280 -- Formal parameter Date is here for interfacing, but is never
1281 -- actually used.
1283 pragma Unreferenced (Date);
1285 function get_gmtoff return Long_Integer;
1286 pragma Import (C, get_gmtoff, "get_gmtoff");
1288 begin
1289 -- VMS is not capable of determining the time zone in some past or
1290 -- future point in time denoted by Date, thus the current time zone
1291 -- is retrieved.
1293 return get_gmtoff;
1294 end UTC_Time_Offset;
1295 end Time_Zones_Operations;
1296 end Ada.Calendar;