2008-11-21 20 views
16

Tengo un tiempo representado como el número de segundos transcurridos desde la medianoche del 1 de enero de 1970, UTC (los resultados de una llamada anterior a time()). ¿Cómo agrego un día a esta vez?Cómo agregar un día a un tiempo obtenido desde el tiempo()

Agregar 24 * 60 * 60 funciona en la mayoría de los casos, pero falla si el horario de verano se enciende o apaga. En otras palabras, principalmente quiero agregar 24 horas, pero a veces 23 o 25 horas.

Para ilustrar - el programa:

#include <time.h> 
#include <iostream> 

int main() 
{ 
    time_t base = 1142085600; 
    for(int i = 0; i < 4; ++i) { 
    time_t time = base + i * 24 * 60 * 60; 
    std::cout << ctime(&time); 
    } 
    return 0; 

}

Produce:

Sat Mar 11 08:00:00 2006 
Sun Mar 12 09:00:00 2006 
Mon Mar 13 09:00:00 2006 
Tue Mar 14 09:00:00 2006 

Quiero que los tiempos para el 12 de marzo de, 13, ... para ser también 8 PM.


La respuesta proporcionada por FigBug me apuntó en la dirección correcta. Pero tuve que usar localtime en lugar de gmtime.

int main() 
{ 
    time_t base = 1142085600; 
    for(int i = 0; i < 4; ++i) { 
    struct tm* tm = localtime(&base); 
    tm->tm_mday += i; 
    std::cout << asctime(tm); 
} 
return 0; 
} 

Dame:

Sat Mar 11 08:00:00 2006 
Sat Mar 12 08:00:00 2006 
Sat Mar 13 08:00:00 2006 
Sat Mar 14 08:00:00 2006 

¿Qué es lo que quiero. Usar gmtime me da los tiempos a las 14:00:00

Sin embargo, tenga en cuenta que todos los días son sábados. Además, se va al 32 de marzo, 33, etc. Si lanzo en la función mktime estoy de vuelta donde empecé:

#include <time.h> 
#include <iostream> 

int main() 
{ 
    time_t base = 1142085600; 
    for(int i = 0; i < 4; ++i) { 
    struct tm* tm = localtime(&base); 
    tm->tm_mday += i; 
    time_t time = mktime(tm); 
    std::cout << asctime(tm); 
} 
return 0; 
} 

me da:

Sat Mar 11 08:00:00 2006 
Sun Mar 12 09:00:00 2006 
Mon Mar 13 09:00:00 2006 
Tue Mar 14 09:00:00 2006 

¿Qué me falta ???


OK, he probado última sugerencia de FigBug que se va a utilizar:

std::cout << ctime(&time); 

en lugar de asctime, pero me da los mismos resultados. Así que supongo que mi biblioteca y/o compilador están en mal estado. Estoy usando g ++ 3.4.4 en cygwin. Copié los archivos a Solaris 5.8 y usé g ++ 3.3 para compilar. ¡Obtuve los resultados correctos allí! De hecho, obtener los resultados correctos si utilizo ctime o asctime para la salida:

Sat Mar 11 08:00:00 2006 
Sun Mar 12 08:00:00 2006 
Mon Mar 13 08:00:00 2006 
Tue Mar 14 08:00:00 2006 

también consigo los resultados correctos (con ambas funciones de salida) en Red Hut Linux con g ++ 3.4.6.

Así que supongo que me he encontrado con un error de Cygwin.

Gracias por toda su ayuda y consejos ....

+0

Creo que desea utilizar ctime no asctime en la versión final ya que desea la hora en su zona horaria local. time() devuelve UTC, localtime() lo convierte en su zona horaria. Agregue un día en su zona horaria, conviértalo nuevamente a UTC. Imprima en su zona horaria con ctime(). Muy confuso. –

+0

Una línea más: tm-> tm_wday = (tm-> tm_wday + i)% 7; –

+0

No necesita preocuparse: desde mktime docs: Los valores originales de los miembros tm_wday y tm_yday de timeptr se ignoran, y los rangos de valores para el resto de sus miembros no están restringidos a sus valores normales (como tm_mday está entre 1 y 31). –

Respuesta

18

uso gmtime() para convertir el time_t a un tm estructura

añadir un código a día ( tm_mday)

uso mktime() para convertir el struct tm a time_t

ver time.h para obtener más información

Editar:

yo sólo probé, esto funciona:

int main() 
{ 
    time_t base = 1142085600; 
    for(int i = 0; i < 4; ++i) { 
    struct tm* tm = localtime(&base); 
    tm->tm_mday += i; 
    time_t next = mktime(tm); 
    std::cout << ctime(&next); 
} 
return 0; 
} 
+0

¿Las palabras con un guión bajo no están en cursiva? –

+0

use some_word_with_underscores en lugar de * word * – Jimmy

+0

O ponga una barra invertida delante del guión bajo. –

3

siempre he tenido el mejor resultado con el mantenimiento de las marcas de hora UTC y convertirlos a la zona horaria especificada (incluyendo el horario de verano) cuando se desea mostrar los valores .

