Преобразование uint64 времени для числа 100 наносекундных интервалов с 1-го января 1601 года до даты строки времени

У меня есть значение времени, которое начинается с файла ******. Значение - это число 100 наносекундных интервалов с 1 января 1601 года. Я понимаю, что тип Windows FILETIME использует этот формат. Мне нужно, чтобы этот ****** преобразовывался в какой-то объект, где я могу читать год, дату, час, мин и т.д. В строчном формате, поэтому я могу создать пользовательскую строку времени даты.

Как я могу получить, что ****** преобразован в что-то полезное. Я получаю ошибку компиляции со всеми способами, которые я пытался использовать для ****** для времени файла, например

****** big_int; // this will end up containing the nanosecond interval time
.
.
.

FILETIME t = static_cast<filetime>(big_int);
</filetime>
1 ответ

Я принял это как вызов, чтобы выполнить это вычисление с помощью chrono -Compatible низкоуровневой Дата Алгоритмы, Утилит, и С++ 11 объекты.

Из chrono -Compatible Алгоритмы даты низкого уровня нам нужны:

template <class int="">
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept;
</class>

который преобразует ay/m/d triple в несколько дней до/с 1970-01-01 и:

template <class int="">
constexpr
std::tuple<int, unsigned,="" unsigned="">
civil_from_days(Int z) noexcept;
</int,></class>

который преобразует несколько дней до/с 1970-01-01 до ay/m/d triple.

Из Утилиты нам нужно:

template <class to,="" rep,="" period="">
To
floor(const std::chrono::duration<rep, period="">& d);
</rep,></class>

который очень похож на std::chrono::duration_cast, за исключением того, что он округляется до отрицательной бесконечности, а не к нулю (имеет значение для округления отрицательных значений).

Сначала нам нужен объект datetime для хранения всей необходимой информации:

struct datetime
{
 int year;
 int month;
 int day;
 int hour;
 int minute;
 int second;
 int nanoseconds;
};

И типография удобна для демонстрационных целей (которые должны быть настроены в любом формате):

std::ostream&
operator<<(std::ostream& os, const datetime& dt)
{
 char fill = os.fill();
 os.fill('0');
 os << dt.year << '-';
 os << std::setw(2) << dt.month << '-';
 os << std::setw(2) << dt.day << " T ";
 os << std::setw(2) << dt.hour << ':';
 os << std::setw(2) << dt.minute << ':';
 os << std::setw(2) << dt.second << '.';
 os << std::setw(9) << dt.nanoseconds;
 os.fill(fill);
 return os;
}

Я выбрал единицы nanoseconds как точность datetime, которая немного переборщила. Вы можете легко установить это на желаемое.

Далее удобно объявить две пользовательские std::chrono::duration:

Один для представления дней (ровно 24 часа):

typedef std::chrono::duration
 <
 int,
 std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
 > days;
</std::ratio<24>

И один, чтобы представить интервал 100 наносекунд из задания проблемы:

typedef std::chrono::duration
 <
 std::int64_t, std::ratio_multiply<std::ratio<100>, std::nano>
 > wtick;
</std::ratio<100>

Теперь у нас есть все инструменты, необходимые для преобразования std::******** в datetime (предмет этого вопроса):

datetime
datetime_from_wtick(wtick t)
{
 // Get the number of days between 1601-01-01 and 1970-01-01
 constexpr days epoch{days_from_civil(1601, 1, 1)};
 // eppoch is a negative number of days, so add it to get time since 1970-01-01
 wtick utc_time = t + epoch;
 days d = floor<days>(utc_time); // Get #days before/since 1970-01-01
 datetime r;
 // Split #days into year/month/day
 std::tie(r.year, r.month, r.day) = civil_from_days(d.count());
 utc_time -= d; // Subtract off #days to leave hours:minutes:seconds.fractional
 auto h = floor<std::chrono::hours>(utc_time); // Get hours
 r.hour = h.count();
 utc_time -= h; // Subtract off hours to leave minutes:seconds.fractional
 auto m = floor<std::chrono::minutes>(utc_time); // Get minutes
 r.minute = m.count();
 utc_time -= m; // Subtract off minutes to leave seconds.fractional
 auto s = floor<std::chrono::seconds>(utc_time); // Get seconds
 r.second = s.count();
 utc_time -= s; // Subtract off seconds to leave fractional seconds
 std::chrono::nanoseconds ns = utc_time; // Get nanoseconds
 r.nanoseconds = ns.count();
 return r;
}
</std::chrono::seconds></std::chrono::minutes></std::chrono::hours></days>

Это решение работает, просто сдвигая эпоху в 1970-01-01, затем урезая количество дней, затем количество часов, затем количество минут и т.д., Пока мы не опустимся до доли секунды. Количество дней далее делится на тройку: y/m/d.

Единственная причина, чтобы сдвигать эпоху является воспользоваться полностью отлажено, высокой производительностью и очень-устойчивых формулами в chrono -Compatible низкоуровневая Дата алгоритмов.

Это решение игнорирует существование секунд прыжка. Я предполагаю, что Windows FILETIME тоже. Однако если это не так, вы можете взять високосные секунды во внимание путем создания таблицы, которая отображает datetime и datetime для #seconds, чтобы добавить. Например, если время datetime выходит за пределы текущей таблицы секунд прыжка, вы добавляете 25 секунд, чтобы получить "истинную" разницу между теперь и 1601-01-01. Мой опыт заключается в том, что компьютеры используют Unix Time, который просто заглядывает на секунды прыжка.

Тестирование всего этого:

int
main()
{
 std::cout << datetime_from_wtick(wtick(130330211760000005)) << '\n';
}

Что должно дать:

2014-01-01 T 03:39:36.000000500

Предназначен для представления datetime в часовом поясе UTC.

На этот пост есть два основных момента:

  1. Правильно используется , чтобы практически исключить все константы преобразования. В этом примере представлены только несколько констант преобразования, которые используются для определения пользовательских std::chrono:duration s и сдвига в эпоху. После этого машина работает, устраняя распространенные ошибки.

  2. chrono -Compatible Low-Level Дата Алгоритмы имеет некоторые действительно полезные и эффективные алгоритмы.

Обновить

Работа с длительностью без знака действительно подвержена ошибкам, а 63 бит wtick - это большой диапазон. Изменен wtick::rep на int64_t. Это дает гораздо лучшие результаты:

int
main()
{
 std::cout << datetime_from_wtick(wtick(0)) << '\n';
 std::cout << datetime_from_wtick(wtick(864000000000)) << '\n';
 std::cout << datetime_from_wtick(wtick(130330211760000005)) << '\n';
 std::cout << datetime_from_wtick(wtick(0x7FFFFFFFFFFFFFFF)) << '\n';
}

1601-01-01 T 00:00:00.000000000
1601-01-02 T 00:00:00.000000000
2014-01-01 T 03:39:36.000000500
30828-09-14 T 02:48:05.477580700

licensed under cc by-sa 3.0 with attribution.