10

私は次の整数を持っています:

int y, mon, d, h, min, s;

それらの値は、2012それぞれ、、、、、、です。アプリケーションの別の場所で「UTC」を選択した場合は「2012/06/2712:47:53 UTC」の日時を表し、「2012/06/2712:47:53AEST」を選択した場合は「アプリケーションのどこかで「AEST」を選択しました。0627124753

これをに変換したいのですが、これtime_tを行うために現在使用しているコードは次のとおりです。

struct tm timeinfo;
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = mon - 1;
timeinfo.tm_mday = day;
timeinfo.tm_hour = hour;
timeinfo.tm_min = min;
timeinfo.tm_sec = sec;
//timeinfo.tm_isdst = 0; //TODO should this be set?

//TODO find POSIX or C standard way to do covert tm to time_t without in UTC instead of local time
#ifdef UNIX
return timegm(&timeinfo);
#else
return mktime(&timeinfo); //FIXME Still incorrect
#endif

だから私はとを使用してtm structmktimeますが、これは常に私のローカルタイムゾーンを想定しているため、うまく機能していません。

これを行う正しい方法は何ですか?

以下は私がこれまでに思いついた解決策です。基本的に、次の3つのいずれかを実行します。

  1. UNIXの場合は、単に使用しますtimegm
  2. UNIXでない場合
    1. どちらか、UTCエポックとローカルエポックの違いをオフセットとして使用して計算を行います
      • 予約:数学が正しくない可能性があります
    2. または、「TZ」環境変数を一時的にUTCに設定します
      • 予約:このコードをマルチスレッド化する必要がある場合は、トリップします
namespace tmUtil
{
    int const tm_yearCorrection = -1900;
    int const tm_monthCorrection = -1;
    int const tm_isdst_dontKnow = -1;

#if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ) && !(defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM))
    static bool isLeap(int year)
    {
        return
            (year % 4) ? false
            : (year % 100) ? true
            : (year % 400) ? false
            : true;
    }

    static int daysIn(int year)
    {
        return isLeap(year) ? 366 : 365;
    }
#endif
}

time_t utc(int year, int mon, int day, int hour, int min, int sec)
{
    struct tm time = {0};
    time.tm_year = year + tmUtil::tm_yearCorrection;
    time.tm_mon = mon + tmUtil::tm_monthCorrection;
    time.tm_mday = day;
    time.tm_hour = hour;
    time.tm_min = min;
    time.tm_sec = sec;
    time.tm_isdst = tmUtil::tm_isdst_dontKnow;

    #if defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM) //TODO remove && 00
        time_t result;
        result = timegm(&time);
        return result;
    #else
        #if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ)
            //TODO check that math is correct
            time_t fromEpochUtc = mktime(&time);

            struct tm localData;
            struct tm utcData;
            struct tm* loc = localtime_r (&fromEpochUtc, &localData);
            struct tm* utc = gmtime_r (&fromEpochUtc, &utcData);
            int utcYear = utc->tm_year - tmUtil::tm_yearCorrection;
            int gmtOff =
                (loc-> tm_sec - utc-> tm_sec)
                + (loc-> tm_min - utc-> tm_min) * 60
                + (loc->tm_hour - utc->tm_hour) * 60 * 60
                + (loc->tm_yday - utc->tm_yday) * 60 * 60 * 24
                + (loc->tm_year - utc->tm_year) * 60 * 60 * 24 * tmUtil::daysIn(utcYear);

            #ifdef UNIX
                if (loc->tm_gmtoff != gmtOff)
                {
                    StringBuilder err("loc->tm_gmtoff=", StringBuilder((int)(loc->tm_gmtoff)), " but gmtOff=", StringBuilder(gmtOff));
                    THROWEXCEPTION(err);
                }
            #endif

            int resultInt = fromEpochUtc + gmtOff;
            time_t result;
            result = (time_t)resultInt;
            return result;
        #else
            //TODO Find a way to do this without manipulating environment variables
            time_t result;
            char *tz;
            tz = getenv("TZ");
            setenv("TZ", "", 1);
            tzset();
            result = mktime(&time);
            if (tz)
                setenv("TZ", tz, 1);
            else
                unsetenv("TZ");
            tzset();
            return result;
        #endif
    #endif
}

NBStringBuilderは内部クラスであり、この質問の目的には関係ありません。

より詳しい情報:

これは、ブーストなどを使用して簡単に実行できることを私は知っています。しかし、これはオプションではありません。数学的に、またはacまたはc ++標準関数、あるいはそれらの組み合わせを使用して実行する必要があります。

timegmこの問題は解決しているように見えますが、C/POSIX標準の一部ではないようです。#ifdef $PLATFORMこのコードは現在、複数のプラットフォーム(Linux、OSX、Windows、iOS、Android(NDK))でコンパイルされているため、ソリューションにタイプのものが含まれている場合でも、これらすべてのプラットフォームで機能させる方法を見つける必要があります。

