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.


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.)


More information about the tz mailing list