10

これは単純な質問ですが、解決策は単純ではないようです。UTC から現地時間に変換する方法を知りたいです。私は、標準であり、あらゆる場所のあらゆるコンピューターで動作することが多かれ少なかれ保証されている C のソリューションを探しています。

次のリンクを注意深く読みましたが、解決策が見つかりません。

Cでlocaltimeを含む文字列をUTCに変換する

C/C++ での現地時間と GMT/UTC 間の変換

次のような多くのバリエーションを試しました(datetimeはUTCの時刻と日付の文字列です):

strptime(datetime, "%A %B %d %Y %H %M %S", tp);
strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp);

または

strptime(datetime, "%A %B %d %Y %H %M %S", tp);
lt=mktime(tp);
printtime=ctime(&lt);

何を試しても、印刷時間はUTCと同じになります。

編集 11-29-2013 : 以下の「R」による非常に役立つ回答に基づいて、最終的に実際の例を作成することができました。テストした 2 つのタイムゾーン、CET と PST で正しく動作していることがわかりました。

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

long long diff_tm(struct tm *a, struct tm *b)
{
  return a->tm_sec - b->tm_sec
          +60LL*(a->tm_min - b->tm_min)
          +3600LL*(a->tm_hour - b->tm_hour)
          +86400LL*(a->tm_yday - b->tm_yday)
          +(a->tm_year-70)*31536000LL
          -(a->tm_year-69)/4*86400LL
          +(a->tm_year-1)/100*86400LL
          -(a->tm_year+299)/400*86400LL
          -(b->tm_year-70)*31536000LL
          +(b->tm_year-69)/4*86400LL
          -(b->tm_year-1)/100*86400LL
          +(b->tm_year+299)/400*86400LL;
}


int main()
{
  time_t utc, local;
  char buf[100];
  const char datetime[]="2013 11 30 23 30 26 UTC"; /* hard coded date and time in UTC */

  struct tm *tp=malloc(sizeof(struct tm));
  if(tp==NULL)
    exit(-1);

  struct tm *localt=malloc(sizeof(struct tm));
  if(localt==NULL)
    exit(-1);

  memset(tp, 0, sizeof(struct tm));
  memset(localt, 0, sizeof(struct tm));

  printf("UTC date and time to be converted in local time: %s\n", datetime);

  /* put values of datetime into time structure *tp */
  strptime(datetime, "%Y %m %d %H %M %S %z", tp);

  /* get seconds since EPOCH for this time */
  utc=mktime(tp);
  printf("UTC date and time in seconds since EPOCH: %d\n", utc);

  /* lets convert this UTC date and time to local date and time */

  struct tm e0={ .tm_year = 70, .tm_mday = 1 }, e1, new;
  /* get time_t EPOCH value for e0 (Jan. 1, 1970) */
  time_t pseudo=mktime(&e0);

  /* get gmtime for this value */
  e1=*gmtime(&pseudo);

  /* calculate local time in seconds since EPOCH */
  e0.tm_sec += utc - diff_tm(&e1, &e0);

  /* assign to local, this can all can be coded shorter but I attempted to increase clarity */
  local=e0.tm_sec;
  printf("local date and time in seconds since EPOCH: %d\n", local);

  /* convert seconds since EPOCH for local time into localt time structure */
  localt=localtime(&local);

  /* get nicely formatted human readable time */
  strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S %Z", localt);

  printf("local date and time: %s\n", buf);
}

ほとんどのシステムで問題なくコンパイルできます。時刻と日付を UTC でハードコーディングし、それを現地の時刻と日付に変換しました。

4

10 に答える 10

6

POSIX(したがって、エポックからの秒数としてのPOSIX仕様)を想定できる場合、time_t最初にPOSIX式を使用して、エポックからの秒数に変換します。

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

次に、 を使用して、現地時間でエポックを表すlocaltime((time_t []){0})を取得します。struct tmエポックからの秒数tm_secを this のフィールドに追加しstruct tm、呼び出しmktimeて正規化します。

