2010-09-24 22 views
49

Entrada: cadenas con fecha y hora opcional. Las diferentes representaciones serían agradables pero necesarias. Las cadenas son suministradas por el usuario y pueden estar mal formadas. Ejemplos:¿Cómo se analiza la fecha/hora de la cadena?

  • "2004-03-21 12:45:33" (que consideran este diseño predeterminado)
  • "2004/03/21 12:45:33" (diseño opcional)
  • "23.09.2004 04:12:21" (formato alemán, opcional)
  • "2003-02-11" (tiempo puede faltar)

Salida necesaria: Segundos desde Epoch (1970/01/01 00:00:00) o algún otro punto fijo.

Bonificación: Además, leer el desplazamiento UTC de la hora del sistema local sería genial.

Se supone que la entrada es una hora local en la máquina en cuestión. La salida debe ser UTC. El sistema es solo Linux (se necesitan Debian Lenny y Ubuntu).

He intentado usar boost/date_time, pero debo admitir que no puedo entender bien la documentación. Las siguientes obras sin la necesidad de conversión del sistema de hora local a UTC:

std::string date = "2000-01-01"; 
boost::posix_time::ptime ptimedate = boost::posix_time::time_from_string(date); 
ptimedate += boost::posix_time::hours(Hardcoded_UTC_Offset);// where to get from? 
struct tm = boost::posix_time::to_tm(ptimedate); 
int64_t ticks = mktime(&mTmTime); 

creo boost::date_time pueden proporcionar la UTC es necesario compensar, pero no sabrían.

+1

Creo que tendrá que analizarlos por su cuenta (quizás con espíritu) porque el número de un solo dígito del mes en "2004-3-21" no es analizable por ninguno de los especificadores de formato IO del tiempo de impulso http: //www.boost.org/doc/libs/1_44_0/doc/html/date_time/date_time_io.html#date_time.format_flags – Cubbi

+0

@Cubbi: si ese es el único problema, es mucho más fácil verificarlo e insertar un 0 en la cuerda que para traer espíritu a la imagen. –

+0

@Cubbi - puede manejar formatos de entrada y salida personalizados en 'boost :: date_time' -' boost :: spirit' es overkill aquí –

Respuesta

59

Aunque no sé cómo formatear una entrada de un solo dígito en el impulso, puedo hacerlo después de la edición de dos dígitos:

#include <iostream> 
#include <boost/date_time.hpp> 
namespace bt = boost::posix_time; 
const std::locale formats[] = { 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))}; 
const size_t formats_n = sizeof(formats)/sizeof(formats[0]); 

std::time_t pt_to_time_t(const bt::ptime& pt) 
{ 
    bt::ptime timet_start(boost::gregorian::date(1970,1,1)); 
    bt::time_duration diff = pt - timet_start; 
    return diff.ticks()/bt::time_duration::rep_type::ticks_per_second; 

} 
void seconds_from_epoch(const std::string& s) 
{ 
    bt::ptime pt; 
    for(size_t i=0; i<formats_n; ++i) 
    { 
     std::istringstream is(s); 
     is.imbue(formats[i]); 
     is >> pt; 
     if(pt != bt::ptime()) break; 
    } 
    std::cout << " ptime is " << pt << '\n'; 
    std::cout << " seconds from epoch are " << pt_to_time_t(pt) << '\n'; 
} 
int main() 
{ 
    seconds_from_epoch("2004-03-21 12:45:33"); 
    seconds_from_epoch("2004/03/21 12:45:33"); 
    seconds_from_epoch("23.09.2004 04:12:21"); 
    seconds_from_epoch("2003-02-11"); 
} 

nota de que la salida de segundos-de-época estará asumiendo la fecha fue en UTC:

~ $ ./test | head -2 
ptime is 2004-Mar-21 12:45:33 
seconds from epoch are 1079873133 
~ $ date -d @1079873133 
Sun Mar 21 07:45:33 EST 2004 

probablemente se podría utilizar boost::posix_time::c_time::localtime() de #include <boost/date_time/c_time.hpp> para conseguir esta conversión hecho asumiendo la entrada está en la zona horaria actual, pero es bastante inconsistente: para mí, por ejemplo, el resultado será diferente entre hoy y el mes que viene, cuando termine el horario de verano.

+1

