2010-09-24 4 views
49

Entrée: chaînes avec date et heure optionnelle. Différentes représentations seraient bien mais nécessaires. Les chaînes sont fournies par l'utilisateur et peuvent être malformées. Exemples:Comment analyser la date/heure d'une chaîne?

  • "2004-03-21 12:45:33" (je considère ce la mise en page par défaut)
  • "2004/03/21 12:45:33" (mise en page en option)
  • "23.09.2004 04:12:21" (format allemand, en option)
  • "2003-02-11" (temps peut manquer)

Sortie nécessaire: secondes depuis l'époque (1970/01/01 00:00:00) ou un autre point fixe.

Bonus: De plus, lire le décalage UTC de l'heure système locale serait super.

L'entrée est supposée être une heure locale sur la machine en question. La sortie doit être UTC. Le système est Linux seulement (Debian Lenny et Ubuntu sont nécessaires).

J'ai essayé d'utiliser boost/date_time, mais je dois admettre que je ne peux pas faire le tour de la documentation. Les travaux suivants sans la conversion nécessaire de l'heure locale du système à 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); 

Je pense que boost::date_time peut fournir l'UTC nécessaire décalage, mais je ne sais pas comment.

+1

Je crois que vous devrez les analyser vous-même (peut-être avec l'esprit) parce que le nombre de mois à un chiffre dans "2004-3-21" n'est pas analysable par l'un des spécificateurs de format IO de temps de boost 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 c'est le seul problème, il est beaucoup plus facile de vérifier cela et d'insérer un 0 dans la chaîne que d'apporter l'esprit dans l'image. –

+0

@Cubbi - vous pouvez gérer les formats d'entrée et de sortie personnalisés dans boost - date_time' - 'boost :: spirit' est trop grand ici –

Répondre

59

Bien que je ne sache pas comment formater une entrée de mois à un chiffre en boost, je peux le faire après le montage à deux chiffres:

#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"); 
} 

Notez que la sortie des secondes de-époque sera acquis que la date était 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 

Vous pouvez probablement utiliser boost::posix_time::c_time::localtime() de #include <boost/date_time/c_time.hpp> pour obtenir cette conversion fait en supposant que l'entrée est dans le fuseau horaire actuel, mais il est plutôt incohérent: pour moi, par exemple, le résultat sera différent entre aujourd'hui et le mois prochain, lorsque l'heure d'été se terminera.

+1

Montrer comment travailler avec des facettes est très apprécié. L'utilisation de localtime n'est pas une option si je la comprends bien car elle me donnerait l'offset DST d'aujourd'hui plutôt que de la date donnée. –

+0

@Gabriel Schreiber: Vous pouvez probablement faire le DST-offset à la date donnée, en faisant le contraire de ce que 'utc_to_local()' fait dans/usr/include/boost/date_time/c_local_time_adjustor.hpp', qui utiliserait encore la zone de l'ordinateur actuel.Un meilleur moyen est probablement quelque chose de plus proche de http://www.boost.org/doc/libs/1_44_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch – Cubbi

1

la plus simple, la solution portable est d'utiliser 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 ... 

Initialiser une struct tm avec les int valeurs et passer à mktime pour obtenir un temps de calendrier comme time_t. Pour les conversions de fuseau horaire, veuillez see information sur gmtime.

+7

C runtime 'scanf/printf' introduit la gestion de la mémoire tampon et des problèmes de sécurité de type qui peuvent être évités en utilisant les bibliothèques C++ appropriées. –

+0

Cela ne résout pas le problème local à utc. En outre, la chaîne est fournie par l'utilisateur et peut être invalide/mal formée. Je pense que cela peut être un problème avec scanf? –

+0

@Gabriel si la chaîne est malformée, scanf ne retournera pas 6. à propos de utc, j'ai ajouté quelques informations supplémentaires à la réponse. –

8

boost::gregorian a quelques-unes des choses que vous avez besoin sans vous faire plus de travail:

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; 
} 

Il y a un exemple sur la façon d'utiliser UTC compense avec boost::posix_timehere.

Vous pouvez fournir la génération de la date et de l'heure à partir des formats de chaîne d'entrée personnalisés à l'aide des formats date_input_facet et time_input_facet. Il y a un tutoriel d'E/S sur this page qui devrait vous aider à démarrer.

+1

Thx pour la facette/tutoriel linx. L'utilisation de boost :: gregorian ne résout pas le problème car il ne fournit pas d'analyse/représentation du temps. –

+0

@Gabriel - à droite, vous devriez construire votre propre analyseur et formateur en utilisant ces outils pour gérer tous vos cas requis. À moins que vous ayez des formats d'entrée possibles illimités, cela devrait être possible en utilisant un analyseur pour chaque format, et un wrapper qui identifie le type de format et les transmet à l'analyseur approprié. –

+0

@Gabriel - notez que quand je dis parser, ce n'est vraiment rien de complexe étant donné vos options d'entrée 'string'. Détectez chacun et construisez les constructions Boost appropriées pour les analyser correctement dans une date_heure. –

6

Si c-style est acceptable: strptime() est le chemin à parcourir, parce que vous pouvez spécifier le format et il peut prendre locale compte:

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

mises en page différentes devront vérifier la valeur de retour (si possible). Le fuseau horaire devra être ajouté à en vérifiant l'horloge du système (localtime_r() avec time(), tm_zone)

+0

strptime a été essayé. Ce n'est pas acceptable car il heurtera heureusement si la chaîne n'est pas bien formée. –

+0

Je l'utilise, il ne plante pas ici, mais l'expérience peut différer. Je vais devoir enquêter (pour google) pour être sûr ... – stefaanv

+0

@Gabriel: Sauf pour MacOs X Leopard, où strptime semble être cassé, rien de spécial n'a été trouvé (crashs getdate, Qalculate retiré strptime (2004)). Pourriez-vous donner des informations sur le système sur lequel il se bloque? – stefaanv

Questions connexes