Как программно преобразовать время из одного часового пояса в другой в C? - PullRequest
6 голосов
/ 10 марта 2010

Информационные страницы для команды GNU date содержат этот пример:

Например, с GNU date Команда, вы можете ответить на вопрос «Сколько времени в Нью-Йорке, когда Парижские часы показывают 6:30 утра в октябре 31, 2004? ", Используя начало даты с `TZ =" Europe / Paris "', как показано в следующая стенограмма оболочки:

 $ 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 , которая казалась многообещающей, но я не могу найти библиотек, которые могли бы с ней что-то сделать полезное.

Ответы [ 3 ]

3 голосов
/ 10 марта 2010

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

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

#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.

Результаты нескольких тестовых углов DST соответствуют результатам эквивалентной команды date, а также этого сайта, который использует базу данных 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)
2 голосов
/ 10 марта 2010

За время:

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

Расчет даты будет аналогичным, но немного более сложным.

0 голосов
/ 02 декабря 2012

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

// 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...