Подтвердить что ты не робот

Std:: mktime и информация о часовом поясе

Я пытаюсь преобразовать информацию о времени, которую я представляю в виде строки UTC, в timestamp, используя std::mktime в С++. Моя проблема заключается в том, что в <ctime>/<time.h> нет функции для преобразования в UTC; mktime вернет метку времени только по местному времени.

Итак, мне нужно выяснить смещение часового пояса и принять его во внимание, но я не могу найти независимый от платформы способ, который не включает перенос всего кода на boost::date_time. Есть ли какое-то простое решение, которое я упустил?

4b9b3361

Ответ 1

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

Windows tzset

Можно также попробовать посмотреть различные самодельные utc-mktimes, mktime-utcs и т.д.

Ответ 2

timestamp = mktime(&tm) - _timezone;

или независимый от платформы способ:

 timestamp = mktime(&tm) - timezone;

Если вы посмотрите в источнике mktime():

http://www.raspberryginger.com/jbailey/minix/html/mktime_8c-source.html

в строке 00117 время, преобразованное в локальное время:

seconds += _timezone;

Ответ 3

mktime() использует tzname для определения временной зоны. tzset() инициализирует переменную tzname из переменной окружения TZ. Если переменная TZ появляется в среде, но ее значение пуст или ее значение не может быть правильно интерпретировано, используется UTC.

Портативная (не потоковая) версия в соответствии с справочная страница 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;
   }

Eric S Raymond имеет поточную версию, опубликованную в его статье Время, часы и программирование календаря в C

time_t my_timegm(register struct tm * t)
/* struct tm to seconds since Unix epoch */
{
    register long year;
    register time_t result;
#define MONTHSPERYEAR   12      /* months per calendar year */
    static const int cumdays[MONTHSPERYEAR] =
        { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

    /*@ +matchanyintegral @*/
    year = 1900 + t->tm_year + t->tm_mon / MONTHSPERYEAR;
    result = (year - 1970) * 365 + cumdays[t->tm_mon % MONTHSPERYEAR];
    result += (year - 1968) / 4;
    result -= (year - 1900) / 100;
    result += (year - 1600) / 400;
    if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) &&
        (t->tm_mon % MONTHSPERYEAR) < 2)
        result--;
    result += t->tm_mday - 1;
    result *= 24;
    result += t->tm_hour;
    result *= 60;
    result += t->tm_min;
    result *= 60;
    result += t->tm_sec;
    if (t->tm_isdst == 1)
        result -= 3600;
    /*@ -matchanyintegral @*/
    return (result);
}

Ответ 4

Если вы пытаетесь сделать это в многопоточной программе и не хотите иметь дело с блокировкой и разблокировкой мьютексов (если вы используете метод переменной среды, который вам нужен), существует функция, называемая timegm, которая делает это, Он не переносится, поэтому вот источник: http://trac.rtmpd.com/browser/trunk/sources/common/src/platform/windows/timegm.cpp

int is_leap(unsigned y) {
    y += 1900;
    return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
}

