8

年、月、日などの数値で指定された UTC 日付と時刻を time_t に変換したいと考えています。一部のシステムは、mkgmtimeまたはtimegmこの目的のために機能を提供しますが、それは標準ではなく、私の Solaris システムには存在しません。

これまでに見つけた唯一の解決策は、 setenv を使用してローカル タイム ゾーンを UTC に設定してから を呼び出すことmktimeです。ただし、このアプローチはスレッドセーフではなく、遅く、移植性がなく、システムでメモリリークが発生することさえあります。

gmtimeを使用して現在の UTC オフセットを決定し、それを の結果に追加しようとするアプローチも見てきましたmktime。しかし、私が見た限りでは、これらのアプローチにはすべてギャップがありました。結局のところ、現地時間から UTC への変換は一意ではありません。

最善の解決策は何だと思いますか?

4

4 に答える 4

11

独自のバージョンの mkgmtime を実装することにしましたが、思ったより簡単でした。

const int SecondsPerMinute = 60;
const int SecondsPerHour = 3600;
const int SecondsPerDay = 86400;
const int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

bool IsLeapYear(short year)
{
    if (year % 4 != 0) return false;
    if (year % 100 != 0) return true;
    return (year % 400) == 0;
}

time_t mkgmtime(short year, short month, short day, short hour, short minute, short second)
{
    time_t secs = 0;
    for (short y = 1970; y < year; ++y)
        secs += (IsLeapYear(y)? 366: 365) * SecondsPerDay;
    for (short m = 1; m < month; ++m) {
        secs += DaysOfMonth[m - 1] * SecondsPerDay;
        if (m == 2 && IsLeapYear(year)) secs += SecondsPerDay;
    }
    secs += (day - 1) * SecondsPerDay;
    secs += hour * SecondsPerHour;
    secs += minute * SecondsPerMinute;
    secs += second;
    return secs;
}

私の主な関心事は、それmkgmtimeが と一致していなければならないということでしたgmtime。そのようなものgmtime(mktime(t))は、元の入力値を返します。したがって、time_t の 0 と MAX_INT の間の 61 のすべての倍数の結果を比較しましたが、実際には同じです (少なくとも私のシステムでは)。したがって、上記のルーチンは正しいです。

この結果は、C ライブラリがうるう秒を考慮していないことも意味します。これは、それ自体は悪いことですが、私の目的には適しています。2 つの機能は、長い間一貫したままになります。念のために言うと、この関数を使用するタイムスタンプ クラスは常にプログラムの開始時にクイック チェックを実行し、いくつかの意味のある値の一貫性を証明します。

于 2012-09-10T13:44:57.117 に答える
10

完全を期すために、構造体 tm* を引数として取る mkgmtime() のバージョンを次に示します。

static time_t mkgmtime(const struct tm *ptm) {
    time_t secs = 0;
    // tm_year is years since 1900
    int year = ptm->tm_year + 1900;
    for (int y = 1970; y < year; ++y) {
        secs += (IsLeapYear(y)? 366: 365) * SecondsPerDay;
    }
    // tm_mon is month from 0..11
    for (int m = 0; m < ptm->tm_mon; ++m) {
        secs += DaysOfMonth[m] * SecondsPerDay;
        if (m == 1 && IsLeapYear(year)) secs += SecondsPerDay;
    }
    secs += (ptm->tm_mday - 1) * SecondsPerDay;
    secs += ptm->tm_hour       * SecondsPerHour;
    secs += ptm->tm_min        * SecondsPerMinute;
    secs += ptm->tm_sec;
    return secs;
}
于 2015-11-06T18:48:06.783 に答える