2010-03-09 4 views
6

инфо-страницы для команды GNU date содержит этот пример:Как программно преобразовать время из одного часового пояса в другое в C?

Например, с date команды GNU вы можете ответить на этот вопрос «Сколько времени в Нью-Йорке, когда часы Париж показывает 6 : 30am октября 31, 2004? " с использованием даты начала с `TZ =«Европа/Париж»« как показано в следующей оболочки расшифровку:

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

В этом примере „--date“ операнд начинается с его собственной»TZ ' настройка, поэтому остальная часть этого операнда обрабатывается в соответствии с правилами ' Европа/Париж ', обрабатывая строку 2004-10-31 06:30, как будто она находилась в Париже. Однако, поскольку выход команды date составляет , обработанный в соответствии с правилами часового пояса , он использует время в Нью-Йорке . (Париж был обычно шесть часов вперед в Нью-Йорке в 2004 году, но это пример относится к краткому Хэллоуин период, когда разрыв в пять часов.)

Я пытаюсь сделать по существу то же самое программно в C без вызова программы date миллионы раз. В основном я ищу способ принять произвольную дату и время в одном часовом поясе и преобразовать его в эквивалентную дату и время в другой часовой пояс либо напрямую, либо путем преобразования в и из UTC. Мне не нужны форматы ввода и вывода, если я могу ими управлять, используя стандартные функции (strftime/strptime/mktime/etc).

Программа date, как представляется, выполняет это, используя сложные процедуры внутри пакета coreutils. Я ищу способ сделать это на C, используя стандартные подпрограммы POSIX/Linux или внешнюю библиотеку. Я посмотрел на zoneinfo совсем немного, что казалось многообещающим, но я не могу найти никаких библиотек, чтобы делать с ними что-нибудь полезное.

ответ

2

Для времени:

  • Получить время по Гринвичу с gmtime
  • Добавить/вычитать часы из time_t.tm_hour
  • Использование mktime перенормировать

Расчет дата будет похож но немного сложнее.

+1

Существует ряд тонких вопросов, связанных с ручными расчетами, которые я пытаюсь избежать, например, с помощью DST. Эта информация содержится в базе данных zoneinfo, но не настолько полезной для меня до сих пор. –

+1

Например, эта работа особенно плохо в Венесуэле - это официально UTC-4: 30 – Quintus

3

Я придумал решение, которое, похоже, работает на Linux с glibc, я не знаю, насколько переносимо это поведение, любые комментарии о переносимости или лучший способ сделать это можно было бы приветствовать.

convert_time.с (не проверка Нет ошибок для ясности):

#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; 
} 

Первый аргумент является временной зоны источника (на самом деле «TZ = Timezone», чтобы перейти к PutEnv), то второй аргумент это время в указанном формате, последний аргумент это часовой пояс назначения. Я использую имена зон зоны zoneinfo, которые glibc поддерживает с использованием базы данных zoneinfo.

Результаты нескольких ДСТ случаев испытания угловых соответствуют результатам из эквивалентной date команды, а также this сайта, который использует базу данных ZoneInfo:

$ ./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

Yuck, но что вы можете сделать? Это именно то, что говорится в man-странице: http://www.kernel.org/doc/man-pages/online/pages/man3/timegm.3.html. Это просто ужасно небезопасно. – smparkes

0

Я был на самом деле искал тот же ответ вы сделали, и это решение, которое я придумал. Следующая примерная программа берет локальное системное время и преобразует ее в любой часовой пояс, который вы хотите.

// 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; 
} 
Смежные вопросы