Блог им. elektroyar

Работа с датой и временем в С++

В свое время для алготрейдерских задач мне нужно было много оперировать датой и временем. Конечно, в С++ и Си есть библиотеки для работы с датой и временем. Но мне захотелось сделать свой велосипед, который бы мог легко и удобно превращать строковое представление времени в метку времени, менять часовой пояс, получать время UTC компьютера, преобразовывать метку времени в стандартный формат даты и времени и обратно и т.д. и т.п. Одним словом, целый спектр задач.

В итоге я сделал библиотеку xtime (ну, громко сказано «библиотека», это всего лишь два файла .cpp и .hpp). Для хранения и преобразования меток времени используется тип данных uint64 либо double, поэтому у данной библиотеки нет проблемы 2038 года.

Используемые типы данных:
  • timestamp_t — тип длиной 64 бита для хранения метки времени.
  • ftimestamp_t - тип с плавающей точкой длиной 64 бита для хранения метки времени с дробной частью секунд.
  • oadate_t - тип с плавающей точкой длиной 64 бита для хранения даты автоматизации (OADate)

Что умеет библиотека, покажу на примерах.

Нужно получить время UTC на компьютере вне зависимости от текущего часового пояса? Без проблем:
#include <iostream>
#include <xtime.hpp>

uint32_t main() {
    std::cout << "Hello world!" << std::endl;
    // Получаем метку времени компьютера
    xtime::timestamp_t t = get_timestamp();
    // выводим в человекочитабельном формате
    std::cout << xtime::get_str_date_time(t) << std::endl;
    /* но можно сразу строку получить
     * В строке время будет представлено как 
     * в примере (24.05.2018 00:00:00)
     */
    std::string str = get_str_date_time();
    /* а может нужен формат OLE Automation Date 
     *(Дата автоматизации OLE)
     */
    std::cout << "oadate " << xtime::get_oadate() << endl;
    return 0;
}
Хочется иметь стандартное представление времени (часы, минуты и пр.) в виде класса с соответствующими объектами? Без проблем:
using namespace xtime;

// Инициализируем датой 24.05.2018
DateTime iTime(24,5,2018);

// Второй вариант инициализации с указанием времени
iTime = DateTime(24,5,2018, 0, 0, 0);
// iTime = DateTime(24,5,2018);

// Третий вариант инициализации (Инициализация с указанием unix-времени в формате ISO)
// iTime = DateTime("2013-12-06T15:23:01+00:00");

// Или инициализируем Unix epoch или Unix time или POSIX time или Unix timestamp
xtime::timestamp_t unix_epoch = 1527120000;

iTime.set_timestamp(unix_epoch);

// Переменные класса DateTime
iTime.day = 24; // день
iTime.month = 5; // месяц
iTime.year = 2018 // год
iTime.hour = 0; // час
iTime.minutes = 0; // минуты
iTime.seconds = 0; // секунды

// Получить Unix epoch или Unix time или POSIX time или Unix timestamp 
unix_epoch = iTime.get_timestamp();

// Вывести время и дату на экран
iTime.print();

// Получить дату и время в виде строки
std::string str = iTime.get_str_date_time(); // В строке будет 24.05.2018 00:00:00
Конечно же можно получать день недели, день года, количество дней в месяце, метку времени начала или конца дня, час, минуту от метки времени и многое другое.

К примеру, получить день недели:
using namespace xtime;

// Получить номер дня недели
uint32_t wday = get_weekday(24,5,2018);

if(wday == SUN) std::cout << "SUN" << std::endl; // Если функция вернула 0 или Воскресенье
else if(wday == MON) std::cout << "MON" << std::endl; // Если функция вернула 1 или Понедельник
else if(wday == TUS) std::cout << "TUS" << std::endl;
else if(wday == WED) std::cout << "WED" << std::endl;
else if(wday == FRI) std::cout << "FRI" << std::endl;
else if(wday == SAT) std::cout << "SAT" << std::endl;

xtime::timestamp_t unix_epoch = 1527120000;

// Второй вариант функции для определения дня недели
wday = get_weekday(unix_epoch);

// Получить день недели через метод класса DateTime
DateTime iTime(24,5,2018);
wday = iTime.get_weekday();
Конвертировать строку в формате ISO в данные класса DateTime:
using namespace xtime;

DateTime iTime;
std::string strISOformattedUTCdatetime = "2013-12-06T15:23:01+00:00";
if(convert_iso(strISOformattedUTCdatetime, iTime) == true) {
  iTime.print();
}
Перевод времени CET во время GMT и обратно с учетом перехода на зимнее время
using namespace xtime;
// получаем время GMT для примера
DateTime startTime(20,3,2018);

