Как преобразовать std :: chrono :: time_point в календарную строку datetime с дробными секундами?

Как преобразовать std::chrono::time_point в календарную строку даты и времени с дробными секундами?

Например:

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

person boxx    schedule 11.10.2012    source источник


Ответы (7)


Если system_clock, у этого класса есть преобразование 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
}

пример результата:

Thu Oct 11 19:10:24 2012

РЕДАКТИРОВАТЬ: Но time_t не содержит дробных секунд. Альтернативный способ - использовать функцию time_point :: time_since_epoch (). Эта функция возвращает продолжительность от эпохи. Следующий пример - дробное разрешение в миллисекундах.

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

пример результата:

Thu Oct 11 19:10:24 2012

925
person Akira Takahashi    schedule 11.10.2012
comment
И как добавить к выходной строке доли секунды? - person PaperBirdMaster; 11.10.2012
comment
time_t не содержит дробных секунд. Я добавляю еще образец кода. - person Akira Takahashi; 11.10.2012
comment
Спасибо! теперь ответы лучше и ближе к тому, о чем спрашивал оператор. - person PaperBirdMaster; 11.10.2012
comment
en.cppreference.com/w/cpp/chrono/time_point/time_since_epoch пример подтверждает - person Ciro Santilli 新疆再教育营六四事件ۍ 18.12.2016
comment
Это работает, только если сама эпоха не содержит дробных секунд :) - person Konstantin; 02.01.2017
comment
Для информации, std::ctime не является потокобезопасным и согласно cppreference , POSIX помечает его как устаревший. - person Louis Dionne; 01.12.2017
comment
Если единственный способ - преобразовать в time_t, то я не вижу причин использовать chrono - person zzz777; 31.01.2020
comment
Для информации: std::ctime переполняется за 2261 год и ниже 1678 года. - person Adrian Maire; 30.04.2021

Далее следует понятный код, который сначала создает std::tm, соответствующий 10-10-2012 12:38:40, преобразует его в std::chrono::system_clock::time_point, добавляет 0,123456 секунды, а затем распечатывает это, преобразовывая обратно в std::tm. Обработка дробных секунд находится в самом последнем шаге.

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

Для меня это выводы:

10-10-2012 12:38:40.123456

Ваш std::chrono::system_clock::time_point может быть достаточно точным, а может и не быть достаточно точным, чтобы удерживать микросекунды.

Обновить

Более простой способ - просто использовать эту библиотеку дат. Код упрощается до (с использованием литералов продолжительности 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';
}

который выводит:

2012-10-10 12:38:40.123456

Вы можете пропустить static_assert, если вам не нужно доказывать, что тип t - это std::chrono::time_point.

Если результат вам не нравится, например, вам действительно нужен порядок дд-мм-гггг, вы можете:

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

что дает именно требуемый результат:

10-10-2012 12:38:40.123456

Обновить

Вот как аккуратно отформатировать текущее время в формате UTC с точностью до миллисекунд:

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

который просто выводит для меня:

2016-10-17 16:36:02.975

C ++ 17 позволит вам заменить time_point_cast<milliseconds> на floor<milliseconds>. До тех пор date::floor доступен в "date.h".

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

Обновление C ++ 20

В C ++ 20 теперь это просто:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std::chrono;
    auto t = sys_days{10d/10/2012} + 12h + 38min + 40s + 123456us;
    std::cout << t << '\n';
}

Или просто:

std::cout << std::chrono::system_clock::now() << '\n';

std::format будет доступен для настройки вывода.

person Howard Hinnant    schedule 11.10.2012
comment
Это находится в процессе предложения комитету по стандартам C ++: howardhinnant.github.io/date/ d0355r1.html - person Howard Hinnant; 17.10.2016

В общем, вы не можете сделать это каким-либо простым способом. time_point - это, по сути, просто duration из эпохи, связанной с часами.

Если у вас есть std::chrono::system_clock::time_point, вы можете использовать std::chrono::system_clock::to_time_t для преобразования time_point в time_t, а затем использовать обычные функции C, такие как ctime или strftime, для его форматирования.


Пример кода:

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;
person Anthony Williams    schedule 11.10.2012
comment
Проблема с этим решением заключается в том, что std :: localtime не является потокобезопасным. - person minexew; 29.08.2017

У меня это сработало для такого формата, как YYYY.MM.DD-HH.MM.SS.fff. Попытка сделать этот код способным принимать любой строковый формат будет похоже на изобретение колеса (т.е. в 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;
}
person Darien Pardinas    schedule 30.09.2014
comment
На самом деле, когда есть только 1 MS, вы получите неправильный результат, поскольку to_String () преобразует это Long (ms.count (()). Вам нужно преобразовать миллисекунды с помощью sprintf_s. - person schorsch_76; 03.08.2015
comment
Мне не нужны были десятичные секунды, поэтому я их снял. Затем мне пришлось использовать strftime(time_str, strlen(time_str)+1, date_time_format, ttm);. В противном случае функция думает, что недостаточно места для последнего поля, и не заполняет его. Я думаю, что это приемлемо, потому что time_str фактически имеет одно дополнительное место для нулевого терминатора. - person Brandon; 10.07.2018
comment
Возможно, стоит упомянуть, что это работает только для UTC (не для часового пояса, без DST). - person Adrian Maire; 30.04.2021

Я бы добавил это в комментарий к принятому ответу, так как он принадлежит ему, но я не могу. Итак, на всякий случай, если кто-то получит ненадежные результаты, может быть, поэтому.

Будьте осторожны с принятым ответом, он не работает, если time_point находится до эпохи.

Эта строка кода:

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

приведет к неожиданным значениям, если ms.count () отрицательно (поскольку size_t не предназначен для хранения отрицательных значений).

person user3074363    schedule 23.11.2015
comment
Время до эпохи? Почему ? :) - person Ahmet Ipkin; 29.03.2016
comment
Потому что мир был создан не в 1970 году. - person Neutrino; 11.07.2016

В моем случае я использую функцию chrono и c localtime_r, которая является потокобезопасной (в отличие от 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;
}
person rodolk    schedule 28.02.2019

Если вы хотите отформатировать system_clock::time_point в формате numpy datetime64, вы можете использовать:

std::string format_time_point(system_clock::time_point point)
{
    static_assert(system_clock::time_point::period::den == 1000000000 && system_clock::time_point::period::num == 1);
    std::string out(29, '0');
    char* buf = &out[0];
    std::time_t now_c = system_clock::to_time_t(point);
    std::strftime(buf, 21, "%Y-%m-%dT%H:%M:%S.", std::localtime(&now_c));
    sprintf(buf+20, "%09ld", point.time_since_epoch().count() % 1000000000);
    return out;
}

пример вывода: 2019-11-19T17:59:58.425802666

person doraemon    schedule 19.11.2019