簡単な質問ですが、解決策は決して単純ではないようです。 UTCから現地時間に変換する方法を知りたいのですが。私は、標準であり、あらゆる場所のあらゆるコンピューターで動作することが多かれ少なかれ保証されているCのソリューションを探しています。
次のリンクを注意深く読みましたが、解決策が見つかりません。
私は次のようないくつかのバリエーションを試しました(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(<);
何を試しても、printtimeはUTCと同じになります。
2013年11月29日編集:以下の「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で時刻と日付をハードコーディングしました。これは、現地の時刻と日付に変換されます。
POSIX(したがって、_time_t
_のPOSIX仕様をエポックからの秒数と見なすことができる場合)、最初に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
_の_struct tm
_フィールドに追加し、mktime
を呼び出して正規化します。
編集:実際には、POSIXの依存関係は、_(time_t)0
_が対応する既知のエポックを持つことだけです。本当に必要な場合は、おそらくそれを回避する方法を見つけることができます...たとえば、_time_t
_ 0でgmtime
とlocaltime
の両方への呼び出しを使用します。
編集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時間を秒に変換できます。このコードはPOSIXにまったく依存していませんが、_diff_tm
_とseconds-since-the-Epoch値の組み合わせによって返されるオフセットがint
をオーバーフローしないことを前提としています。これに対する修正は、_long long
_オフセットと、_INT_MAX/2
_以下(または_INT_MIN/2)
_未満)の増分を追加し続けるループを使用し、mktime
を呼び出して再正規化することです。オフセットが0に達します。
ああ...私は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と現地時間の差が秒単位で表示されます。それを変換するにはそれで十分なはずです。
コードへの1つの注意、aseq:ここでは必要なしにmallocを使用しています(スタックに値をmemsetすることもできます。また、スタックの割り当てがはるかに高速である一方で、mallocは高額になる可能性があります)、解放しません。それは非常に、非常に悪い習慣です。
別物:
memset(tp、0、sizeof(struct tm));
Sizeof(* tp)(または、tpをスタックに置く場合はsizeof(tp))をmemsetに渡すと、より適切に実行されます。これにより、オブジェクトのタイプが変更された場合でも、完全にmemsetになります。
要約すると、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;
}
//working stand alone function adjusting UTC to local date and time
//globals(unsigned integers): gps.Mth, gps.Yr, gps.Hm (eg:2115 for 21:15)
//adjust date and time according to UTC
//tz(timezone) eg: 1100, for 11 hours, tzdir: 1 forward, 0 backwards
void AdjustUTCToTimeZone(u16 tz, u8 tzdir){
u8 maxDayInAnyMonth[13] = {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //gps.Mth 1-12 (not zero)
if(gps.Yr%4==0){maxDayInAnyMonth[2]=29;}//adjust for leapyear
u8 maxDayUtcMth =maxDayInAnyMonth[gps.Mth];
u8 maxDayPrevMth=maxDayInAnyMonth[gps.Mth-1];
if(!maxDayPrevMth){maxDayPrevMth=31;} //month before utc month
u16 hr=(gps.Hm/100)*100;u16 m=gps.Hm-hr; //2115 --> 2100 hr and 15 min
if(tzdir){//adjusting forwards
tz+=gps.Hm;
if(tz>2400){gps.Hm=tz-2400;gps.Day++; //spill over to next day
if(gps.Day>maxDayUtcMth){ gps.Day=1;gps.Mth++; //spill over to next month
if(gps.Mth>12){gps.Mth=1; gps.Yr++; //spill over to next year
}
}
}else{gps.Hm=tz;}
}else{//adjusting backwards
if(tz>gps.Hm){gps.Hm=(2400-(tz-hr))+m;gps.Day--; // back to previous day
if(gps.Day==0){ //back to previous month
gps.Mth--;gps.Day=maxDayPrevMth;
if(!gps.Mth){gps.Mth=12; //back to previous year
gps.Yr--;
}
}
}else{gps.Hm-=tz;}
}
}
それよりも簡単だと思います。 time.hは、次の3つの変数を定義します。
extern int daylight;
extern long timezone;
extern char *tzname[];
呼び出したときにTZenv変数に基づいてロードされます
tzset();
協定世界時がある場合
struct tm date;
date.tm_isdst = 0;
mktimeを使用してtime_tに変換します
time_t utc = mktime( &date );
次に、それを現地時間に変換します
time_t local = utc - timezone + ( daylight?3600:0 );
タイムゾーンは、現在のタイムゾーンでUTCから離れている秒数であり、夏時間は1であり、夏時間が有効であることを示し、ゼロは無効であることを示します。
ちょっとした注意:これをマイクロコントローラー用にコーディングしてクロスコンパイルしたとき、time.hはこれらの変数を最初のアンダースコアで定義しました。
DSTが適用される場合、OPが提供するソリューションが機能しないことがわかりました。たとえば、私の場合、現時点ではDSTは有効ではありませんでしたが、現地時間に変換する必要がある最初の日付を設定した場合with DST、つまり今日の日付では機能しません。は2018年3月1日で、DSTは有効ではありませんが、変換の日付を、たとえばDST isが有効なときに2018年8月1日0:00:00に設定すると、与えられたソリューションは現地時間に変換されますが、DSTは考慮されません。 e0
を最初の日付/時刻文字列の日付と時刻に初期化し、そのメンバーtm_isdst
を-1
に初期化すると問題が解決することがわかりました。次に、コードに含めることができる補完的な関数を使用して、次のプログラムを作成しました。日付と時刻の初期形式は、MySQLが使用するものと同じです。これは、そのような目的で必要だったためです。
#include <stdio.h>
#include <time.h>
#include <string.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;
}
void localToUTC(char *buf, const char *localTime) {
struct tm tp;
strptime(localTime, "%Y-%m-%d %H:%M:%S", &tp);
tp.tm_isdst = -1;
time_t utc = mktime(&tp);
struct tm res = *gmtime(&utc);
strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &res);
}
void utcToLocal(char *buf, const char *utcTime) {
struct tm tp;
strptime(utcTime, "%Y-%m-%d %H:%M:%S", &tp);
tp.tm_isdst = -1;
time_t utc = mktime(&tp);
struct tm e0 = { .tm_year = tp.tm_year, .tm_mday = tp.tm_mday, .tm_mon = tp.tm_mon, .tm_hour = tp.tm_hour, .tm_isdst = -1 };
time_t pseudo = mktime(&e0);
struct tm e1 = *gmtime(&pseudo);
e0.tm_sec += utc - diff_tm(&e1, &e0);
time_t local = e0.tm_sec;
struct tm localt = *localtime(&local);
strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &localt);
}
int main(void) {
char mytime_1[20] = "2018-02-28 13:00:00";
char utctime_1[20], back_1[20];
localToUTC(utctime_1, mytime_1);
utcToLocal(back_1, utctime_1);
printf("My time: %s\n", mytime_1);
printf("UTC time: %s\n", utctime_1);
printf("Back: %s\n", back_1);
printf("-------------------------------------------\n");
char mytime_2[20] = "2018-07-28 17:00:00";
char utctime_2[20], back_2[20];
localToUTC(utctime_2, mytime_2);
utcToLocal(back_2, utctime_2);
printf("My time: %s\n", mytime_2);
printf("UTC time: %s\n", utctime_2);
printf("Back: %s\n", back_2);
printf("-------------------------------------------\n");
return 0;
}
簡単で効果的な方法:タイムゾーンとUTCの間の秒数を加算(または減算)します(夏時間を考慮)。
1分前の2017年12月30日、UTCから7時間遅れている米国山岳部標準時(DSTなし)で問題なく機能した例として、
time_t current_time_UTC;
time_t current_time_MST;
struct tm *current_broken_time_MST;
uint32_t seven_hours_in_seconds = 25200; // Get this any way you want
current_time_UTC = time (NULL); // UTC
current_time_MST = current_time_UTC - seven_hours_in_seconds; // MST
current_broken_time_MST = localtime (¤t_time_MST); // MST
楽しい。
@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時間のタイムゾーンオフセットの違いがあります)。
これを試して、出力をテストしてください:utcEpochTime:1487652668、localEpochTime:1487699488、diff:46800
$ python
>>>46800 / 60 / 60
13
私のタイムゾーンはUTC + 8なので、差分は13時間です。これは良いことです。
#include <stdio.h>
#include <time.h>
int main(int argc, char *argv[])
{
time_t utcEpochTime = time(0);
time_t localEpochTime = 0;
struct tm tm = {0};
localtime_r(&utcEpochTime, &tm);
tm.tm_isdst = -1;
localEpochTime = timegm(&tm);
printf("utcEpochTime: %d, localEpochTime: %d, diff: %d\n", (int)utcEpochTime, (int)localEpochTime, (int)(localEpochTime - utcEpochTime));
return 0;
}