xtime::timestamp_t startGMT = startTime.get_timestamp();
// переводим время GMT во время CET
DateTime realCET(convert_gmt_to_cet(startGMT));
realCET.print();
// переводим время CET во время GMT
DateTime realGMT(convert_cet_to_gmt(realCET.get_timestamp()));
realGMT.print();
А еще можно получить синхронизированное UTC время
#include <iostream>
#include <xtime_sync.hpp>

uint32_t main() {
    std::cout << "Hello world!" << std::endl;
    // класс для получения синхронизированного времени
    xtime::TimeSync iTimeSync;
    while(!iTimeSync.is_time_sync()) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    };

    double last_utc = iTimeSync.get_ftimestamp();
    while(true) {
        double real_utc = iTimeSync.get_ftimestamp();
        double pc_utc = xtime::get_ftimestamp();
        if(real_utc - last_utc > 0.1) {
            std::cout 
            << "accuracy: " 
            << iTimeSync.get_accuracy() 
            << " sync utc: " << xtime::get_str_time_ms(real_utc) 
            << " pc utc: " << xtime::get_str_time_ms(pc_utc) << "\r";
            last_utc = real_utc;
        }
    };
    return 0;
}
В последнем случае вам нужно будет подключать в проект библиотеку curl.

Репозиторий с кодом
★8
21 комментарий
этот пост для тех, кто не умеет программировать?
avatar
meat, в том числе
avatar
elektroyar, мне кажется ты сайтом ошибся, тебе на хабр и прочие ресурсы, без обид :)
avatar
meat, работа с временем чаще пригождается в околотрейдреских разработках. Поэтому для тех, кто строит свои системы, может пригодиться. 
avatar
elektroyar, есть куча вещей, которые могут пригодиться в трейдинге, но работа с датами не только в трейдинге происходит

у тебя по сути еще одна библиотека для работы с датами с привязкой к C++
avatar
А какие тайминги у либы? Сколько занимает получение таймстемпа текущего времени? И какая разрешающая способность?
avatar
suren, через компьютер если? вызов xtime::get_timestamp() занимает 8* 10 в минус 8 степени. Если с интернетом синхронизировать, то точность зависит от времени ответа сервера, который присылает время. После нескольких замеров рассчитывается среднее смещение между временем компьютера и сервера, и дальше метка времени возвращается при вызове метода уже со смещением. На практике я получал 3 сигмы в районе 0.3 секунд после синхронизации, было примерно 50-100 замеров времени ответа сервера.
avatar
boost::posix_time
std, boost ::chrono

надо учится эффективно использовать чужие велосипеды, иначе легко завязнуть в своих
avatar
bwc, std везде есть, а вот тащить boost ради лишь метки времени, можно, но зачем?
avatar
elektroyar, экономии времени ради. чтобы не проектировать-писать-тестировать свое.
avatar
bwc, еще в своей либе я добавил специфичные функции, типа «Получить последнюю метку времени последнего воскресения текущего месяца», вроде как у boost::posix_time такого нет. 
avatar
bwc, если нужно быстро вычислять, boost сразу в помойку
avatar

Андрей К, я про _быстро_ мало знаю.
целочисленные boost, std ::chrono, boost::posix_time - медленные?

avatar
bwc, ну вот так вот прямо нельзя сказать что бусты сверх медленные. Но их же создают для универсального решения задач, поэтому там много перестраховок. Идет нагромождение кода. А это напрямую влияет на производительность. Например, плавают такие величины, как время вызова их кода. Один раз кусок может отработать за микросеку, второй раз за 10мксек.

есть много алго, где время взятия меток очень ощутимо для самого алго. Вот именно тут, лучше делать кастом.
avatar
Андрей К, а, не. concern был по поводу chrono. что там все тяжело (а смотреть лень) и можно сделать как тут
кастом — это личное, свои требования, окружение, история. нет смысла обсуждать исходя из общих представлений.
avatar
Глянул гитхаб. Все базируется на struct tm.

Чисто для дальнейшего опыта. Quik уже работает с микросеками. Сама биржа наша уже работает с наносеками. Текущему коду есть куда стремиться. Так как tm не умеет ни микросеки, ни наносеки =).

Синхрон тоже. Проткол ntp умеет синхронить до микросек. ptp до наносек. Но наша биржа ptp никак не хочет вводить
avatar
Андрей К, мне кажется ему все равно, что ты тут пишешь 




avatar
meat, ааа =) ну тогда да
avatar
А как можно повернуть время назад?
avatar
Technotrade, когда Эммет Браун форкнет репозиторий, думаю можно будет
avatar
Конечно, в С++ и Си есть библиотеки для работы с датой и временем. Но мне захотелось сделать свой велосипед

Настоящий русский программист 
avatar

теги блога elektroyar

....все тэги



UPDONW
Новый дизайн