2010-09-24 15 views
49

Ingresso: stringhe con data e ora facoltativa. Rappresentazioni diverse sarebbero belle ma necessarie. Le stringhe sono fornite dall'utente e possono essere malformate. Esempi:Come analizzare data/ora dalla stringa?

  • "2004-03-21 12:45:33" (considero questo il layout di default)
  • "2004/03/21 12:45:33" (il layout optional)
  • "23.09.2004 04:12:21" (formato tedesco, opzionale)
  • "2003-02-11" (tempo può mancare)

Uscita richiesta: secondi da Epoch (1970/01/01 00:00:00) o qualche altro punto fisso.

Bonus: Inoltre, la lettura dell'ora UTC dell'ora del sistema locale sarebbe ottima.

Si presuppone che l'ingresso sia un orario locale sulla macchina in questione. L'output deve essere UTC. Il sistema è solo Linux (sono necessari Debian Lenny e Ubuntu).

Ho provato a utilizzare boost/date_time, ma devo ammettere che non riesco a comprendere la documentazione. Le seguenti opere senza la conversione necessaria dal sistema ora locale 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); 

penso boost::date_time in grado di fornire l'offset UTC necessario, ma non saprei come fare.

+1

Credo che dovrete analizzarli da soli (forse con spirito) perché il numero del mese a una cifra in "2004-3-21" non è analizzabile da nessuno degli indicatori di formato I/O del boost time http: //www.boost.org/doc/libs/1_44_0/doc/html/date_time/date_time_io.html#date_time.format_flags – Cubbi

+0

@Cubbi: se questo è l'unico problema, è molto più facile controllarlo e inserire uno 0 in la stringa che portare lo spirito nella foto. –

+0

@Cubbi - puoi gestire i formati di input e output personalizzati in 'boost :: date_time' -' boost :: spirit' è eccessivo qui –

risposta

59

Anche se non so come formattare un input mese a una cifra in boost, posso farlo dopo la modifica a due cifre:

#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 che l'uscita secondi-da-epoca sarà assumendo la data era in 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 

probabilmente si potrebbe utilizzare boost::posix_time::c_time::localtime() da #include <boost/date_time/c_time.hpp> per ottenere questa conversione fatto assumendo l'input è nel fuso orario corrente, ma è piuttosto incoerente: per me, ad esempio, il risultato sarà diverso tra oggi e il mese prossimo, quando termina l'ora legale.

+1

È molto apprezzato mostrare come lavorare con le faccette. L'utilizzo di localtime non è un'opzione se ho capito bene dato che mi avrebbe dato l'offset dell'ora legale piuttosto che della data specificata. –

+0

@Gabriel Schreiber: Probabilmente si può eseguire l'offset dell'ora legale alla data specificata, facendo l'opposto di ciò che 'utc_to_local()' fa in '/ usr/include/boost/date_time/c_local_time_adjustor.hpp', che continuerebbe a essere utilizzato la zona del computer corrente.Un modo migliore è probabilmente qualcosa di più vicino a http://www.boost.org/doc/libs/1_44_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch – Cubbi

1

il, soluzione portatile più semplice è quello di utilizzare 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 ... 

inizializzare un struct tm con i valori int e passarlo al mktime per ottenere un tempo di calendario come time_t. Per le conversioni di fuso orario, per favore see information su gmtime.

+7

Il runtime 'scanf/printf' introduce la gestione del buffer e digita i problemi di sicurezza che possono essere evitati usando le librerie C++ appropriate. –

+0

Questo non risolve il problema local to utc. Inoltre, la stringa è fornita dall'utente e può essere non valida/non valida. Penso che questo possa essere un problema con scanf? –

+0

@Gabriel se la stringa è malformata, scanf non restituirà 6. su utc, ho aggiunto qualche altra informazione alla risposta. –

8

boost::gregorian ha alcune delle cose è necessario senza di te fare più lavoro:

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

C'è un esempio su come utilizzare UTC compensa con boost::posix_timehere.

È possibile fornire la generazione di data e ora dai formati di stringa di input personalizzati utilizzando date_input_facet e time_input_facet. Esiste un tutorial I/O su this page che dovrebbe aiutarti ad andare avanti.

+1

Thx per il facet/tutorial linx. Usare boost :: gregorian non risolve il problema perché non fornisce analisi/rappresentazione del tempo. –

+0

@Gabriel - a destra, dovresti creare il tuo parser e formattatore utilizzando questi strumenti per gestire tutti i casi richiesti. A meno che non siano disponibili formati di input illimitati, ciò dovrebbe essere possibile utilizzando un parser per ogni formato e un wrapper che identifica il tipo di formato e i feed al parser appropriato. –

+0

@Gabriel - nota che quando dico parser, questo non è davvero niente di complesso date le opzioni di 'stringa' di input. Basta rilevare ciascuno e creare i costrutti Boost appropriati per analizzare in modo appropriato in un data_time. –

6

Se c-style è accettabile: strptime() è la strada da percorrere, perché è possibile specificare il formato e si può prendere locale in conto:

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

diversi layout dovranno essere controllato con il valore di ritorno (se possibile). Il fuso orario dovrà essere aggiunto controllando l'orologio di sistema (localtime_r() con time(), tm_zone)

+0

strptime è stato provato. Non è accettabile perché si bloccherà allegramente se la stringa non è ben formata. –

+0

Lo uso, non si blocca qui, ma l'esperienza potrebbe essere diversa. Dovrò investigare (su google) per essere sicuro ... – stefaanv

+0

@Gabriel: Tranne MacOs X Leopard, dove il tempo sembra essere rotto, non è stato trovato nulla di speciale (getdate si blocca, Qalculate removed strptime (2004)). Potresti fornire alcune informazioni sul sistema su cui si blocca? – stefaanv

Problemi correlati