Solaris 上 timegm 的替代品

Alternative to timegm on Solaris

我有一个程序最初是为 Linux 编写的,但我现在需要在 Solaris 10 上获得它 运行。

该程序的一部分使用 timegm 函数将 struct tm 转换为 time_t 纪元秒值。输入时间参考UTC。

尝试在 Solaris 上编译此程序,但失败,因为找不到 timegm。经过一些谷歌搜索后,我意识到这个函数很久以前就已经从 Solaris 中删除了(甚至 Linux 联机帮助页也建议不要使用它,因为它没有标准化)。

但是到目前为止,我还没有找到一个替代函数,它采用引用 UTC 的 struct tm 并转换为纪元时间。我在网上找到的大多数参考资料都推荐使用 mktime,但是该函数会参考系统本地时区来解释输入。

请注意,我不希望使用 tzset 将时区强制为 UTC,因为这会对程序产生其他副作用。

所以我的问题是:在没有 timegm 的情况下,如何将 struct tm 细分时间值(相对于 UTC 表示)转换为纪元时间?

该程序是用 C++ 编写的,所以我不限于 C 解决方案,尽管我不想进行大规模重写以使用一些额外的时间库。

根据 the POSIX standard for tzset():

SYNOPSIS

#include <time.h>

extern int daylight;
extern long timezone;

extern char *tzname[2];
void tzset(void);

...

The tzset() function also shall set the external variable daylight to 0 if Daylight Savings Time conversions should never be applied for the timezone in use; otherwise, non-zero. The external variable timezone shall be set to the difference, in seconds, between Coordinated Universal Time (UTC) and local standard time.

您应该可以调用 tzset() 来设置 timezone 中的值,然后使用 mktime() 获取当前时区的时间,然后在 timezone 变量到 mktime() 的结果以将该结果转换为 UTC。

我现在无法访问 Solaris 来测试它。

您可以使用 days_from_civildescribed 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;
}

tm 中的 {year, month, day} 三元组转换为自纪元 (1970-01-01) 以来的天数。将这些字段从 tm 转换为它们的偏心率时要小心(例如 tm_year + 1900)。

将此天数乘以 86400,然后加上来自 tm 的 {小时、分钟、秒} 数据(每个都转换为秒)。

大功告成。不用担心闰秒,timegm 也不担心。如果你真的很关心闰秒,我有一个 C++11/14 solution available to deal with that,但我猜这比你想了解的要多。

不要被上面显示的 C++14 语法拖延。将此算法转换为 C(或与此相关的任何其他语言)是微不足道的。