2009-08-03 1 views
24

Я ищу что-то, что, как я полагал, было бы очень простым - учитывая местное время Unix в определенном часовом поясе (указанном в виде строки, например, «Америка/Нью-Йорк»), обратите внимание, что это не по местному времени), получите соответствующее значение времени в GMT. То есть, что-то вдоль линийПреобразование часовых поясов C API на Linux, кто-нибудь?

time_t get_gmt_time(time_t local_time, 
        const char* time_zone); 

Как обманчиво просто, как это кажется, ближе всего я мог бы найти, был следующий фрагмент кода страницы человека timegm в:

 #include <time.h> 
     #include <stdlib.h> 

     time_t 
     my_timegm(struct tm *tm) 
     { 
      time_t ret; 
      char *tz; 

      tz = getenv("TZ"); 
      setenv("TZ", "", 1); 
      tzset(); 
      ret = mktime(tm); 
      if (tz) 
       setenv("TZ", tz, 1); 
      else 
       unsetenv("TZ"); 
      tzset(); 
      return ret; 
     } 

Там должен быть лучше, чем это воинственно не потокобезопасная мерзость, не так ли? Правильно??

+6

Хороший вопрос. Вещи на самом деле почти такие плохие. Весь мир, кажется, думает, что вы никогда не будете заинтересованы в том, чтобы делать вид, что находитесь в другом часовом поясе, или вам нужно преобразовать в любой часовой пояс, кроме, возможно, UTC. Это проблема. –

+0

Здесь есть небольшая путаница относительно того, что означают 'time_t' и' struct tm'.Во всех UN * X-подобных системах 'time_t' /' time() 'равно" секундам с «эпохи» (UTC 0:00 01/01/1970), которые могут быть преобразованы в 'struct tm' через 'localtime()' (дающий результат с исправлением по часовому поясу) или 'gmtime()' (дающий результат в UTC), соответственно их реентерабельным версиям. 'mktime()' - обратное, преобразует 'struct tm' в' time_t '. То, что вы закодировали, это просто' mktime() 'в маскировке? –

+0

Меня тоже интересует вопрос безопасности потока. Как конвертировать в/из произвольного часового пояса, не обязательно« текущую »зону, в ** ** потокобезопасный ** способ. Не просто преобразование, но также использование strftime для печати зоны. – netjeff

ответ

1

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

Я знаю, что у Python есть понятие часовых поясов через класс tzinfo - вы можете прочитать об этом в datetime documentation. Вы можете посмотреть исходный код модуля (в tarball, он находится в модулях/datetime.c) - похоже, у вас есть документация, поэтому, возможно, вы можете что-то извлечь из этого.

+0

Да, я имел в виду прямой C API, конечно, Что касается взгляда на реализацию Python, эта идея cro я ушел, но я действительно надеялся избежать этого пути. Посмотрев на источник, а затем на документы API, я обнаружил, что дата и время Python блаженно не знают о часовых поясах - все, что он дает, - это абстрактный класс tzinfo, который вы должны реализовать, если вы хотите сделать временные преобразования между временем зоны. Есть несколько библиотек, которые реализуют это, но, действительно, действительно ли это *, что * плохо? – igor

4

От tzfile (5), какие документы файлы в/USR/доли/ZoneInfo (на моей системе) в отвратительном деталях:

кажется, что временная зона использует tzfile внутренне, но Glibc отказывается выставить его в пользовательское пространство. Это, скорее всего, , потому что стандартизованные функции более полезны и портативны и фактически задокументированы glibc.

Опять же, это, вероятно, не то, что вы ищете (например, API), но информация есть, и вы можете анализировать ее без особых проблем.

+0

Да, это звучит как единственный способ сделать эту работу такой же уродливой, как она есть. Конечно, после внедрения я получаю общий алгоритм преобразования часового пояса, который я искал в первую очередь: o Поскольку я имею дело с однопоточным раздвоенным процессом, у меня может не быть достаточной мотивации для сделайте это и, скорее всего, просто выберете TZ «взломать», упомянутую в man-странице timegm. – igor

1

Подобный ответ на Python, я могу показать вам, что R делает:

R> now <- Sys.time()  # get current time 
R> format(now)    # format under local TZ 
[1] "2009-08-03 18:55:57" 
R> format(now,tz="Europe/London") # format under explicit TZ 
[1] "2009-08-04 00:55:57" 
R> format(now,tz="America/Chicago") # format under explicit TZ 
[1] "2009-08-03 18:55:57" 
R> 

но R использует внутреннее представление, которое расширяет обычное struct tm --- см R-2.9.1/SRC/главная/datetime.c.

Тем не менее, это волосатая тема, и было бы неплохо, если бы это была стандартная библиотека. Поскольку это не возможно, ваш лучший выбор - использовать Boost Date_Time (example)

+1

Из того, что я понимаю, Date_time Boost не использует файлы zoneinfo системы и вместо этого использует свою собственную базу данных часового пояса, что делает ее не стартером IMHO. Поправьте меня, если я ошибаюсь ... – igor

0

Почему вы не можете использовать gmtime_r()? Последующие работали хорошо для меня:

int main() 
{ 
    time_t t_gmt, t_local=time(NULL); 
    struct tm tm_gmt; 

    gmtime_r(&t_local, &tm_gmt); 

    t_gmt = mktime(&tm_gmt); 

    printf("Time now is: %s", ctime(&t_local)); 
    printf("Time in GMT is: %s", ctime(&t_gmt)); 

    return 0; 
} 
+0

