stephes_ada_library_3.7.3_08b48307/source/sal-time_conversions.ads

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
--  Abstract :
--
--  Conversions between several time representations.
--
--  All values are expressed relative to a TAI clock.
--
--  References:
--
--  [1] http://aa.usno.navy.mil/faq/docs/TT.html
--  [2] http://hpiers.obspm.fr/eoppc/bul/bulc/UTC-TAI.history
--
--  Copyright (C) 2001, 2004 - 2015, 2017, 2018, 2019 Stephen Leake.  All Rights Reserved.
--
--  SAL is free software; you can redistribute it and/or modify it
--  under terms of the GNU General Public License as published by the
--  Free Software Foundation; either version 3, or (at your option)
--  any later version. SAL is distributed in the hope that it will be
--  useful, but WITHOUT ANY WARRANTY; without even the implied
--  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
--  See the GNU General Public License for more details. You should
--  have received a copy of the GNU General Public License distributed
--  with SAL; see file COPYING. If not, write to the Free Software
--  Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
--  USA.
--
--  As a special exception, if other files instantiate generics from
--  SAL, or you link SAL object files with other files to produce an
--  executable, that does not by itself cause the resulting executable
--  to be covered by the GNU General Public License. This exception
--  does not however invalidate any other reasons why the executable
--  file might be covered by the GNU Public License.

