web-dev-qa-db-fra.com

Comment analyser une chaîne de date dans un c ++ 11 std :: chrono time_point ou similaire?

Considérons une chaîne de date historique de format:

Thu Jan 9 12:35:34 2014

Je veux analyser une telle chaîne dans une sorte de représentation de date C++, puis calculer la quantité de temps qui s'est écoulée depuis.

De la durée qui en résulte, j'ai besoin d'accéder au nombre de secondes, minutes, heures et jours.

Cela peut-il être réalisé avec le nouvel espace de noms C++ 11 std::chrono? Si non, comment devrais-je m'y prendre aujourd'hui?

J'utilise g ++ - 4.8.1 mais je suppose qu'une réponse devrait simplement cibler la spécification C++ 11.

34
Drew Noakes
std::tm tm = {};
std::stringstream ss("Jan 9 2014 12:35:34");
ss >> std::get_time(&tm, "%b %d %Y %H:%M:%S");
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));

Avant la version 5, GCC n'implémentait pas std::get_time. Vous devriez aussi pouvoir écrire:

std::tm tm = {};
strptime("Thu Jan 9 2014 12:35:34", "%a %b %d %Y %H:%M:%S", &tm);
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
60
Simple

Nouvelle réponse à l'ancienne question. Justification de la nouvelle réponse: La question a été modifiée à partir de sa forme originale car les outils de l'époque ne géraient pas exactement ce qui était demandé. Et la réponse acceptée qui en résulte donne un comportement légèrement différent de celui demandé par la question initiale.

Je n'essaie pas de poser la réponse acceptée. C'est une bonne réponse. C'est juste que l'API C est so confuse qu'il est inévitable que de telles erreurs se produisent.

La question initiale était d’analyser "Thu, 9 Jan 2014 12:35:34 +0000". L’intention était donc clairement d’analyser un horodatage représentant une heure UTC. Mais strptime (qui n'est pas le standard C ou C++, mais POSIX) n'analyse pas le décalage UTC final indiquant qu'il s'agit d'un horodatage UTC (il le formatera avec %z, mais ne l'analysera pas).

La question a ensuite été modifiée pour poser des questions sur "Thu Jan 9 12:35:34 2014". Mais la question était pas modifiée pour préciser s'il s'agissait d'un horodatage UTC ou d'un horodatage dans le fuseau horaire local actuel de l'ordinateur. La réponse acceptée implicitement suppose que l'horodatage représente le fuseau horaire local actuel de l'ordinateur en raison de l'utilisation de std::mktime.

std::mktime non seulement transforme le type de champ tm en type de série time_t, il effectue également un ajustement du décalage du fuseau horaire local de l'ordinateur au format UTC.

Mais que se passe-t-il si nous voulons analyser un horodatage UTC comme la question originale (non modifiée) a été posée?

Cela peut être fait aujourd'hui avec cette plus récente, bibliothèque gratuite à code source ouvert .

#include "date.h"
#include <iostream>
#include <sstream>

int
main()
{
    using namespace std;
    using namespace date;
    istringstream in{"Thu, 9 Jan 2014 12:35:34 +0000"};
    sys_seconds tp;
    in >> parse("%a, %d %b %Y %T %z", tp);
}

Cette bibliothèque peut analyser %z. Et date::sys_seconds est juste un typedef pour:

std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>

La question demande également:

De la durée qui en résulte, j'ai besoin d'accéder au nombre de secondes, minutes, heures et jours.

Cette partie est restée sans réponse. Voici comment procéder avec cette bibliothèque . Pour cette partie, je vais aussi utiliser un deuxième bibliothèque en-tête uniquement "chrono_io.h" :

#include "chrono_io.h"
#include "date.h"
#include <iostream>
#include <sstream>

int
main()
{
    using namespace std;
    using namespace date;
    istringstream in{"Thu, 9 Jan 2014 12:35:34 +0000"};
    sys_seconds tp;
    in >> parse("%a, %d %b %Y %T %z", tp);
    auto tp_days = floor<days>(tp);
    auto hms = make_time(tp - tp_days);
    std::cout << "Number of days    = " << tp_days.time_since_Epoch() << '\n';
    std::cout << "Number of hours   = " << hms.hours() << '\n';
    std::cout << "Number of minutes = " << hms.minutes() << '\n';
    std::cout << "Number of seconds = " << hms.seconds() << '\n';
}

floor<days> tronque le précision_secondes time_point en un jours-précisiontime_point. Si vous soustrayez le time_point de __ jours__ de tp, vous vous retrouverez avec un duration représentant le temps écoulé depuis minuit (UTC).

La fonction d'usine make_time prend une variable duration (dans ce cas, l'heure écoulée depuis minuit) et crée un type de champ {hours, minutes, seconds} avec des sélecteurs pour chaque champ. Si la durée a une précision inférieure à secondes, ce type de champ aura également un getter pour les sous-secondes.

Enfin, en utilisant "chrono_io.h" , on peut simplement imprimer toutes ces durées. Cet exemple génère:

Number of days    = 16079[86400]s
Number of hours   = 12h
Number of minutes = 35min
Number of seconds = 34s

Le [86400]s représente les unités de la duration qui a la précision des jours. Donc, 2014-01-09 est 16079 jours après 1970-01-01.

9
Howard Hinnant

Ceci est plutôt simple et n'est pas aussi élégante d'une solution que la réponse de Simple, mais je pense que cela pourrait fonctionner. Cette réponse est probablement fausse, mais je vais la laisser afin que quelqu'un puisse poster des corrections.

#include <iostream>
#include <ctime>

int main ()
{
  struct tm timeinfo;
  std::string buffer = "Thu, 9 Jan 2014 12:35:00";

  if (!strptime(buffer.c_str(), "%a, %d %b %Y %T", &timeinfo))
    std::cout << "Error.";

  time_t now;
  struct tm timeinfo2;
  time(&now);
  timeinfo2 = *gmtime(&now);

  time_t seconds = difftime(mktime(&timeinfo2), mktime(&timeinfo));
  time(&seconds);
  struct tm result;
  result = *gmtime ( &seconds );
  std::cout << result.tm_sec << " " << result.tm_min << " "
            << result.tm_hour << " " << result.tm_mday;
  return 0;
}
2
user1508519