Es muy útil mostrar cómo trabajar con facetas. Usar el tiempo local no es una opción si lo entiendo bien, ya que me daría el desplazamiento DST de hoy en lugar de la fecha dada. –

+0

@Gabriel Schreiber: Probablemente podría hacer el desplazamiento DST en la fecha dada, haciendo lo contrario a lo que 'utc_to_local()' hace en '/ usr/include/boost/date_time/c_local_time_adjustor.hpp', que seguiría usando la zona de la computadora actual.Una mejor manera es probablemente algo más cercano a http://www.boost.org/doc/libs/1_44_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch – Cubbi

1

la solución más simple y portátil es el uso de scanf:

int year, month, day, hour, minute, second = 0; 
int r = 0; 

r = scanf ("%d-%d-%d %d:%d:%d", &year, &month, &day, 
      &hour, &minute, &second); 
if (r == 6) 
{ 
    printf ("%d-%d-%d %d:%d:%d\n", year, month, day, hour, minute, 
      second); 
} 
else 
{ 
    r = scanf ("%d/%d/%d %d:%d:%d", &year, &month, &day, 
      &hour, &minute, &second); 
    // and so on ... 

inicializar un struct tm con los valores int y pasarlo a mktime para obtener un tiempo de calendario como time_t. Para las conversiones de zona horaria, see information en gmtime.

+7

C runtime 'scanf/printf' introduce la gestión de búfer y escribe problemas de seguridad que pueden evitarse mediante el uso de bibliotecas C++ apropiadas. –

+0

Esto no resuelve el problema local a utc. Además, la cadena es suministrada por el usuario y puede ser no válida/malformada. Creo que esto puede ser un problema con scanf? –

+0

@Gabriel si la cadena está mal formada, scanf no devolverá 6. Acerca de utc, he agregado más información a la respuesta. –

8

boost::gregorian tiene algunas de las cosas que necesita sin que usted haga más obra:

using namespace boost::gregorian; 
{ 
    // The following date is in ISO 8601 extended format (CCYY-MM-DD) 
    std::string s("2000-01-01"); 
    date d(from_simple_string(s)); 
    std::cout << to_simple_string(d) << std::endl; 
} 

Hay un ejemplo sobre cómo usar UTC compensa con boost::posix_timehere.

Puede proporcionar la generación de fecha y hora a partir de los formatos de cadena de entrada personalizados usando date_input_facet y time_input_facet. Hay un tutorial de E/S en this page que debería ayudarlo a comenzar.

+1

Thx para la faceta/tutorial linx. El uso de boost :: gregorian no resuelve el problema porque no proporciona análisis/representación del tiempo. –

+0

@Gabriel - a la derecha, tendrías que construir tu propio analizador y formateador usando estas herramientas para manejar todos tus casos requeridos. A menos que tenga formatos de entrada posibles sin límites, eso debería ser posible usando un analizador para cada formato, y un contenedor que identifique el tipo de formato y lo alimente al analizador apropiado. –

+0

@Gabriel - tenga en cuenta que cuando digo el analizador, esto realmente no es nada complicado dado sus opciones de entrada 'cadena'. Simplemente detecte cada uno y construya las construcciones de Boost apropiadas para analizar apropiadamente en una fecha_hora. –

6

Si c-estilo es aceptable: strptime() es el camino a seguir, porque se puede especificar el formato y se puede tomar la configuración regional en cuenta:

tm brokenTime; 
strptime(str.c_str(), "%Y-%m-%d %T", &brokenTime); 
time_t sinceEpoch = timegm(brokenTime); 

diferentes diseños tendrán que ser controlados con la valor de retorno (si es posible). La zona horaria tendrá que ser agregada al marcar el reloj del sistema (localtime_r() con time(), tm_zone)

+0

strptime ha sido probado. No es aceptable porque se bloqueará felizmente si la cuerda no está bien formada. –

+0

Lo uso, no se cuelga aquí, pero la experiencia puede variar. Tendré que investigar (a google) para estar seguro ... – stefaanv

+0

@Gabriel: Excepto para MacOs X Leopard, donde el tiempo de respuesta parece estar roto, no se encontró nada especial (getdate crashes, Qalculate removed strptime (2004)). ¿Podría darnos alguna información sobre el sistema en el que se bloquea? – stefaanv

Cuestiones relacionadas