web-dev-qa-db-fra.com

Comment convertir std :: chrono :: time_point en calendrier datetime avec des fractions de secondes?

Comment convertir std::chrono::time_point à la chaîne date/heure du calendrier avec des fractions de secondes?

Par exemple:

"10-10-2012 12:38:40.123456"
48
boxx

Si system_clock, cette classe a une conversion time_t.

#include <iostream>
#include <chrono>
#include <ctime>

using namespace std::chrono;

int main()
{
  system_clock::time_point p = system_clock::now();

  std::time_t t = system_clock::to_time_t(p);
  std::cout << std::ctime(&t) << std::endl; // for example : Tue Sep 27 14:21:13 2011
}

exemple de résultat:

Thu Oct 11 19:10:24 2012

EDIT: Mais time_t ne contient pas de fractions de secondes. Une autre méthode consiste à utiliser la fonction time_point :: time_since_Epoch (). Cette fonction renvoie la durée de Epoch. Suivez l'exemple est la résolution fractionnaire en millisecondes.

#include <iostream>
#include <chrono>
#include <ctime>

using namespace std::chrono;

int main()
{
  high_resolution_clock::time_point p = high_resolution_clock::now();

  milliseconds ms = duration_cast<milliseconds>(p.time_since_Epoch());

  seconds s = duration_cast<seconds>(ms);
  std::time_t t = s.count();
  std::size_t fractional_seconds = ms.count() % 1000;

  std::cout << std::ctime(&t) << std::endl;
  std::cout << fractional_seconds << std::endl;
}

exemple de résultat:

Thu Oct 11 19:10:24 2012

925
58
Akira Takahashi

Suit le code explicite qui crée le premier un std::tm correspondant au 10-10-2012 12:38:40, convertit cela en un std::chrono::system_clock::time_point, ajoute 0,123456 seconde, puis l’imprime en reconvertissant en un std::tm. La dernière étape consiste à gérer les secondes fractionnelles.

#include <iostream>
#include <chrono>
#include <ctime>

int main()
{
    // Create 10-10-2012 12:38:40 UTC as a std::tm
    std::tm tm = {0};
    tm.tm_sec = 40;
    tm.tm_min = 38;
    tm.tm_hour = 12;
    tm.tm_mday = 10;
    tm.tm_mon = 9;
    tm.tm_year = 112;
    tm.tm_isdst = -1;
    // Convert std::tm to std::time_t (popular extension)
    std::time_t tt = timegm(&tm);
    // Convert std::time_t to std::chrono::system_clock::time_point
    std::chrono::system_clock::time_point tp = 
                                     std::chrono::system_clock::from_time_t(tt);
    // Add 0.123456 seconds
    // This will not compile if std::chrono::system_clock::time_point has
    //   courser resolution than microseconds
    tp += std::chrono::microseconds(123456);

    // Now output tp

    // Convert std::chrono::system_clock::time_point to std::time_t
    tt = std::chrono::system_clock::to_time_t(tp);
    // Convert std::time_t to std::tm (popular extension)
    tm = std::tm{0};
    gmtime_r(&tt, &tm);
    // Output month
    std::cout << tm.tm_mon + 1 << '-';
    // Output day
    std::cout << tm.tm_mday << '-';
    // Output year
    std::cout << tm.tm_year+1900 << ' ';
    // Output hour
    if (tm.tm_hour <= 9)
        std::cout << '0';
    std::cout << tm.tm_hour << ':';
    // Output minute
    if (tm.tm_min <= 9)
        std::cout << '0';
    std::cout << tm.tm_min << ':';
    // Output seconds with fraction
    //   This is the heart of the question/answer.
    //   First create a double-based second
    std::chrono::duration<double> sec = tp - 
                                    std::chrono::system_clock::from_time_t(tt) +
                                    std::chrono::seconds(tm.tm_sec);
    //   Then print out that double using whatever format you prefer.
    if (sec.count() < 10)
        std::cout << '0';
    std::cout << std::fixed << sec.count() << '\n';
}

Pour moi cela génère:

10-10-2012 12:38:40.123456

Votre std::chrono::system_clock::time_point peut être ou ne pas être assez précis pour contenir des microsecondes.

Mettre à jour

Un moyen plus simple consiste simplement à utiliser cette bibliothèque de dates . Le code se simplifie jusqu'à (en utilisant des littéraux de durée C++ 14):

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

int
main()
{
    using namespace date;
    using namespace std::chrono;
    auto t = sys_days{10_d/10/2012} + 12h + 38min + 40s + 123456us;
    static_assert(std::is_same<decltype(t),
                               time_point<system_clock, microseconds>>{}, "");
    std::cout << t << '\n';
}

qui produit:

2012-10-10 12:38:40.123456

Vous pouvez ignorer le static_assert si vous n’avez pas besoin de prouver que le type de t est un std::chrono::time_point.

Si la sortie ne vous convient pas, par exemple, vous aimeriez vraiment commander jj-mm-aaaa, vous pouvez:

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

