FW: Notes on 'timesub"

Olson, Arthur David (NIH/NCI) [E] olsona at dc37a.nci.nih.gov
Thu Jan 15 17:44:24 UTC 2009


I'm forwarding this message from John Dlugosz, who is not on the time zone mailing list. Those of you who are on the list, please direct replies appropriately.

				--ado

From: John Dlugosz [mailto:JDlugosz at TradeStation.com] 
Sent: Thursday, January 15, 2009 12:40
To: tz at lecserver.nci.nih.gov
Subject: Notes on 'timesub"

The implementation of 'timesub' uses quite a bit of complex logic, approximations, and looping to do what can be done with a single expression.  The part of the function that digests the number of days into y/m/d fields has been known since before computers as "Julian Day Number" and that can be harnessed directly simply by subtracting a constant for the Epoch base.

Here is my version, which is not so digested into my own library that you can't follow the logic:

       void timezone_t::state::timesub (
          const __time64_t t, // input time number
          const long offset, // added to time, but figures date from original (e.g. like a denormal value)
          ymd_hms& tmp  // output
          ) const
        {
        // more complex than it needs to be!
        static const int JDayEpoch= Julian_day_number (ymd(EPOCH_YEAR, 1, 1));
        int days_in_epoch = t / SECSPERDAY;
        int rem = t - days_in_epoch * SECSPERDAY;
        int corr= count_leap_seconds (JDayEpoch, JDayEpoch+days_in_epoch);
        tmp= Julian_day_number (JDayEpoch+days_in_epoch);
        tmp.set_normalized (hms(0,0,rem+offset - corr));
        }

In your code, the part I'm referring to is the middle.  First it finds 'corr' for leap seconds, and afterwards it adds the seconds into the h/m/s possibly overflowing into the d/m/y.  I have those in callable functions, but the approach is the same.  It's just that the middle part, converting a number of seconds into y/m/d, is not as hard as you make it out to be.  

This is a direct implementation of the published formula, which involves no looping or successive approximations or peeling off each month individually:

       ymd Julian_day_number (int J)
        {
        const int j= J+32044;
        const int g= j / 146097;
        const int dg= j % 146097;
        const int c= (dg / 36524 + 1) * 3 / 4;
        const int dc= dg - c * 36524;
        const int b= dc / 1461;
        const int db= dc % 1461;
        const int a= (db/365+1) * 3 / 4;
        const int da= db - a * 365;
        const int y= g*400 + c*100 + b*4 + a;
        const int m= (da*5+308) / 153 - 2;
        const int d= da - (m+4) *153 / 5 + 122;
        return ymd (y-4800+(m+2)/12,  (m+2)%12+1, d+1);
        }

(The comment that it is (still) more complex than it needs to be is because the normalization code in my library can handle that directly, so I can just call existing code.)

--John





More information about the tz mailing list