File : xleaps.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                          GNAT SYSTEM UTILITIES                           --
   4 --                                                                          --
   5 --                               X L E A P S                                --
   6 --                                                                          --
   7 --                                 B o d y                                  --
   8 --                                                                          --
   9 --          Copyright (C) 1992-2015, 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 -- GNAT was originally developed  by the GNAT team at  New York University. --
  19 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
  20 --                                                                          --
  21 ------------------------------------------------------------------------------
  22 
  23 --  This utility is used to generate the hard time values of all existing
  24 --  leap second occurrences. It uses several target dependent values taken
  25 --  from a-calend.ads to perform its computations.
  26 
  27 --  The algorithm behind this utility calculates the total number of days
  28 --  up to a leap second occurrence from a target dependent origin of time.
  29 --  The program then converts the resulting days into target dependent
  30 --  time units used to create the aggregate.
  31 
  32 --    Usage:
  33 
  34 --      xleaps
  35 
  36 --    Output files:
  37 
  38 --      xleaps.txt
  39 
  40 --  When a new leap second is introduced, the following steps must be carried
  41 --  out:
  42 
  43 --     1) Increment Leap_Seconds_Count in a-calend.adb
  44 --     2) Increment LS_Count in xleaps.adb by one
  45 --     3) Add the new date to the aggregate of array LS_Dates in xleaps.adb
  46 --     4) Compile and execute xleaps for common targets
  47 --     5) Replace the values of array Leap_Second_Times in a-calend.adb with
  48 --        the aggregate generated by xleaps
  49 --     6) Update the following tests under 0005-144:
  50 --          cy90006.adb
  51 --          cy90006_ls.adb
  52 --          cy90010.adb
  53 --          cy90010_ls.adb
  54 --          c790016.adb
  55 --          cy90016_ls.adb
  56 
  57 with Ada.Calendar;     use Ada.Calendar;
  58 with Ada.Command_Line; use Ada.Command_Line;
  59 with Ada.Text_IO;      use Ada.Text_IO;
  60 
  61 procedure XLeaps is
  62 
  63    --  A general type used for calculating the hard time values of a leap
  64    --  second occurrence. The type is explicitly declared to be large enough
  65    --  and system independent.
  66 
  67    type Units is range -2 ** 63 .. +2 ** 63 - 1;
  68 
  69    --  Target specific units (currently all targets use nano-seconds)
  70 
  71    Mili         : constant := 10_000_000;
  72    Milis_In_Day : constant := 864_000_000_000;
  73    Nano         : constant := 1_000_000_000;
  74    Nanos_In_Day : constant := 86_400_000_000_000;
  75 
  76    --  Origins of different time systems relative to a zero value. See
  77    --  a-calend for more details.
  78 
  79    Origin_Common : constant := -(61 * 366 + 188 * 365);
  80 
  81    --  Various constants
  82 
  83    Ada_Min_Year       : constant := 1901;
  84    Days_In_Four_Years : constant := 365 * 3 + 366;
  85    Secs_In_Day        : constant := 86_400;
  86 
  87    Cumulative_Days_Before_Month :
  88      constant array (Month_Number) of Units :=
  89        (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
  90 
  91    type LS_Date is record
  92       Year  : Year_Number;
  93       Month : Month_Number;
  94       Day   : Day_Number;
  95    end record;
  96 
  97    LS_Count : constant := 25;
  98    LS_Dates : constant array (1 .. LS_Count) of LS_Date :=
  99      ((1972,  6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31),
 100       (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31),
 101       (1979, 12, 31), (1981,  6, 30), (1982,  6, 30), (1983,  6, 30),
 102       (1985,  6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31),
 103       (1992,  6, 30), (1993,  6, 30), (1994,  6, 30), (1995, 12, 31),
 104       (1997,  6, 30), (1998, 12, 31), (2005, 12, 31), (2008, 12, 31),
 105       (2012,  6, 30));
 106 
 107    Days       : Units;
 108    Leap       : LS_Date;
 109    Origin     : Units;
 110    Result     : Units;
 111    Time_Units : Units;
 112    Years      : Natural;
 113 
 114    --  All hard time values will be written to this file
 115 
 116    F : File_Type;
 117 
 118    function Is_Leap (Year : Year_Number) return Boolean;
 119    --  Determine whether a given year is leap
 120 
 121    procedure Usage;
 122    --  Generate screen of usage information
 123 
 124    -------------
 125    -- Is_Leap --
 126    -------------
 127 
 128    function Is_Leap (Year : Year_Number) return Boolean is
 129    begin
 130       --  Leap centenial years
 131 
 132       if Year mod 400 = 0 then
 133          return True;
 134 
 135       --  Non-leap centenial years
 136 
 137       elsif Year mod 100 = 0 then
 138          return False;
 139 
 140       --  Regular years
 141 
 142       else
 143          return Year mod 4 = 0;
 144       end if;
 145    end Is_Leap;
 146 
 147    -----------
 148    -- Usage --
 149    -----------
 150 
 151    procedure Usage is
 152    begin
 153       Put_Line ("Usage : xleaps");
 154       Put_Line ("Output: xleaps.txt");
 155    end Usage;
 156 
 157 --  Start of processing for XLeaps
 158 
 159 begin
 160    --  Determine the target and set the appropriate time origin and units
 161 
 162    if Argument_Count = 0 then
 163       Origin := Origin_Common;
 164       Time_Units := Nano;
 165    else
 166       Usage;
 167    end if;
 168 
 169    Create (F, Out_File, "xleaps.txt");
 170 
 171    for Index in 1 .. LS_Count loop
 172       Leap := LS_Dates (Index);
 173 
 174       --  Add the segments of four years expressed as days until the current
 175       --  leap second occurrence. Note that non-leap centenial years are not
 176       --  accounted for since there are no leap seconds after 2100.
 177 
 178       Years := Leap.Year - Ada_Min_Year;
 179       Days  := Units (Years / 4) * Days_In_Four_Years;
 180       Years := Years mod 4;
 181 
 182       --  Add the remaining years as days
 183 
 184       if Years = 1 then
 185          Days := Days + 365;
 186 
 187       elsif Years = 2 then
 188          Days := Days + 365 * 2;
 189 
 190       elsif Years = 3 then
 191          Days := Days + 365 * 3;
 192       end if;
 193 
 194       --  Add all the days to the beginning of the current month. Handle
 195       --  February 29 during leap years.
 196 
 197       Days := Days + Cumulative_Days_Before_Month (Leap.Month);
 198 
 199       if Is_Leap (Leap.Year)
 200         and then Leap.Month > 2
 201       then
 202          Days := Days + 1;
 203       end if;
 204 
 205       --  Add all the days into the current month and shift the origin to the
 206       --  target time system.
 207 
 208       Days := Days + Units (Leap.Day) + Origin;
 209 
 210       --  Finally, convert all days calculated above into seconds and add all
 211       --  leap seconds that have occurred up to the current one (Index - 1).
 212       --  The seconds are then converted to target-dependent units.
 213 
 214       Result := (Days * Secs_In_Day + Units (Index - 1)) * Time_Units;
 215 
 216       --  First line of the output file
 217 
 218       if Index = 1 then
 219          Put_Line (F, "     (" & Result'Img & ",");
 220 
 221       --  Last line of the output file
 222 
 223       elsif Index = LS_Count then
 224          Put_Line (F, "      " & Result'Img & ");");
 225 
 226       --  In between lines
 227 
 228       else
 229          Put_Line (F, "      " & Result'Img & ",");
 230       end if;
 231    end loop;
 232 
 233    Close (F);
 234 end XLeaps;