Как сделать так, чтобы значения «дней с тех пор» играли хорошо с std :: chrono? - PullRequest
2 голосов
/ 09 июля 2019

Я получаю от волшебного гремлина массив неотрицательных целочисленных значений в памяти, скажем, типа I.Каждый из них представляет количество дней, прошедших с некоторой эпохи (что за много лет до 0 в календаре ISO 8601).

Теперь я хочу обернуть эти значения - по сути, через указатель или реинтерпретацию - классами C ++,который может хорошо взаимодействовать с кодом std::chrono, или, может быть, я должен сказать, что он используется с кодом, который обычно занимает std::chrono временных и длительных периодов.

Каков будет мой лучший способ действий для этого?Примечание. Я не хочу заменять какие-либо данные в массиве и создавать новые значения для каждого из существующих в других местах.

Ответы [ 2 ]

6 голосов
/ 10 июля 2019

Каждый из них представляет количество дней с начала эпохи (то есть за много лет до 0 в календаре ISO 8601).

Допустим, вы имеете в виду эпоху Юлианского Дня № . Эта эпоха соответствует вашему описанию. Ссылка говорит, что эта эпоха - 24 ноября 4714 г. до н.э. в грипористическом календаре. Вместо того, чтобы использовать систему "BC", я считаю удобным использовать систему с отрицательными годами, чтобы обеспечить плавный математический переход через год 0. В этой системе эпоха - 24 ноября -4713.

Используя бесплатную библиотеку дат Говарда Хиннанта с открытым исходным кодом, содержащую только заголовки , это очень легко сделать. Если у меня неправильная эпоха, просто замените правильную в очевидном месте.

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

date::sys_days
to_sys_days(int i)
{
    using namespace date;
    return sys_days{days{i} -
        (sys_days{1970_y/January/1} - sys_days{-4713_y/November/24})};
}

Тип возврата date::sys_days - std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<int, std::ratio<86400>>>. Или по-английски: это system_clock на основе time_point с точностью до days. Это time_point будет неявно преобразовано в system_clock::time_point.

вашей платформы.

Теперь вы можете передать int в to_sys_days и передать результат функции, принимающей system_clock::time_point. Например:

void
display(std::chrono::system_clock::time_point tp)
{
    using date::operator<<;
    std::cout << tp << '\n';
}

int
main()
{
    display(to_sys_days(2'458'674));
}

Это выводит:

2019-07-09 00:00:00.000000

to_sys_days - это очень дешевая операция. Вы можете позволить себе делать это каждый раз, когда читаете один элемент данных. Все, что он делает, это вычитает 2440588 из i. Оптимизированный машинный код для to_sys_days (clang ++ -O3) буквально:

leal    -2440588(%rdi), %eax

т.е. все операции по изменению типов происходят во время компиляции . Это бесплатно. Единственное, что происходит во время выполнения, - это корректировка смещения эпохи. Это самый необходимый минимум, который нужно сделать независимо от того, что выровнять свою эпоху с system_clock эпохой.

Итак, если у вас есть массив int в качестве ваших данных, вам не нужно делать копию всего массива. Вы просто трансформируете каждый его элемент по требованию. Например:

int
main()
{
    int data[] = {2'458'674, 2'458'675, 2'458'676, 2'458'677, 2'458'678};
    for (auto i : data)
        display(to_sys_days(i));
}

Выход:

2019-07-09 00:00:00.000000
2019-07-10 00:00:00.000000
2019-07-11 00:00:00.000000
2019-07-12 00:00:00.000000
2019-07-13 00:00:00.000000

Если вы не хотите использовать библиотеку дат , вы все равно можете выполнить работу, это просто немного больше работы.

Сначала создайте тип duration, который означает дни:

using days = std::chrono::duration
    <int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;

Затем подсчитайте количество дней между 1970-01-01 и вашей эпохой.

Затем возьмите свое интегральное значение i, оберните его в days и вычтите разницу в времени. Затем вы можете построить system_clock::time_point со значением days.

Примечание. Важно выполнить настройку эпохи в единицах days вместо преобразования сначала в единицы system_clock::time_point, а затем выполнить настройку. Эта последняя стратегия будет переполнена на некоторых платформах. Вы защищены от переполнения, если вы делаете смещение эпохи с точностью days.


Я настоятельно не рекомендую использовать инструмент reinterpret_cast для выполнения этой работы. Это кажется ненужным и опасным.


Обновление

Я забыл о части, в которой говорится, что юлианская эпоха - это полдень, а не полночь. Если вы хотите принять это во внимание, это очень просто с date lib :

auto
to_sys_days(int i)
{
    using namespace date;
    using namespace std::chrono;
    return sys_time<hours>{days{i} -
        (sys_days{1970_y/January/1} - sys_days{-4713_y/November/24} - 12h)};
}

Я просто вычел 12 часов из разности эпох и позволил auto определить для меня тип возврата (который теперь system_clock на основе time_point с точностью hours).

Точно такая же функция display с тем же входом теперь выводит:

2019-07-09 12:00:00.000000
0 голосов
/ 10 июля 2019

Если это действительно длительность, вы можете просто набрать

std::chrono::days(integer_value);

, и вы получите хронографический тип продолжительности.

...