int
main()
{
    using namespace date;
    using namespace std::chrono;
    using namespace std;
    auto t = sys_days{10_d/10/2012} + 12h + 38min + 40s + 123456us;
    auto dp = floor<days>(t);
    auto time = make_time(t-dp);
    auto ymd = year_month_day{dp};
    cout.fill('0');
    cout << ymd.day() << '-' << setw(2) << static_cast<unsigned>(ymd.month())
         << '-' << ymd.year() << ' ' << time << '\n';
}

ce qui donne exactement le résultat demandé:

10-10-2012 12:38:40.123456

Mettre à jour

Voici comment formater proprement le temps UTC actuel avec une précision de quelques millisecondes:

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

int
main()
{
    using namespace std::chrono;
    std::cout << date::format("%F %T\n", time_point_cast<milliseconds>(system_clock::now()));
}

qui vient de sortir pour moi:

2016-10-17 16:36:02.975

C++ 17 vous permettra de remplacer time_point_cast<milliseconds> avec floor<milliseconds>. Jusque là date::floor est disponible dans "date.h".

std::cout << date::format("%F %T\n", date::floor<milliseconds>(system_clock::now()));
30
Howard Hinnant

En général, vous ne pouvez pas le faire de manière simple. time_point est essentiellement juste un duration d’une époque spécifique à une horloge.

Si tu as un std::chrono::system_clock::time_point, alors vous pouvez utiliser std::chrono::system_clock::to_time_t pour convertir le time_point à un time_t, puis utilisez les fonctions C normales telles que ctime ou strftime pour le formater.


Exemple de code:

std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(tp);
std::tm timetm = *std::localtime(&time);
std::cout << "output : " << std::put_time(&timetm, "%c %Z") << "+"
          << std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_Epoch()).count() % 1000 << std::endl;
7
Anthony Williams

Cela a fonctionné pour moi pour un format tel que YYYY.MM.DD-HH.MM.SS.fff. Vouloir rendre ce code capable d’accepter n’importe quel format de chaîne reviendra à réinventer la roue (c’est-à-dire qu’il existe des fonctions pour tout cela dans Boost.

std::chrono::system_clock::time_point string_to_time_point(const std::string &str)
{
    using namespace std;
    using namespace std::chrono;

    int yyyy, mm, dd, HH, MM, SS, fff;

    char scanf_format[] = "%4d.%2d.%2d-%2d.%2d.%2d.%3d";

    sscanf(str.c_str(), scanf_format, &yyyy, &mm, &dd, &HH, &MM, &SS, &fff);

    tm ttm = tm();
    ttm.tm_year = yyyy - 1900; // Year since 1900
    ttm.tm_mon = mm - 1; // Month since January 
    ttm.tm_mday = dd; // Day of the month [1-31]
    ttm.tm_hour = HH; // Hour of the day [00-23]
    ttm.tm_min = MM;
    ttm.tm_sec = SS;

    time_t ttime_t = mktime(&ttm);

    system_clock::time_point time_point_result = std::chrono::system_clock::from_time_t(ttime_t);

    time_point_result += std::chrono::milliseconds(fff);
    return time_point_result;
}

std::string time_point_to_string(std::chrono::system_clock::time_point &tp)
{
    using namespace std;
    using namespace std::chrono;

    auto ttime_t = system_clock::to_time_t(tp);
    auto tp_sec = system_clock::from_time_t(ttime_t);
    milliseconds ms = duration_cast<milliseconds>(tp - tp_sec);

    std::tm * ttm = localtime(&ttime_t);

    char date_time_format[] = "%Y.%m.%d-%H.%M.%S";

    char time_str[] = "yyyy.mm.dd.HH-MM.SS.fff";

    strftime(time_str, strlen(time_str), date_time_format, ttm);

    string result(time_str);
    result.append(".");
    result.append(to_string(ms.count()));

    return result;
}
3
Darien Pardinas

J'aurais mis cela dans un commentaire sur la réponse acceptée, puisque c'est là sa place, mais je ne peux pas. Donc, juste au cas où quelqu'un obtiendrait des résultats peu fiables, cela pourrait être la raison.

Faites attention à la réponse acceptée, elle échouera si le time_point est avant l’époque.

Cette ligne de code:

std::size_t fractional_seconds = ms.count() % 1000;

produira des valeurs inattendues si ms.count () est négatif (puisque size_t n'est pas censé contenir des valeurs négatives).

3
user3074363

Dans mon cas, j'utilise chrono et la fonction c localtime_r qui est thread-safe (contrairement à std :: localtime).

#include <iostream>
#include <chrono>
#include <ctime>
#include <time.h>
#include <iomanip>


int main() {
  std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
  std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
  std::chrono::milliseconds now2 = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_Epoch());
  struct tm currentLocalTime;
  localtime_r(&currentTime, &currentLocalTime);
  char timeBuffer[80];
  std::size_t charCount { std::strftime( timeBuffer, 80,
                                         "%b %d %T",
                                          &currentLocalTime)
                         };

  if (charCount == 0) return -1;

  std::cout << timeBuffer << "." << std::setfill('0') << std::setw(3) << now2.count() % 1000 << std::endl;
  return 0;
}
0
rodolk