Удивительно то же самое, особенно после того, как плакат orig сказал «GMT». –

+2

Это довольно хорошо, но представьте, что часовой пояс компьютера равен +1, а время, которое вы хотите преобразовать, равно +2. И это не худший сценарий. Представьте себе, что компьютерный часовой пояс - это Европа/Амстердам, как в моем случае, и время, которое вы хотите преобразовать в структуру 'tm', находится в часовом поясе Европа/Афины. Это не только добавление/вычитание одного часа, но также и летнее время, которое отличается по всему миру. – NickSoft

5

Хотелось бы добавить здесь более подробно.

Если попробовать следующее:

#include <stdio.h> 
#include <time.h> /* defines 'extern long timezone' */ 

int main(int argc, char **argv) 
{ 
    time_t t, lt, gt; 
    struct tm tm; 

    t = time(NULL); 
    lt = mktime(localtime(&t)); 
    gt = mktime(gmtime(&t)); 

    printf("(t = time(NULL)) == %x,\n" 
     "mktime(localtime(&t)) == %x,\n" 
     "mktime(gmtime(&t)) == %x\n" 
     "difftime(...) == %f\n" 
     "timezone == %d\n", t, lt, gt, 
     difftime(gt, lt), timezone); 
    return 0; 
} 

вы заметите, что преобразования часовых поясов убедитесь, что:

  • mktime(localtime(t)) == t и
  • mktime(gmtime(t)) == t + timezone,
    поэтому:
  • difftime(mktime(gmtime(t)), mktime(localtime(t))) == timezone
    (последняя является глобальной переменной, инициализированной либо tzset(), либо вызовом любой функции преобразования времени).

Пример вывода из вышеперечисленного:

 
$ TZ=GMT ./xx 
(t = time(NULL)) == 4dd13bac, 
mktime(localtime(&t)) == 4dd13bac, 
mktime(gmtime(&t)) == 4dd13bac 
difftime(...) == 0.000000 
timezone == 0 

$ TZ=EST ./xx 
(t = time(NULL)) == 4dd13baf, 
mktime(localtime(&t)) == 4dd13baf, 
mktime(gmtime(&t)) == 4dd181ff 
difftime(...) == 18000.000000 
timezone == 18000 

$ TZ=CET ./xx 
(t = time(NULL)) == 4dd13bb2, 
mktime(localtime(&t)) == 4dd13bb2, 
mktime(gmtime(&t)) == 4dd12da2 
difftime(...) == -3600.000000 
timezone == -3600 

В этом смысле, вы пытаетесь «делать это в обратном направлении» - time_t трактуется как абсолютной в UN * X, то есть всегда относительны «EPOCH» (0:00 UTC 01.01.1970).

Разница между UTC и текущим часовым поясом (последний звонок tzset()) всегда находится в поле external long timezone.

Это не избавляет от уродства, вызванного окружающей средой, но вы можете сэкономить усилия, пройдя через mktime().

+1

Я бы этого не сделал, потому что смещение между часовыми поясами не является постоянным (подумайте о DST и изменениях исторического смещения). –

+0

Просьба уточнить - что _exactly_ вы бы «не сделали»? Я согласен, что механизм часовых поясов ООН * X не должен быть исторической базой данных. Тем не менее, можете ли вы привести пример, где приведенное выше дает неверный вывод? –

+0

Этот ответ на самом деле самый лучший! Большое спасибо! :) – Gandaro

1

Проблема с gmtime, localtime и их вариантами - это зависимость от переменной окружения TZ. Функции времени сначала вызывают tzset (void), который считывает TZ для определения смещений DST и т. Д. Если TZ не задано в пользовательской среде, (g) libc использует системный часовой пояс. Поэтому, если у вас есть локальная структура, например, «Европа/Париж», а ваша машина или среда установлена ​​в «Америка/Денвер», неправильное смещение будет применяться при конвертации в GMT. Все функции времени вызывают tzset (void), который читает TZ для установки char * tzname [2], длинного часового пояса (разность, в секундах, от GMT) и int daylight (boolean for DST). Установка этих параметров напрямую не влияет, потому что tzset() перезапишет их при следующем вызове по местному времени и т. Д.

Я столкнулся с той же проблемой, что и «igor» в исходном вопросе, в то время как работы setenv кажутся проблематичными (повторно Entran?). Я решил еще раз посмотреть, могу ли я изменить tzset (void) на tzset (char *), чтобы явно установить вышеупомянутые переменные. Ну, конечно, это просто плохая идея ... но, исследуя источник glibc и источник базы данных IANA TZ, я пришел к выводу, что подход setenv не так уж плох.

Во-первых, setenv только модифицирует процесс глобальный «char ** environ» (не вызывающая оболочка, поэтому «реальный» TZ не влияет). И, во-вторых, glibc фактически ставит блокировку в setenv. Недостаток заключается в том, что вызовы setenv/tzset не являются атомарными, поэтому другой поток, возможно, может записываться в TZ перед исходным вызовом tzset потока. Но хорошо реализованное приложение, которое использует потоки, должно следить за этим в любом случае.

Было бы здорово, если бы POSIX определил tzset, чтобы взять char * для поиска в обширной базе данных IANA TZ (и принять значение NULL, чтобы использовать «пользователь или система TZ /), но, в противном случае, setenv кажется будет хорошо.

Смежные вопросы