編集:実際には、唯一の POSIX 依存関係は、対応する既知のエポックを持つ(time_t)0ことです。本当に必要な場合は、おそらくそれを回避する方法を見つけることができます...たとえば、両方への呼び出しgmtimelocaltimeat time_t0..

編集2:これを行う方法のスケッチ:

#include <time.h>
#include <stdio.h>

long long diff_tm(struct tm *a, struct tm *b)
{
        return a->tm_sec - b->tm_sec
                +60LL*(a->tm_min - b->tm_min)
                +3600LL*(a->tm_hour - b->tm_hour)
                +86400LL*(a->tm_yday - b->tm_yday)
                +(a->tm_year-70)*31536000LL
                -(a->tm_year-69)/4*86400LL
                +(a->tm_year-1)/100*86400LL
                -(a->tm_year+299)/400*86400LL
                -(b->tm_year-70)*31536000LL
                +(b->tm_year-69)/4*86400LL
                -(b->tm_year-1)/100*86400LL
                +(b->tm_year+299)/400*86400LL;
}

int main(int argc, char **argv)
{
        char buf[100];
        struct tm e0 = { .tm_year = 70, .tm_mday = 1 }, e1, new;
        time_t pseudo = mktime(&e0);
        e1 = *gmtime(&pseudo);
        e0.tm_sec += atoi(argv[1]) - diff_tm(&e1, &e0);
        mktime(&e0);
        strftime(buf, sizeof buf, "%c", &e0);
        puts(buf);
}

醜い出力コードを気にしないでください。このプログラムは、「POSIX エポックからの相対秒数」の形式で引数を取り、結果の時間を現地時間で出力します。上で引用した式を使用して、エポック以降の任意の UTC 時間を秒に変換できます。diff_tmこのコードは決してPOSIXに依存しないことに注意してくださいint。これを修正するには、オフセットと、オフセットが 0 に達するまで(またはそれ以下long longの) インクリメントを追加し続けるループを使用し、オフセットが 0 になるまで再正規化を呼び出します。INT_MAX/2INT_MIN/2)mktime

于 2012-01-31T08:58:42.520 に答える
5

ああ...私はCの初心者かもしれませんが、この実用的な例を手に入れました:

#include <time.h>
#include <stdio.h>
int main(void)
{
        time_t abs_ts,loc_ts,gmt_ts;
        struct tm loc_time_info,gmt_time_info;

        /*Absolute time stamp.*/
        time(&abs_ts);

        /*Now get once the local time for this time stamp,
        **and once the GMT (UTC without summer time) time stamp.*/
        localtime_r(&abs_ts,&loc_time_info);
        gmtime_r(&abs_ts,&gmt_time_info);

        /*Convert them back.*/
        loc_ts=mktime(&loc_time_info);
        gmt_ts=mktime(&gmt_time_info);

        /*Unfortunately, GMT still has summer time. Get rid of it:*/
        if(gmt_time_info.tm_isdst==1)
                {gmt_ts-=3600;}

        printf("Local timestamp: %lu\n"
                "UTC timestamp: %lu\n"
                "Difference in hours: %lu\n\n",
                loc_ts,
                gmt_ts,
                (loc_ts-gmt_ts)/3600);

        return 0;
}

次の出力が生成されます。

ローカル タイムスタンプ: 1412554119

GMT タイムスタンプ: 1412546919

時間差:2

これで、UTC と現地時間の差が秒単位でわかりました。それはそれを変換するのに十分なはずです。

あなたのコード、aseq への 1 つの注意: ここでは必要なく malloc を使用しています (スタックに値を memset することもできます。malloc は高価になる可能性がありますが、スタック割り当ては多くの場合はるかに高速です)。それは非常に悪い習慣です。

別物:

memset(tp, 0, sizeof(struct tm));

sizeof(*tp) (または、tp をスタックに置く場合は sizeof(tp)) を memset に渡した方がよいでしょう。これにより、オブジェクトのタイプが変更された場合でも、完全に memset のままになります。

于 2014-10-06T00:10:53.920 に答える
3

要約すると、UTC の分解された日付 (struct tm) の (ローカル) カレンダー時間 (time_t) への変換は、timegm() で実現されます - mktime() の反対 - ただし、timegm() は標準ではありません機能(それがどのように論理的であるか)。C 標準では、time()、gmtime()、mktime()、および difftime() のみが残されています。

