C: Как напечатать определенное значение `time` в определенном часовом поясе (по смещению)? - PullRequest
4 голосов
/ 23 февраля 2011

Я пишу приложение на C, которое анализирует файлы данных, записанные внешней программой (над которыми у меня нет контроля).Он хранит двоичные данные, одно поле которых представляет собой время в стандартном формате «эпохи» UNIX (секунды с 1 января 1970 года, UTC).

Другое поле - это часовой пояс, который сохраняется в виде смещения в секундах от UTC.

Круто, у меня есть все, что нужно, чтобы сделать строку даты / времени, представляющую эту информацию в часовом поясе, в котором она была записана, верно?Ну ... это не так, и / или я не уверен, как это сделать.

Я свел вещи к довольно простому тестовому примеру:

#include <stdio.h>
#include <time.h>

int main(void)
{
    time_t t;
    struct tm *tm;
    char buf[BUFSIZ];

    int offset = 4980; /* slightly bizarre, just to test this - an hour
                        * and 23 minutes ahead of UTC */

    t = time(NULL);
    tm = localtime(&t);

    strftime(buf, BUFSIZ, "%FT%T%z", tm);
    printf("before: %s\n", buf);

    /* since we're not telling localtime anything different,
     * compensate here (by subtracting applied offset, and adding
     * desired one): */
    t += offset - tm->tm_gmtoff;
    tm = localtime(&t);

    tm->tm_zone = "XYZ"; // not used -- but it was in an earlier version
    tm->tm_gmtoff = offset;

    // on macos, I used to also have %+, which referenced tm_zone
    strftime(buf, BUFSIZ, "%FT%T%z", tm);
    printf("after:  %s\n", buf);

    return 0;
}

Когда я запускаю это на MacOS X 10.6, я получаю:

before: 2011-02-23T00:53:04-0800
after:  2011-02-23T10:16:04-0800

То, что я ожидал (и фактически получал, на Linux-коробке), было бы:

before: 2011-02-23T00:53:04-0800
after:  2011-02-23T10:16:04+0123

Нужно ли менять переменную окружения TZ (и, возможно, вызывать tzset)?Кажется, что должен быть способ манипулировать структурами данных и получить правильные вещи, но вышеприведенное определенно не работает (в любом случае, на MacOS X 10.6 - прекрасно работает на Linux).

КакВ качестве обходного пути, я полагаю, что я могу удалить% z из моей строки формата и создать эту часть самостоятельно.

В идеале, однако, я бы хотел иметь модификацию struct tm или какую-то другую функциювызов, который я могу использовать (например, strftime, но с дополнительным параметром или чем-то, или, возможно, вместо этого альтернативной формой локального времени), это заставит вещи делать правильные вещи.

Поскольку Linux, кажется, ведет себя (хотядаже там, вышеупомянутое решение не совсем идеально, потому что я выдумал свое значение time_t; я бы предпочел иметь параметр, который изменяет способ вычисления struct tm), это то, что я должен сообщить какошибка в MacOS?

Альтернативно, есть ли другой набор библиотечных подпрограмм, которые я мог бы вызвать, даже если для этого требуется сторонняя программа (что-то из GNU fолкс, я представляю) библиотека?Я бы предпочел придерживаться C, хотя я бы рассмотрел параметры ObjC или C ++.

Ответы [ 3 ]

2 голосов
/ 25 февраля 2011

Я предпочитаю использовать mktime() вместо изменения значения time_t. Однако это относится к смещению TZ (так же, как к решению localtime()), поэтому требуется реализация mkgmtime().

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

static void set_timezone(char *tz)
{
  static char var[1024];

  snprintf(var, sizeof(var), "TZ=%s", tz);
  putenv(var);
  tzset();
}

static time_t mkgmtime(struct tm *tm)
{
  char var[1024];
  time_t t;

  snprintf(var, sizeof(var), "%s", getenv("TZ") ? getenv("TZ") : "");

  set_timezone("GMT0");

  t = mktime(tm);

  set_timezone(var);

  return t;
}

int main(void)
{
  time_t t;
  struct tm tm;
  char buf[BUFSIZ];
  int offset = 4980; /* slightly bizarre, just to test this - an hour
                      * and 23 minutes ahead of UTC */

  t = time(NULL);
  tm = *localtime(&t);
  strftime(buf, BUFSIZ, "%FT%T%z", &tm);
  printf("before: %s\n", buf);

  tm = *gmtime(&t);
  tm.tm_sec += offset;
  mkgmtime(&tm);

  strftime(buf, BUFSIZ, "%FT%T%z", &tm);
  printf("after:  %s\n", buf);

  return 0;
}
0 голосов
/ 02 октября 2012

Лично мне нравится вставлять небольшие скрипты на Python в мой bash-код. В Python есть много мощных готовых библиотек для функций, которые вам необходимы.

Вы можете встроить код Python в скрипт bash следующим образом (Предполагается, что Python установлен)

python << END
from pytz import timezone    

south_africa = timezone('Africa/Johannesburg')
sa_time = datetime.now(south_africa)
print sa_time.strftime('%Y-%m-%d_%H-%M-%S')
END

Вы можете изменить настройку часового пояса, если хотите отображать ее в разных часовых поясах. Посмотрите на http://pytz.sourceforge.net/ для более подробной информации.

0 голосов
/ 23 февраля 2011

Я думаю, что лучшим подходом было бы

  1. считывание вашего значения в переменную time_t;

  2. установка переменной окружения TZследуя этим рекомендациям.См. Также на этой странице для получения информации о том, как вы должны указывать TZ;

  3. invoke localtime().

Это должно быть гарантировано, хотя я не могу протестировать его на платформе MacOS.

...