4

7 に答える 7

4

struct tmUTCで指定されたものをに変換するだけの場合は、次のtime_tように実行できます。

#include <time.h>

time_t utc_to_time_t(struct tm* timeinfo)
{
    tzset(); // load timezone information (this can be called just once)

    time_t t = mktime(timeinfo);
    return t - timezone;
}

これは基本的に、UTC時刻をtime_t指定された時刻がローカルであるかのように変換し、結果にタイムゾーン補正を適用してUTCに戻します。

gcc/cygwinおよびVisualStudio2010でテスト済み。

これがお役に立てば幸いです。

更新:ご指摘のとおり、上記の私のソリューションは、照会された日付の夏時間の状態が現在の時刻と異なる場合、1時間ずれたtime_t値を返す可能性があります。

この問題の解決策は、日付がDSTリージョンに該当するかどうかを通知できる追加の関数を用意し、それと現在のDSTフラグを使用して、によって返される時刻を調整することmktimeです。これは実際には簡単です。電話をかけるときは、メンバーを-1mktime()に設定するだけでtm_dst、システムは指定された時間にDSTを把握するために最善を尽くします。これについてシステムを信頼していると仮定すると、この情報を使用して修正を適用できます。

#include <time.h>

time_t utc_to_time_t(struct tm* timeinfo)
{
    tzset(); // load timezone information (this can be called just once)

    timeinfo->tm_isdst = -1; // let the system figure this out for us
    time_t t = mktime(timeinfo) - timezone;

    if (daylight == 0 && timeinfo->tm_isdst != 0)
        t += 3600;
    else if (daylight != 0 && timeinfo->tm_isdst == 0)
        t -= 3600;
    return t;
}
于 2012-07-04T17:54:54.373 に答える
4

少し口の中に入れたくなりますが、で文字列に変換しstrftime()、文字列のタイムゾーンを置き換えてから、で、に変換して戻すことstrptime()time_tできmktime()ます。詳細に:

#ifdef UGLY_HACK_VOIDS_WARRANTY
time_t convert_time(const struct tm* tm)
{
    const size_t BUF_SIZE=256;
    char buffer[BUF_SIZE];
    strftime(buffer,256,"%F %H:%M:%S %z", tm);
    strncpy(&buffer[20], "+0001", 5); // +0001 is the time-zone offset from UTC in hours
    struct tm newtime = {0};
    strptime(buffer, "%F %H:%M:%S %z", &newtime);
    return mktime(&newtime);
}
#endif

しかし、結局のところ、ブーストがオプションであるという力を納得させることを強くお勧めします。Boostは、カスタムタイムゾーンを強力にサポートしています。これをエレガントに行う他のライブラリもあります。

于 2012-07-10T00:10:14.303 に答える
2

Linuxまたは他のUNIxまたはUNIXライクなシステムを使用している場合は、必要なtimegm機能を実行できる可能性があります。リンクされたマニュアルページには移植可能な実装があるので、自分で作成できます。Windowsでは、そのような機能はありません。

于 2012-06-27T05:15:49.133 に答える
1

timegm(1)Android(付属していない)で動作する関数を取得しようと何日もこれに頭を悩ませた後、私はついにこのシンプルでエレガントなソリューションを発見しました。これは美しく動作します。

time_t timegm( struct tm *tm ) {
  time_t t = mktime( tm );
  return t + localtime( &t )->tm_gmtoff;
}

これが適切なクロスプラットフォームソリューションではない理由がわかりません。

これがお役に立てば幸いです。

于 2014-01-29T18:53:59.827 に答える
1
time_t my_timegm2(struct tm *tm)
{
    time_t ret = tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 + tm->tm_yday*86400;
    ret += ((time_t)31536000) * (tm->tm_year-70);
    ret += ((tm->tm_year-69)/4)*86400 - ((tm->tm_year-1)/100)*86400 + ((tm->tm_year+299)/400)*86400;
    return ret;
}
于 2022-02-04T20:37:28.090 に答える
0

より簡単な解決策があるようです:

#include <time64.h>
time_t timegm(struct tm* const t) 
{
  return (time_t)timegm64(t);
}

実際には、まだ移植が少しあるので、実際に機能するかどうかはまだテストしていませんが、コンパイルされます。

于 2014-12-08T21:50:04.713 に答える
0

これが私の解決策です:

#ifdef WIN32
#   define timegm _mkgmtime
#endif

struct tm timeinfo;
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = mon - 1;
timeinfo.tm_mday = day;
timeinfo.tm_hour = hour;
timeinfo.tm_min = min;
timeinfo.tm_sec = sec;

return timegm(&timeinfo);

これは、UNIXとWindowsの両方で機能するはずです

于 2018-08-30T14:02:04.353 に答える