他のドキュメントで見つかった回避策では、最初に環境変数 TZ を null 文字列に設定し、次に mktime() を呼び出して UTC カレンダー時間を取得し、次に TZ を初期値にリセットすることで、timegm() をエミュレートすることをアドバイスしていますが、これもまた、標準ではありません。

基本的に、私が理解しているように、現地時間と UTC 時間の差は単なるオフセットであるため、そのオフセットを評価できれば、mktime() の結果を調整できるので、ここに私の提案があります。

time_t my_timegm(struct tm *tm) {
    time_t epoch = 0;
    time_t offset = mktime(gmtime(&epoch));
    time_t utc = mktime(tm);
    return difftime(utc, offset);
}

簡単なテスト:

int main(void) {
    time_t now = time(0);
    struct tm local = *localtime(&now);
    struct tm utc = *gmtime(&now);
    time_t t1 = mktime(&local);
    time_t t2 = my_timegm(&utc);
    assert(t1 == t2);
    printf("t =%lu\nt1=%lu\nt2=%lu\n",now,t1,t2);
    return 0;
}
于 2012-07-16T09:24:16.367 に答える
0

@Dachschadenの回答に従い、人間が読める出力も示す例を作成し、UTCと現地時間の秒数の差のDSTオプションを削除しました。ここにあります:

#include <time.h>
#include <stdio.h>

#define DATE_MAX_STR_SIZE 26
#define DATE_FMT "%FT%TZ%z"

int main() {

    time_t now_time, now_time_local;
    struct tm now_tm_utc, now_tm_local;
    char str_utc[DATE_MAX_STR_SIZE];
    char str_local[DATE_MAX_STR_SIZE];

    time(&now_time);
    gmtime_r(&now_time, &now_tm_utc);
    localtime_r(&now_time, &now_tm_local);

    /* human readable */
    strftime(str_utc, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_utc);
    strftime(str_local, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_local);

    printf("\nUTC: %s", str_utc);
    printf("\nLOCAL: %s\n", str_local);

    /* seconds (epoch) */
    /* let's forget about DST for time difference calculation */
    now_tm_local.tm_isdst = 0;
    now_tm_utc.tm_isdst = 0;
    now_time_local = now_time + (mktime(&now_tm_local) - mktime(&now_tm_utc));

    printf("\nUTC in seconds: %lu", now_time);
    printf("\nLOCAL in seconds: %lu\n", now_time_local);

    return 0;
}

私のマシンの出力は次のとおりです。

UTC: 2016-05-05T15:39:11Z-0500
LOCAL: 2016-05-05T11:39:11Z-0400

UTC in seconds: 1462462751
LOCAL in seconds: 1462448351

この場合、DST がオンになっていることに注意してください (UTC と LOCAL の間には 1 時間のタイム ゾーン オフセットの違いがあります)。

于 2016-05-05T15:38:34.630 に答える
-8
void   CTestDlg::OnBtnTest()   
{ 
HANDLE   hFile; 
WIN32_FIND_DATA   wfd; 
SYSTEMTIME   systime; 
FILETIME   localtime; 
char   stime[32];     //
memset(&wfd,   0,   sizeof(wfd)); 

if((hFile=FindFirstFile( "F:\\VC\\MFC\\Test\\Release\\Test.exe ",        &wfd))==INVALID_HANDLE_VALUE) 
{ 
char   c[2]; 
DWORD   dw=GetLastError(); 
wsprintf(c,   "%d ",   dw); 
AfxMessageBox(c);   
return   ;//
} 
FileTimeToLocalFileTime(&wfd.ftLastWriteTime,&localtime); 
FileTimeToSystemTime(&localtime,&systime); 
sprintf(stime, "%4d-%02d-%02d   %02d:%02d:%02d ", 
      systime.wYear,systime.wMonth,systime.wDay,systime.wHour, 
      systime.wMinute,systime.wSecond); 
AfxMessageBox(stime);   
} 
于 2012-01-31T08:35:19.507 に答える