2010-03-09 9 views
6

Las páginas de información para el comando GNU date contiene este ejemplo:¿Cómo convertir programáticamente una hora de una zona horaria a otra en C?

Por ejemplo, con el GNU date de comandos que puede responder a la pregunta "¿Qué hora es en Nueva York, cuando un reloj París muestra 6 : 30 am en octubre 31, 2004? " mediante el uso de una fecha de inicio con `TZ = "Europa/París" 'como se muestra en la siguiente transcripción shell:

$ export TZ="America/New_York" 
$ date --date='TZ="Europe/Paris" 2004-10-31 06:30' 
Sun Oct 31 01:30:00 EDT 2004 

En este ejemplo, el '--date' operando comienza con su propia' TZ ' configuración, por lo que el resto de ese operando se procesa de acuerdo con ' Europa/París 'reglas, tratando el cadena 2004-10-31 06:30 como si fuera en París. Sin embargo, dado que la salida del comando date es procesada de acuerdo con las reglas generales de zona horaria , usa la hora de Nueva York . (París era normalmente seis horas por delante de Nueva York en 2004, pero esta ejemplo se refiere a un breve período de Halloween cuando la diferencia fue de cinco horas.)

que estoy tratando de lograr esencialmente la misma cosa mediante programación en C sin llamar al programa date millones de veces. Básicamente, estoy buscando una manera de tomar una fecha y hora arbitraria en una zona horaria y convertirla a la fecha y hora equivalentes en otra zona horaria, ya sea directamente o a través de la conversión hacia y desde UTC. No me importan los formatos del tiempo de entrada y salida, siempre que pueda manipularlos usando funciones estándar (strftime/strptime/mktime/etc).

El programa date parece lograr esto usando rutinas complejas internas al paquete coreutils, estoy buscando una forma de hacerlo en C usando rutinas POSIX/Linux estándar o una biblioteca externa. Miré a zoneinfo un poco que parecía prometedor pero no puedo encontrar ninguna biblioteca para hacer algo útil con él.

Respuesta

2

Por tiempo:

  • Obtener la hora GMT con gmtime
  • /menos las horas de time_t.tm_hour
  • Uso mktime a renormalizar

El cálculo de la fecha será similar pero un poco más complicado.

+1

Hay una serie de cuestiones sutiles relacionadas con los cálculos manuales que trato de evitar, tales como tener que manejar como DST. Esta información está contenida en la base de datos de zoneinfo, pero no de una manera que me haya resultado útil hasta ahora. –

+1

Por ej., Esto funciona especialmente mal en Venezuela, que es oficialmente UTC-4: 30 – Quintus

3

Se me ocurrió una solución que parece funcionar en Linux con glibc, no sé qué tan portátil es este comportamiento, cualquier comentario sobre la portabilidad o una mejor manera de hacerlo sería bienvenido.

convert_time.c (verificación Sin error para mayor claridad):

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

int main (int argc, char *argv[]) 
{ 
    struct tm mytm = {0}; 
    time_t mytime; 
    char buf[100]; 

    mytm.tm_isdst = -1; 
    putenv(argv[1]); 
    tzset(); 
    strptime(argv[2], "%Y-%m-%d %H:%M", &mytm); 
    mytime = mktime(&mytm); 

    putenv(argv[3]); 
    tzset(); 
    localtime_r(&mytime, &mytm); 
    strftime(buf, 100, "%a, %d %b %Y %H:%M:%S %z(%Z)", &mytm); 
    puts(buf); 

    return 0; 
} 

El primer argumento es la zona horaria de origen (en realidad "TZ = Zona horaria" para pasar a putenv), el segundo argumento es el momento en el formato especificado, el último argumento es la zona horaria de destino. Estoy utilizando los nombres de zona horaria zoneinfo que glibc admite el uso de la base de datos zoneinfo.

Los resultados de varios casos de esquina prueba DST corresponden a los resultados del comando equivalente date, así como this sitio que utiliza la base de datos de información de zonas:

$ ./convert_time "TZ=America/New_York" "2005-05-31 06:30" "TZ=America/Indiana/Indianapolis" 
Tue, 31 May 2005 05:30:00 -0500(EST) 

$ ./convert_time "TZ=America/New_York" "2006-05-31 06:30" "TZ=America/Indiana/Indianapolis" 
Wed, 31 May 2006 06:30:00 -0400(EDT) 

$ ./convert_time "TZ=Europe/Paris" "2004-10-30 06:30" "TZ=America/New_York" 
Sat, 30 Oct 2004 00:30:00 -0400(EDT) 

$ ./convert_time "TZ=Europe/Paris" "2004-10-31 06:30" "TZ=America/New_York" 
Sun, 31 Oct 2004 01:30:00 -0400(EDT) 

$ ./convert_time "TZ=Europe/Paris" "2004-11-01 06:30" "TZ=America/New_York" 
Mon, 01 Nov 2004 00:30:00 -0500(EST) 
+1

¿Qué puedes hacer? Esto es exactamente lo que dice la página man: http://www.kernel.org/doc/man-pages/online/pages/man3/timegm.3.html. Es horriblemente inseguro. – smparkes

0

En realidad estaba buscando la misma respuesta que hizo, y esta es la solución que se me ocurrió. El siguiente programa de ejemplo toma la hora del sistema local y la convierte a cualquier zona horaria que desee.

// compile: gcc snippet.c 
// usage: ./a.out Europe/Berlin Pacific/Nauru Asia/Hong_Kong Asia/Kabul 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <time.h> 
#include <errno.h> 

int main(int argc, char *argv[]) 
{ 
     char buf[80]; 
     time_t tt = time(NULL); 
     struct tm tm; 
     int i; 

     if (!localtime_r(&tt, &tm)) { 
       printf("Could not convert time_t to tm struct. %s\n", strerror(errno)); 
       return 1; 
     } 

     printf("time_t: %ld\n", tt); 
     printf("timezone: %ld, %s/%s, daylight:%d\n", timezone, tzname[0], tzname[1], daylight); 
     strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z %Z", &tm); 
     printf("time: %s\n", buf); 

     for (i = 1; i < argc; ++i) { 
       printf("\ninput: %s\n", argv[i]); 

       snprintf(buf, sizeof(buf), "TZ=%s", argv[i]); 
       putenv(buf); 
       tzset(); 
       printf("\ttimezone: %ld, %s/%s, daylight:%d\n", timezone, tzname[0], tzname[1], daylight); 

       if (!localtime_r(&tt, &tm)) { 
         printf("Could not convert time_t to tm struct. %s\n", strerror(errno)); 
         return 1; 
       } 

       strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z %Z", &tm); 
       printf("\ttime: %s\n", buf); 
     } 

     return 0; 
} 
Cuestiones relacionadas