From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,b1850e397df49d95 X-Google-Attributes: gid103376,public From: "Norman H. Cohen" Subject: Re: How to convert an Fixed_Point to to an Integer ? Date: 1996/12/26 Message-ID: <32C2A408.53B8@watson.ibm.com> X-Deja-AN: 206060473 references: <01bbf058$cbdbf980$LocalHost@jerryware> <1996Dec23.072835.1@eisner> <01bbf179$0c4361e0$9b2d5c8b@jerryware> <01bbf24c$a8db4800$b72d5c8b@jerryware> content-type: text/plain; charset=us-ascii organization: IBM Thomas J. Watson Research Center mime-version: 1.0 reply-to: ncohen@watson.ibm.com newsgroups: comp.lang.ada x-mailer: Mozilla 3.0 (Win95; I) Date: 1996-12-26T00:00:00+00:00 List-Id: Referring to the portable calculation of the number of seconds since the start of 1970, Robert Dewar wrote: > The way to do this is to write a loop using Time_Of that goes forward one > year at a time, accumulating seconds, starting with 1970, till you get to > the year you want. Of course you could make this a memo function so that > once it knew the number of seconds to the start of a given year, it would > not compute it again. A more efficient, and to me conceptually simpler, approach is to split the Time value returned by Calendar.Clock into a year, month, day of month, and number of seconds since midnight, and use those values to calculate the number of seconds. Accounting for leap years introduces a slight complication, and this approach does not account for leap seconds, but I'm not sure how Unix systems deal with leap seconds either. (Does the current Unix time include all leap seconds since the start of 1970? If not, is it possible for a file created during a leap second to have the same time stamp as a file created a second later, and a later time stamp than a file created half a second later?) Here's the calculation, basically what an earlier message referred to as "calling Split and doing a bunch of multiplications": package Epoch is -- Note: This package works from the beginning of 1970 until the end of -- 2099, after which the package Ada.Calendar is no longer usable. -- During this period, a year y is a leap year if and only if y mod 4 = 0. -- This package ignores the existence of leap seconds. That is, the number -- of seconds in previous years is calculated as if leap seconds did not -- exist, and the behavior of the function Seconds_Since_Epoch during a -- leap second depends on the value returned by Ada.Calendar.Clock during -- a leap second, which is not defined by the Ada standard. -- Some fundamental constants: Days_Per_Ordinary_Year : constant := 365; Days_Per_Leap_Year : constant := 366; Seconds_Per_Day : constant := 86_400; -- A compile-time calculation of the number of seconds from the start of -- 1970 to the start of 2100: Max_Years : constant := 2100 - 1970; Max_Leap_Years : constant := (2100 - 1969) / 4; -- The number of leap years since 1970 but before -- year y is given by (y-1969)/4. -- ("Proof": The value of the integer quotient -- increases by 1 whenever y increases to a value -- such that (y-1969) mod 4 = 0, i.e., whenever -- y mod 4 = 1, i.e., whenver y-1 is a leap year.) Max_Ordinary_Years : constant := Max_Years - Max_Leap_Years; Max_Days : constant := Max_Ordinary_Years*Days_Per_Ordinary_Year + Max_Leap_Years*Days_Per_Leap_Year; Max_Seconds : constant := Max_Days * Seconds_Per_Day; -- The principal facilities provided by the package: type Seconds_Type is delta Duration'Delta range 0.0 .. 1.0*Max_Seconds; -- multiplication by 1.0 converts universal_integer to universal_real function Seconds_Since_Epoch return Seconds_Type; -- Returns the number of seconds since the start of 1970. end Epoch; with Ada.Calendar; package body Epoch is subtype Month_Number is Ada.Calendar.Month_Number; January : constant Month_Number := Month_Number'First; February : constant Month_Number := Month_Number'Succ(January); December : constant Month_Number := Month_Number'Last; type Days_Type is range 0 .. Max_Days; -- Use of type Integer would not be portable to implementations for -- which Integer'Size = 16, since Max_Days > 2**15-1 Days_In_Month : array (Month_Number) of Days_Type := (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); -- Gives the number of days in the given month -- in an ordinary year. Days_Before_Month : array (Month_Number) of Days_Type; -- Gives the number of days since the start of the -- year occurring before the given month in an -- ordinary year; initialized during package-body -- elaboration. function Seconds_Since_Epoch return Seconds_Type is This_Year : Ada.Calendar.Year_Number; This_Month : Month_Number; This_Day : Ada.Calendar.Day_Number; Seconds_Since_Midnight : Ada.Calendar.Day_Duration; Previous_Years : Natural; Previous_Leap_Years : Natural; Days_Before_This_Year : Days_Type; Days_Before_This_Month : Days_Type; Days_Before_Today : Days_Type; Seconds_Before_Today : Seconds_Type; begin Ada.Calendar.Split (Ada.Calendar.Clock, This_Year, This_Month, This_Day, Seconds_Since_Midnight); Previous_Years := This_Year - 1970; Previous_Leap_Years := (This_Year - 1969) / 4; -- See declaration of Max_Leap_Years in package declaration. Days_Before_This_Year := Days_Type(Previous_Years) * Days_Per_Ordinary_Year + Days_Type(Previous_Leap_Years); -- i.e., number of ordinary days + number of February 29ths if This_Year mod 4 = 0 and This_Month > February then Days_Before_This_Month := Days_Before_This_Year + Days_Before_Month(This_Month) + 1; -- + 1 is for February 29 else Days_Before_This_Month := Days_Before_This_Year + Days_Before_Month(This_Month); end if; Days_Before_Today := Days_Before_This_Month + Days_Type(This_Day - 1); Seconds_Before_Today := Seconds_Per_Day * Seconds_Type(Days_Before_Today); return Seconds_Before_Today + Seconds_Type(Seconds_Since_Midnight); end Seconds_Since_Epoch; begin -- initialization of Days_Before_Month Days_Before_Month(January) := 0; for Month in February .. December loop Days_Before_Month(Month) := Days_Before_Month(Month-1) + Days_In_Month(Month-1); end loop; end Epoch; -- Norman H. Cohen mailto:ncohen@watson.ibm.com http://www.research.ibm.com/people/n/ncohen