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