with Ada.Calendar;
with Ada.Real_Time;
with Interfaces;
with SAL.Generic_Decimal_Image;
with SAL.Generic_Fixed_Image;
with SAL.Interfaces_More;
package SAL.Time_Conversions is
   pragma Elaborate_Body; --  Body depends on Ada.Exceptions

   type Time_System_Type is (UTC, TAI, Relative);
   subtype Internal_Time_System_Type is Time_System_Type range TAI .. Relative;
   --  UTC is only for user input or display

   Small : constant := 10.0**(-9);
   type Time_Type is delta Small range -2**63 * Small .. (2**63 - 1) * Small;
   for Time_Type'Small use Small;
   for Time_Type'Size use 64;
   --  This gives a range of +- 292 years:
   --
   --  10.0**-9 seconds/small * 2**63 smalls = 9.2234e9 seconds
   --  60 seconds/min * 60 min/hr * 24 hrs/day * 365 days/year = 3.153e7 seconds/year

   --  Auto_Text_IO: ignore
   type Leap_Second_Table_Type (<>) is private;

   --  Auto_Text_IO: ignore
   type Leap_Second_Table_Access_Type is access constant Leap_Second_Table_Type;

   function Leap_Seconds
     (TAI_Time            : in Time_Type;
      Leap_Seconds_Table : in Leap_Second_Table_Type)
     return Integer;

   function TAI_To_UTC
     (TAI_Input_Time     : in Time_Type;
      Leap_Seconds_Table : in Leap_Second_Table_Type)
     return Time_Type;
   --  Return a UTC converted from TAI.

   function UTC_To_TAI
     (UTC_Input_Time     : in Time_Type;
      Leap_Seconds_Table : in Leap_Second_Table_Type)
     return Time_Type;
   --  Return a TAI converted from UTC.

   function Create (File_Name : in String) return Leap_Second_Table_Type;
   --  Create the leap_second table from a standard input file.
   --
   --  The latest input file is at [2].

   type Month_Type is (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);

   function Day_Of_Year
     (In_Year  : in Integer;
      In_Month : in Month_Type)
     return Integer;
   --  Return the day of year for the first day of each month; Jan 1
   --  is day 1.

   function Floor (Item : in Time_Type) return Time_Type;
   function Floor (Item : in Time_Type) return Integer;
   --  Same as Time_Type'Floor would be.

   function Modulo (Dividend, Divisor : in Time_Type) return Time_Type;
   function "mod" (Dividend, Divisor : in Time_Type) return Time_Type renames Modulo;

   ----------
   --  Useful constants.

   Seconds_Per_Minute    : constant := 60.0;
   Seconds_Per_Hour      : constant := 3600.0;
   Seconds_Per_Day       : constant := 86_400.0;
   Seconds_Per_Week      : constant := 604_800.0;
   Seconds_Per_Year      : constant := 31_536_000.0;
   Seconds_Per_Leap_Year : constant := 31_622_400.0;
   Days_Per_Week         : constant := 7;
   Days_Per_Year         : constant := 365;
   Days_Per_Leap_Year    : constant := 366;

   Days_Per_Julian_Year        : constant := 365.25;
   Days_Per_Julian_Century     : constant := 100.0 * Days_Per_Julian_Year;
   Julian_Days_Per_Second      : constant := 1.0 / (Seconds_Per_Day);
   Julian_Centuries_Per_Second : constant := 1.0 / (Seconds_Per_Day * Days_Per_Julian_Century);

   --  See [1] for a definition of TT, and the J2000 epoch.

   TT_Offset : constant := 32.184;
   --  time in TT system = time in TAI system + TT_Offset

   Julian_Days_1958_TAI : constant := 2_436_204.5;
   --  [1] 0 Hour 1 January 1958, TAI system; TAI origin

   TT_Offset_Days        : constant := TT_Offset / Seconds_Per_Day;
   Julian_Days_J2000_TAI : constant := 2_451_545.0 - TT_Offset_Days;
   --  [1] 12 Hour 1 January 2000 TT, converted to TAI system

   Julian_Centuries_1958_TAI : constant := Julian_Days_1958_TAI / Days_Per_Julian_Century;

   --  Note that there are not an integer number of days per year, so
   --  going forward a Julian year can mean shifting time of day by 6
   --  hours.

   ----------
   --  Operations

   function Leap_Year (Year : in Integer) return Boolean;
   --  Return TRUE if Year is a leap year.

   procedure To_Year_Day_Seconds
     (TAI_Time       : in     Time_Type;
      Year           :    out Integer;
      Day_In_Year    :    out Integer;
      Seconds_In_Day :    out Time_Type);
   --  TAI_Time is seconds since TAI origin. Day is in range 1 .. 364

   procedure To_Days_Seconds
     (TAI_Time       : in     Time_Type;
      Days           :    out Integer;
      Seconds_In_Day :    out Time_Type);
   --  TAI_Time is relative. Day is in range 1 .. 364

   function To_Julian_Day (TAI_Time : in Time_Type) return Long_Float;
   function To_Julian_Century (TAI_Time : in Time_Type) return Long_Float;
   --  Convert an absolute time; TAI_Time is seconds since TAI origin.

   function Seconds_To_Julian_Centuries (TAI_Time : in Time_Type) return Long_Float;
   --  Convert a relative time

   function To_J2000_Julian_Centuries (TAI_Time : in Time_Type) return Long_Float;
   --  TAI_Time is seconds since TAI origin; return Julian centuries since J2000 epoch.

   function Julian_Days_TT_To_Seconds_TAI (Julian_Days : in Long_Float) return Time_Type;
   function Julian_Days_TAI_To_Seconds_TAI (Julian_Days : in Long_Float) return Time_Type;

   function To_TAI_Time
     (Year           : in Integer;
      Day_In_Year    : in Integer;
      Seconds_In_Day : in Time_Type;
      Absolute       : in Boolean)
     return Time_Type;
   --  If absolute, Year must be > 1958, and result is seconds since TAI origin.
   --
   --  If not Absolute, leap days are ignored, and result is relative.

   function To_TAI_Time
     (Days           : in Integer;
      Seconds_Of_Day : in Time_Type)
     return Time_Type;
   --  Result is relative.
   --
   --  Nominally, Seconds_Of_Day should be < Seconds_Per_Day, but in
   --  fact it can be any Time_Type value.

   function To_TAI_Time
     (Hours             : in Integer;
      Minutes           : in Integer;
      Seconds_In_Minute : in Time_Type)
     return Time_Type;
   --  Result is relative.

   function To_Time (Time : in Ada.Real_Time.Time) return Time_Type;

   procedure To_Hour_Minute_Seconds
     (Seconds           : in     Time_Type;
      Hour              :    out Integer;
      Minute            :    out Integer;
      Seconds_In_Minute :    out Time_Type);

   ----------
   --  Conversions for counter/timers

   function Floor_Unsigned_16 (Item : in Time_Type) return Interfaces.Unsigned_16;
   --  Return smallest integral value not less than Item.
   --
   --  Raises SAL.Range_Error (with no message) if Item is outside
   --  range of Unsigned_16.

   function Floor_Unsigned_32 (Item : in Time_Type) return Interfaces.Unsigned_32;
   --  Return smallest integral value not less than Item.
   --
   --  Raises SAL.Range_Error (with no message) if Item is outside
   --  range of Unsigned_32.

   function To_Time (Microseconds : in Interfaces.Unsigned_16) return Time_Type;
   function To_Time (Microseconds : in Interfaces.Unsigned_32) return Time_Type;
   function To_Time (Milliseconds : in Interfaces.Unsigned_64) return Time_Type;

   function To_Microseconds (Time : in Time_Type) return SAL.Interfaces_More.Unsigned_15;
   function To_Microseconds (Time : in Time_Type) return Interfaces.Unsigned_16;
   function To_Microseconds (Time : in Time_Type) return Interfaces.Unsigned_32;

   function Checked_Unsigned_16 (Label : in String; Item : in Time_Type) return Interfaces.Unsigned_16;
   --  If Item is in range of Unsigned_16, return Unsigned_16 (Item).
   --  Else raise Range_Error with Label, value of Item in message.

   ----------
   --  The NASA Goddard ASIST package defines a string time representation:
   --
   --  YY-DDD-HH:MM:SS.LLL
   --  1234567890123456789
   --
   --  For absolute times, if YY < 70, year = 2000 + YY, else year = 1900 + YY.
   --
   --  DDD is in range 001 .. 364 (365 for leap years)
   --
   --  No fields of the ASIST time string may be omitted.
   --
   --  To allow dates outside the range 1970 .. 2069, we extend the
   --  ASIST representation to permit 4 digit years.
   --
   --  The to_* (Time : in String) functions raise SAL.Invalid_Format
   --  if the string is not a valid format.

   subtype ASIST_Time_String_Type is String (1 .. 19);
   subtype Extended_ASIST_Time_String_Type is String (1 .. 21);

   TAI_Epoch_String  : constant Extended_ASIST_Time_String_Type := "1958-001-00:00:00.000";
   Unix_Epoch_String : constant Extended_ASIST_Time_String_Type := "1970-001-00:00:00.000";
   J2000_TAI_String  : constant Extended_ASIST_Time_String_Type := "2000-001-11:59:27.816";

   function To_TAI_Time
     (Time     : in String;
      Absolute : in Boolean)
     return Time_Type;
   --  Time must be ASIST format or extended ASIST format. If
   --  Absolute, result is seconds since TAI origin.

   function Image is new SAL.Generic_Decimal_Image (Integer);
   pragma Warnings (Off, "instance does not use primitive operation ""mod"" at line 97"); -- gnat 2017
   pragma Warnings (Off, "instance uses predefined operation, not primitive operation ""mod"" at line 97"); -- gnat 2018
   function Image is new SAL.Generic_Fixed_Image (Time_Type);
   pragma Warnings (On);

   function To_ASIST_String (Time_TAI : in Time_Type) return ASIST_Time_String_Type;

   function To_Extended_ASIST_String (Time_TAI : in Time_Type) return Extended_ASIST_Time_String_Type;

   ----------
   --  GPS times

   type GPS_Time_Type is record
      --  GPS only has 11 bits in the week, but we allow more to
      --  accomodate a larger range of years.
      Weeks           : Interfaces.Unsigned_16;
      Seconds_Of_Week : Time_Type;
   end record;

   GPS_Epoch_TAI_String : constant Extended_ASIST_Time_String_Type := "1980-006-00:00:19.000";

   GPS_Epoch_TAI : constant Time_Type := 694656019.0; --  See unit test

   function To_GPS_Time (TAI : in Time_Type) return GPS_Time_Type;

   function To_TAI_Time (GPS : in GPS_Time_Type) return Time_Type;

   ----------
   --  to/from Ada.Calendar

   function To_Month_Type (Month : in Ada.Calendar.Month_Number) return Month_Type;

   function To_Month_Number (Month : in Month_Type) return Ada.Calendar.Month_Number;

   function To_TAI_Time (Cal : in Ada.Calendar.Time) return Time_Type;

   function To_Calendar_Time (TAI : in Time_Type) return Ada.Calendar.Time;

private

   --  Auto_Text_IO: ignore
   type Leap_Second_Table_Entry_Type is record
      Start_TAI_Time : Time_Type;
      Start_UTC_Time : Time_Type;
      Leap_Second    : Integer;
   end record;

   --  Auto_Text_IO: ignore
   type Leap_Second_Table_Type is array (Integer range <>) of Leap_Second_Table_Entry_Type;

end SAL.Time_Conversions;