Преобразование дней с начала григорианского календаря в дату

Я работаю с файлами netCDF4, которые содержат измерение времени. Значения этого измерения времени представляют собой двойные числа, представляющие дни с 15 10 1582 00:00. Чтобы пользователи могли взаимодействовать с данными, мне нужно преобразовать эти метки времени в фактические даты (например, 01:01:2014T00:00:00).

Исследуя это, я не смог найти решение для этого в С++. Библиотека netCDF Python предоставляет num2date, а также date2num, которые делают именно то, что мне нужно. К сожалению, я не смог найти эти функции в C++ lib. Я был бы признателен за любые предложения о том, как это сделать.


person A. Osterthun    schedule 27.08.2019    source источник
comment
Не по теме: double кажется довольно неуместным для подсчета дней. int64_t намного точнее в этом вопросе (у вас доступно 64 бита, тогда как с двойным значением только 53 — если вам нужно больше бит, то происходит округление, и вы получите нежелательные результаты). Хорошо, вы можете использовать точность субдня с двойным, но даже 0,2 дня не могут быть представлены точно (периодически в двоичном формате)...   -  person Aconcagua    schedule 27.08.2019
comment
Я согласен, но я не в том положении, чтобы это изменить. Файлы netCDF, которые я использовал, созданы не мной, а третьей стороной.   -  person A. Osterthun    schedule 27.08.2019
comment
В руководстве рекомендуется эта библиотека.   -  person Hans Passant    schedule 27.08.2019


Ответы (2)


Это очень легко сделать с помощью библиотеки времени/даты Говарда Хиннанта.

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

date::sys_time<std::chrono::seconds>
to_time_point(double d)
{
    using namespace date;
    using namespace std::chrono;
    using ddays = duration<double, days::period>;
    return round<seconds>(ddays{d} + sys_days{1582_y/10/15});
}

int
main()
{
    using namespace date;
    std::cout << to_time_point(159562.572297) << '\n';
}

Выход:

2019-08-27 13:44:06

Вы не упомянули, какую точность вы хотели, но из вашего примера я предположил, что это секунды. Вы можете выбрать любую хроноточность, которую пожелаете. Эта программа просто преобразует ваши double в основанную на double хроно-длительность с периодом days, а затем добавляет ее к эпохе. В результате получается chrono::system_clock::time_point с выбранной вами точностью. Хотя я рекомендую вам не использовать nanoseconds. Точность nanoseconds на 3 порядка выше, чем у double в этом диапазоне, а диапазон sys_time<nanoseconds> составляет [1677-09-21 00:12:43.145224192, 2262-04-11 23:47:16.854775807]. Тогда как диапазон sys_time<microseconds> составляет +/-292 тысячи лет (много).

Вы можете использовать эту же библиотеку для форматирования этого time_point в любом стиле. Например:

std::cout << format("%d:%m:%YT%T", to_time_point(159562.572297)) << '\n';

Выход:

27:08:2019T13:44:06

Использование этой библиотеки очень хорошо мигрирует на C++20, если вы когда-нибудь решите использовать этот более новый стандарт, как только он станет доступным.

person Howard Hinnant    schedule 27.08.2019

person    schedule
comment
Спасибо. Но я думаю, что это решение не учитывает високосные дни. Таким образом, результирующая дата может немного отличаться. Если я ошибаюсь, как мне настроить эпоху? Меня почему-то очень беспокоит работа с датами... - person A. Osterthun; 27.08.2019
comment
@А. Osterthun: високосных дней не бывает - при условии, что значения, которые вы получаете от netCDF4, верны! Преобразование эпохи: дайте мне 5 минут, и я опубликую редактирование, показывающее, как это сделать! - person Adrian Mole; 27.08.2019
comment
Это было бы замечательно. Я очень благодарен за вашу поддержку. Но я не совсем понимаю, почему не приходят високосные дни. - person A. Osterthun; 27.08.2019
comment
@А. Osterthun — функция gmtime() заботится о високосных днях — при условии, что дата не ранее 1 января 1970 года. - person Adrian Mole; 27.08.2019