time_t timegm (struct tm *tm)
{
    static const unsigned ndays[2][12] = {
        {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
        {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };
    time_t res = 0;
    int i;

    for (i = 70; i < tm->tm_year; ++i)
        res += is_leap(i) ? 366 : 365;

    for (i = 0; i < tm->tm_mon; ++i)
        res += ndays[is_leap(tm->tm_year)][i];
    res += tm->tm_mday - 1;
    res *= 24;
    res += tm->tm_hour;
    res *= 60;
    res += tm->tm_min;
    res *= 60;
    res += tm->tm_sec;
    return res;
}

Ответ 5

Вот простой, проверенный, надеюсь, переносимый фрагмент кода, преобразующийся от struct tm до секунд с начала настраиваемого года UTC без временной смены часового пояса.

// Conversion from UTC date to second, signed 64-bit adjustable epoch version.
// Written by François Grieu, 2015-07-21; public domain.

#include <time.h>                   // needed for struct tm
#include <stdint.h>                 // needed for int_least64_t
#define MY_EPOCH    1970            // epoch year, changeable
typedef int_least64_t my_time_t;    // type for seconds since MY_EPOCH

// my_mktime  converts from  struct tm  UTC to non-leap seconds since 
// 00:00:00 on the first UTC day of year MY_EPOCH (adjustable).
// It works since 1582 (start of Gregorian calendar), assuming an
// apocryphal extension of Coordinated Universal Time, until some
// event (like celestial impact) deeply messes with Earth.
// It strive to be strictly C99-conformant.
//
// input:   Pointer to a  struct tm  with field tm_year, tm_mon, tm_mday,
//          tm_hour, tm_min, tm_sec set per  mktime  convention; thus
//          - tm_year is year minus 1900;
//          - tm_mon is [0..11] for January to December, but [-2..14] 
//            works for November of previous year to February of next year;
//          - tm_mday, tm_hour, tm_min, tm_sec similarly can be offset to
//            the full range [-32767 to 32767].
// output:  Number of non-leap seconds since beginning of the first UTC
//          day of year MY_EPOCH, as a signed at-least-64-bit integer.
//          The input is not changed (in particular, fields tm_wday,
//          tm_yday, and tm_isdst are unchanged and ignored).
my_time_t my_mktime(const struct tm * ptm) {
    int m, y = ptm->tm_year+2000;
    if ((m = ptm->tm_mon)<2) { m += 12; --y; }
// compute number of days within constant, assuming appropriate origin
#define MY_MKTIME(Y,M,D) ((my_time_t)Y*365+Y/4-Y/100*3/4+(M+2)*153/5+D)
    return ((( MY_MKTIME( y           ,  m, ptm->tm_mday)
              -MY_MKTIME((MY_EPOCH+99), 12, 1           )
             )*24+ptm->tm_hour)*60+ptm->tm_min)*60+ptm->tm_sec;
#undef MY_MKTIME // this macro is private
    }

Ключевые наблюдения, позволяющие значительно упростить по сравнению с кодом в этом и который отвечает:

  • число месяцев с марта, за все месяцы, кроме одного, до того, как это происходило, повторяется с циклом в 5 месяцев на общую сумму 153 дня, чередуя 31 и 30 дней, так что в течение любого месяца и без учета високосных лет количество дней поскольку в предыдущем феврале можно вычислить (в пределах константы) с помощью добавления соответствующей константы, умножения на 153 и целочисленного деления на 5;
  • Коррекция в днях, определяющая правило для високосного года по годам в несколько сот 100 (которые по исключению из правил с несколькими четырьмя не являются прыжками, за исключением случаев, когда они являются множественными из 400) могут быть вычислены (в пределах константы ) путем добавления соответствующего постоянного, целочисленного деления на 100, умножения на 3 и целочисленного деления на 4;
  • мы можем вычислить коррекцию для любой эпохи, используя ту же формулу, которую мы используем в основном вычислении, и можем сделать это с помощью макроса, чтобы эта коррекция вычислялась во время компиляции.

Вот еще одна версия, не требующая 64-битной поддержки, заблокированной до 1970 года.

// Conversion from UTC date to second, unsigned 32-bit Unix epoch version.
// Written by François Grieu, 2015-07-21; public domain.

#include <time.h>                   // needed for struct tm
#include <limits.h>                 // needed for UINT_MAX
#if UINT_MAX>=0xFFFFFFFF            // unsigned is at least 32-bit
typedef unsigned      my_time_t;    // type for seconds since 1970
#else
typedef unsigned long my_time_t;    // type for seconds since 1970
#endif

// my_mktime  converts from  struct tm  UTC to non-leap seconds since 
// 00:00:00 on the first UTC day of year 1970 (fixed).
// It works from 1970 to 2105 inclusive. It strives to be compatible
// with C compilers supporting // comments and claiming C89 conformance.
//
// input:   Pointer to a  struct tm  with field tm_year, tm_mon, tm_mday,
//          tm_hour, tm_min, tm_sec set per  mktime  convention; thus
//          - tm_year is year minus 1900
//          - tm_mon is [0..11] for January to December, but [-2..14] 
//            works for November of previous year to February of next year
//          - tm_mday, tm_hour, tm_min, tm_sec similarly can be offset to
//            the full range [-32767 to 32768], as long as the combination
//            with tm_year gives a result within years [1970..2105], and
//            tm_year>0.
// output:  Number of non-leap seconds since beginning of the first UTC
//          day of year 1970, as an unsigned at-least-32-bit integer.
//          The input is not changed (in particular, fields tm_wday,
//          tm_yday, and tm_isdst are unchanged and ignored).
my_time_t my_mktime(const struct tm * ptm) {
    int m, y = ptm->tm_year;
    if ((m = ptm->tm_mon)<2) { m += 12; --y; }
    return ((( (my_time_t)(y-69)*365u+y/4-y/100*3/4+(m+2)*153/5-446+
        ptm->tm_mday)*24u+ptm->tm_hour)*60u+ptm->tm_min)*60u+ptm->tm_sec;
    }

Ответ 6

У меня такая же проблема в прошлый день и поиск документа "man mktime":

Функции mktime() и timegm() преобразуют время разбивки (в структуре, на которое указывает * timeptr), в значение времени с той же кодировкой, что и    значений, возвращаемых функцией времени (3) (то есть секунд от эпохи, UTC). Функция mktime() интерпретирует структуру ввода в соответствии с    текущая настройка часового пояса (см. tzset (3)). Функция timegm() интерпретирует структуру ввода как представление универсального скоординированного времени (UTC).

Short:

Вы должны использовать timegm вместо использования mktime.

Привет,

Пай

Ответ 7

Используйте _ mkgmtime, он заботится обо всем.

Ответ 8

Структура tm, используемая mktime, имеет поле часового пояса.
Что произойдет, если вы поместите "UTC" в поле темновой зоны?

http://www.delorie.com/gnu/docs/glibc/libc_435.html

Ответ 9

Я использовал следующий код для создания метки времени в UTC:

#include <iostream>
#include <sstream>
#include <chrono>
using namespace std;

string getTimeStamp() {
    time_t now = time(NULL);
    tm* gmt_time = gmtime(&now);
    ostringstream oss;
    oss << put_time(gmt_time, "%Y-%m-%d %H:%M:%S");
    return oss.str();
}