
aseq
Es una pregunta simple, pero la solución parece estar lejos de ser simple. Me gustaría saber cómo convertir de UTC a la hora local. Estoy buscando una solución en C que sea estándar y que esté más o menos garantizada para funcionar en cualquier computadora en cualquier lugar.
He leído detenidamente los siguientes enlaces pero no encuentro solución allí:
Convertir una cadena que contiene la hora local en UTC en C
Conversión entre horas locales y GMT/UTC en C/C++
He probado una serie de variaciones, como (datetime es una cadena con hora y fecha en UTC):
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp);
O
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
lt=mktime(tp);
printtime=ctime(<);
No importa lo que intente, el tiempo de impresión termina siendo el mismo que UTC.
Editar 29-11-2013: basado en la respuesta muy útil de “R” a continuación, finalmente logré crear un ejemplo de trabajo. Descubrí que funcionaba correctamente en las dos zonas horarias en las que lo probé, CET y 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);
}
Debería compilarse sin problemas en la mayoría de los sistemas. Codifiqué una hora y fecha en UTC que luego se convertirá a la hora y fecha locales.

Dachschaden
Ahm … Puede que solo sea un principiante en C, pero obtuve este ejemplo de trabajo:
#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;
}
Lo que produce esta salida:
Marca de tiempo local: 1412554119
Marca de tiempo GMT: 1412546919
Diferencia en horas: 2
Ahora tienes la diferencia entre la hora UTC y la hora local en segundos. Eso debería ser suficiente para convertirlo.
Una nota a su código, aseq: está usando malloc sin necesidad aquí (también puede establecer valores en la pila, y malloc puede ser costoso, mientras que la asignación de la pila suele ser mucho más rápida), y no lo libera. Eso es muy, muy mala práctica.
Otra cosa:
conjunto de memoria (tp, 0, tamaño de (estructura tm));
Sería mejor si pasara sizeof(*tp) (o, si pone tp en la pila, sizeof(tp)) a memset. Eso asegura que incluso si el tipo de su objeto cambia, seguirá siendo completamente memset.

Nicolás Novak
Descubrí que la solución que dio el OP no funcionó en los casos en que se aplica el horario de verano. Por ejemplo, en mi caso, en el momento actual, el horario de verano no estaba vigente, pero si configuro la fecha inicial que debe convertirse a la hora local con DST, entonces no funcionaría, es decir, la fecha de hoy es el 1/3/2018 y el horario de verano no está vigente, pero si configuro la fecha para la conversión, digamos, 1/8/2018 0:00:00 cuando DST es en efecto, la solución dada se convertiría a la hora local, pero no tendría en cuenta el horario de verano. Encontré que inicializando e0
a la fecha y hora de la cadena de fecha/hora inicial y su miembro tm_isdst
para -1
resuelve el problema. Luego creé el siguiente programa con funciones complementarias que puedes incluir en tu código. El formato inicial de fecha y hora es el mismo que usa MySQL, porque lo necesitaba para tales fines.
#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;
}

Denés Borsos
//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;}
}
}

zhaixiaohu
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);
}
¿Podemos asumir POSIX (ya que usas
strptime
) o simplemente C?– R.. GitHub DEJA DE AYUDAR A ICE
31 de enero de 2012 a las 8:52
Lo siento, me lo perdí, sí, puedes asumir POSIX.
– aseq
2 de febrero de 2012 a las 9:41