2010-10-05 19 views
5

Considere esto como una continuación de this question. Básicamente, las funciones de formato de fecha/hora C++ parecen estar irremediablemente rotas, tanto que para hacer algo tan simple como convertir una cadena de fecha/hora en un objeto, realmente tienes que recurrir a Boost.Datetime o al viejo C strftime/strptime instalaciones.C++: convertir cadena de fecha/hora a tm struct

El problema es que ninguna de estas soluciones funciona directamente con la configuración local de C++ imbuida en un objeto iostream en particular. Las instalaciones de C usan la configuración regional de C/POSIX global, mientras que las instalaciones de E/S en Boost.Datetime parecen omitir por completo las configuraciones regionales de iostream al permitir al usuario establecer directamente los nombres de meses, días de la semana, etc., independientemente de la configuración regional.

Por lo tanto, quería algo que respetara las configuraciones locales imbuidas en un flujo de E/S particular que me permitiría convertir una cadena en struct tm. Parecía bastante fácil, pero tropecé con obstáculos en cada esquina. Al principio, noté que algunas implementaciones del STL proporcionan una función no estándar std::time_get::get, así que decidí implementar algo similar. Básicamente, simplemente iteraría sobre la cadena de formato y cada vez que tocara un indicador de formato, usaría uno de los recursos time_get (como get_monthname, get_weekday, get_year, etc.) para convertir la cadena de entrada en struct tm. Esto parece bastante fácil, excepto que cada una de estas funciones requiere un rango de iterador exacto. No puede convertir "Monday,", tiene que ser "Monday" exactamente, o la conversión falla. Dado que los iteradores deben ser istreambuf_iterator, no puede simplemente escanear hacia adelante, porque cada incremento cambia la posición de obtención en el almacenamiento intermedio de flujo. Entonces, básicamente, primero tiene que iterar sobre la secuencia, copiando cada carácter en otro streambuffer, y luego cuando toca un delimitador (como un espacio o una coma), use el segundo streambuffer con las instalaciones time_get. Es literalmente como si los diseñadores de C++ hicieran todo lo posible para que esto fuera tan molesto como fuera posible.

Entonces, ¿hay una solución más fácil? ¿Qué hacen la mayoría de los programadores de C++ cuando necesitan convertir una cadena de fecha/hora en un objeto? ¿Simplemente tenemos que usar las instalaciones de C y perder las ventajas que vienen con las diferentes configuraciones locales imbuidas en diferentes objetos iostream?

+0

¿Ha presentado algún error en contra de su biblioteca de C++? Quejas como esta impulsan su desarrollo. – Potatoswatter

+0

'std :: time_get :: get' está definido por C++ 0x 22.4.5.1.1/6 ... Me acaba de ocurrir que tengo la página abierta, pero supongo que también está en C++ 03. – Potatoswatter

+0

No está en C++ 03: está disponible en algunas implementaciones, pero no es estándar. –

Respuesta

2

Boost utiliza la configuración regional estándar de forma predeterminada; usted no tiene que pasar por alto cualquier cosa:

#include "boost/date_time/gregorian/gregorian.hpp" 
#include <iostream> 
#include <sstream> 
#include <ctime> 

int main(){ 
    using namespace boost::gregorian; 

    std::locale::global(std::locale("")); 
    std::locale german("German_Germany"); 
    std::locale french("French_France"); 

    date d1(day_clock::local_day()); 
    date d2; 
    std::stringstream ss("2002-May-01"); 

    std::cout << "Mine: " << d1 << " | "; 
    ss >> d2; 
    std::cout << d2 << '\n'; 

    std::cout.imbue(german); 
    std::cout << "Germany: " << d1 << " | "; 
    ss.imbue(german); 
    ss << "2002-Mai-01"; 
    ss >> d2; 
    std::cout << d2 << '\n'; 

    std::cout.imbue(french); 
    std::cout << "France: " << d1 << " | " << d2 << '\n'; 

    std::tm t = to_tm(d1); 
    std::cout << "tm: " << asctime(&t); 
} 

(Esos nombres específicos son específicos de Windows, por supuesto.) Salida:

Mine: 2010-Oct-28 | 2002-May-01 
Germany: 2010-Okt-28 | 2002-Mai-01 
France: 2010-oct.-28 | 2002-mai-01 
tm: Thu Oct 28 00:00:00 2010 
0

¿Por qué no utilizar la biblioteca C? Es casi seguro que está disponible en su implementación, y está bien depurado y probado.

Si le falta alguna función, seguramente es más fácil hacer la función de envoltura que funciona en la zona horaria que desee.

+0

Si el programa está diseñado en C++ locales, supongo que puede ser inconveniente volver a traducir a C locales ... aunque no veo cómo podría ser más que obtener el nombre de locale :: name() y pasar a setlocale ()/newlocale() ... eso podría ser bastante lento, sin embargo. – Potatoswatter

+0

Depende de cómo los esté usando. Llamar a 'std :: locale :: global()' también establecerá la configuración regional C si el objeto de configuración regional dado tiene un nombre. Si él está imbuyendo flujos individuales con diferentes configuraciones regionales, tendrá que configurar y desarmar manualmente la configuración regional C cada vez que use uno de ellos (o al menos, cada vez que use uno para el procesamiento de la fecha). Sin embargo, sería fácil escribir una clase centinela para ayudar con eso. –

0

Siempre trato de usar cadenas locales independientes para serializar datos. Hace la vida mucho más fácil.