I have a program that was originally written for Linux, but I now have a requirement to get it running on Solaris 10.
Part of this program uses the timegm function to convert a struct tm
into a time_t
epoch seconds value. The input time is referenced to UTC.
Trying to compile this program on Solaris, it fails because timegm
cannot be found. After some googling I realized that this function has been removed from Solaris a long time ago (and even the Linux manpage recommends against using it, because it isn't standardized).
However I have so far not been able to find an alternative function, that takes a struct tm
referenced to UTC and converts to epoch time. Most references I found on the net recommend using mktime, however that function interprets the inputs with reference to the system local time zone.
Note that I do not wish to use tzset
to force the timezone to UTC, as that would have other side effects on the program.
So my question is: how can I convert a struct tm
broken down time value, expressed with respect to UTC, into an epoch time, in the absence of timegm
?
The program is written in C++ so I'm not limited to C solutions, although I would prefer not to embark on a wholesale rewrite to use some additional time library.
You could use days_from_civil
which is described here in detail
// Returns number of days since civil 1970-01-01. Negative values indicate
// days prior to 1970-01-01.
// Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
// m is in [1, 12]
// d is in [1, last_day_of_month(y, m)]
// y is "approximately" in
// [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
// Exact range of validity is:
// [civil_from_days(numeric_limits<Int>::min()),
// civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const Int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<Int>(doe) - 719468;
}
to convert the {year, month, day} triple in the tm
to a count of days since the epoch (1970-01-01). Be careful when converting these fields from tm
for their eccentricities (e.g. tm_year + 1900
).
Multiply this count of days by 86400 and add to that the {hours, minutes, seconds} data from the tm
(each converted to seconds).
And you're done. Don't worry about leap seconds, timegm
didn't worry about them either. If you're really concerned about leap seconds I have a C++11/14 solution available to deal with that, but I'm guessing that is more than you want to get into.
Don't be put off by the C++14 syntax shown above. It is trivial to convert this algorithm to C (or any other language for that matter).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With