Std:: mktime и информация о часовом поясе
Я пытаюсь преобразовать информацию о времени, которую я представляю в виде строки UTC, в timestamp, используя std::mktime
в С++. Моя проблема заключается в том, что в <ctime>
/<time.h>
нет функции для преобразования в UTC; mktime вернет метку времени только по местному времени.
Итак, мне нужно выяснить смещение часового пояса и принять его во внимание, но я не могу найти независимый от платформы способ, который не включает перенос всего кода на boost::date_time
. Есть ли какое-то простое решение, которое я упустил?
Ответы
Ответ 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();
}