Esto ahorra muchas molestias como esta (y hace que su programa sea independiente de las zonas horarias.

+0

Las marcas de tiempo son UTC. –

5

Sólo añadir 24 * 60 * 60. No debería fallar durante el horario de verano, ya que UTC nunca usará el horario de verano.

Si está fallando, no está utilizando UTC en algún lugar de su código. Eliminar la dependencia de la zona horaria.

+1

El punto es que agregar un día para decir 3/31/2008 8:00:00 AM hora local debería darme 4/1/2008 8:00:00 AM, hora local, incluso si el horario de verano cambia localmente de la noche a la mañana del 3/31. –

+0

No entiendo, ¿está usando la marca de tiempo de Unix en UTC o no? La marca de tiempo de Unix no tiene nada que ver con el horario de verano. – Pyrolistical

+1

La conversión de UTC a hora local es donde debe agregarse o restarse la hora DST; el UTC no debe verse afectado, como dice Pyrolistical. –

5

solución de FigBug trabajará casi cada vez, pero es necesario fijar el horario de verano: TM-> tm_isdst = -1

Un valor positivo o 0 para tm_isdst causas mktime() para presumir inicialmente ese horario de verano, respectivamente, está o no está en vigor durante el tiempo especificado. Un valor negativo de para tm_isdst hace que mktime() a intente determinar si la Hora de verano está vigente durante el tiempo especificado .

(cita de mktime spec)

int main() 
{ 
    time_t base = 1142085600; 
    for(int i = 0; i < 4; ++i) { 
    struct tm* tm = localtime(&base); 
    tm->tm_mday += i; 
    tm->tm_isdst = -1;  // don't know if DST is in effect, please determine 
           // this for me 
    time_t next = mktime(tm); 
    std::cout << ctime(&next); 
} 
return 0; 
} 

De lo contrario habrá un error (ejemplo para Moscú el horario de verano que comienza 29 de de marzo de 2009 01:59:59):

int main() 
{ 
    // 28 March 2009 05:00:00 GMT (local - 08:00 (MSK)) 
    time_t base = 1238216400; 

    std::time_t start_date_t = base; 
    std::time_t end_date_t = base; 

    std::tm start_date = *std::localtime(&start_date_t); 
    std::tm end_date = *std::localtime(&end_date_t); 

    end_date.tm_mday += 1; 
// end_date.tm_isdst = -1; 

    std::time_t b = mktime(&start_date); 
    std::time_t e = mktime(&end_date); 

    std::string start_date_str(ctime(&b)); 
    std::string stop_date_str(ctime(&e)); 

    cout << " begin (MSK) (DST is not active): " << start_date_str; 
    cout << " end (MSD) (DST is active):  " << stop_date_str; 
} 

Salida:

begin (MSK) (DST is not active): Sat Mar 28 08:00:00 2009 
end (MSD) (DST is active):  Sun Mar 29 09:00:00 2009 
2

Nueva respuesta para una pregunta muy antigua ción.

Justificación de la nueva respuesta: ahora hay mejores herramientas para resolver este problema, haciendo que el resultado sea menos propenso a errores, más fácil de leer y, de hecho, más eficiente al minimizar la serie < -> conversiones de campo.

La nueva respuesta requiere C++ 11/14, <chrono>, y esta free, open source, timezone library.

Aquí está el código:

#include "tz.h" 
#include <iostream> 

int 
main() 
{ 
    using namespace std::chrono; 
    using namespace date; 
    auto base = make_zoned("Pacific/Easter", sys_seconds{1142085600s}); 
    for (int i = 0; i < 4; ++i) 
    { 
     std::cout << format("%a %b %d %T %Y %Z", base) << '\n'; 
     base = base.get_local_time() + days{1}; 
    } 
} 

El comienza por la creación de un zoned_time por el emparejamiento de lo que es la zona horaria deseada con la marca de tiempo Unix Time.

Formateado en cualquier formato deseado.

Y la adición de 1 día se realiza en el sistema horario local de la zona horaria, lo que tendrá en cuenta el horario de verano. La salida es:

Sat Mar 11 09:00:00 2006 -05 
Sun Mar 12 09:00:00 2006 -06 
Mon Mar 13 09:00:00 2006 -06 
Tue Mar 14 09:00:00 2006 -06 

Como resultado, esta salida no es exactamente lo que el PO indicó que deseaba (la salida solicitada se deja a 08:00:00 todos los días). Sin embargo, utilicé esta biblioteca para investigar completamente las transiciones de tiempo de todo el planeta en esta fecha. Y solo hay una zona horaria que tuvo una transición en esta fecha: Pacífico/Semana Santa. Y esa transición consistió en mover de vuelta por hora, no adelante. Esta es la zona horaria utilizada para Chile, en el hemisferio sur, donde cae en el marco de marzo.

Esto se puede demostrar haciendo la aritmética en UTC en lugar de hacerlo en la hora local. Se trata de un pequeño ajuste en el programa anterior en una línea:

 base = base.get_sys_time() + days{1}; 

Usando base.get_sys_time(), en contraposición a base.get_local_time(), hace que el cálculo se haga de "hora del sistema", que es UTC descuidar segundos intercalares. Y ahora la salida cambia a:

Sat Mar 11 09:00:00 2006 -05 
Sun Mar 12 08:00:00 2006 -06 
Mon Mar 13 08:00:00 2006 -06 
Tue Mar 14 08:00:00 2006 -06 
Cuestiones relacionadas