c++ - son - std:: mktime y la información de la zona horaria
zona horaria utc-5 (9)
La estructura tm utilizada por mktime tiene un campo de zona horaria.
¿Qué sucede si coloca ''UTC'' en el campo timzone?
Estoy tratando de convertir una información de tiempo que revela como una cadena UTC a una marca de tiempo usando std::mktime
en C ++. Mi problema es que en <ctime>
/ <time.h>
no hay ninguna función para convertir a UTC; mktime solo devolverá la marca de tiempo como hora local.
Así que tengo que averiguar el offset de la zona horaria y tenerlo en cuenta, pero no puedo encontrar una forma independiente de la plataforma que no implique portar todo el código para boost::date_time
. ¿Hay alguna solución fácil que he pasado por alto?
mktime asume que el valor de la fecha se encuentra en la zona horaria local. Por lo tanto, puede cambiar la variable de entorno de la zona horaria de antemano (setenv) y obtener la zona horaria UTC.
También puede probar mirar utc-mktimes, mktime-utcs caseros, etc.
timestamp = mktime(&tm) - _timezone;
o plataforma de manera independiente:
timestamp = mktime(&tm) - timezone;
Si miras en la fuente de mktime ():
http://www.raspberryginger.com/jbailey/minix/html/mktime_8c-source.html
en la línea 00117 el tiempo convertido a la hora local:
seconds += _timezone;
Tengo el mismo problema el último día y al buscar en el documento "man mktime":
Las funciones mktime () y timegm () convierten el tiempo de ruptura (en la estructura señalada por * timeptr) en un valor de tiempo con la misma codificación que la de los valores devueltos por la función de tiempo (3) (es decir, segundos desde la Época, UTC). La función mktime () interpreta la estructura de entrada de acuerdo con la configuración actual de la zona horaria (ver tzset (3)). La función timegm () interpreta que la estructura de entrada representa el Tiempo Universal Coordinado (UTC).
Corto:
Debe usar timegm, en lugar de usar mktime.
Saludos,
Pai
Si está tratando de hacer esto en un programa multiproceso y no quiere lidiar con el bloqueo y desbloqueo de mutexes (si usa el método de la variable de entorno), hay una función llamada timegm que hace esto. No es portátil, así que aquí está la fuente: 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;
}
Usa _mkgmtime , se ocupa de todo.
mktime () usa tzname para detectar la zona horaria. tzset () inicializa la variable tzname de la variable de entorno TZ. Si la variable TZ aparece en el entorno pero su valor está vacío o su valor no se puede interpretar correctamente, se utiliza el UTC.
Una versión portátil (no enhebrable) de acuerdo con la página de manual de tiempo
#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 tiene una versión de threadsafe publicada en su artículo Time, Clock y Calendar Programming In 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);
}
Aquí hay una pieza de código simple, probada, con suerte, que se convierte de struct tm
a segundos desde el comienzo de un año UTC ajustable, sin cambio temporal de zona horaria.
// 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
}
Observaciones clave que permiten una gran simplificación en comparación con el código en esta y en la respuesta:
- número de meses a partir de marzo, todos los meses excepto el anterior se repiten con un ciclo de 5 meses que suman 153 días, alternando 31 y 30 días, por lo que, para cualquier mes, y sin considerar los años bisiestos, el número de días desde el anterior Febrero se puede calcular (dentro de una constante) usando la adición de una constante apropiada, la multiplicación por 153 y la división entera por 5;
- la corrección en días que representa la regla para el año bisiesto en los años múltiplo de 100 (que por excepción a las reglas de múltiplo de 4 no son saltos, excepto si se puede calcular el múltiplo de 400) (dentro de una constante) por adición de una constante adecuada, división entera por 100, multiplicación por 3 y división entera por 4;
- podemos calcular la corrección para cualquier época usando la misma fórmula que usamos en el cálculo principal, y podemos hacer esto con una macro para que esta corrección se compute en el tiempo de compilación.
Aquí hay otra versión que no requiere soporte de 64 bits, bloqueado para origen 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;
}
Usé el siguiente código para generar una marca de tiempo en 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();
}