Преобразование времени Unix/Linux в Windows FILETIME

Я снова перехожу с Windows на Linux, мне нужно портировать функцию с Windows на Linux, которая вычисляет время NTP. Кажется простым, но формат в формате Windows FILETIME. У меня вроде есть представление, в чем разница, но пока я не могу правильно преобразовать свое время Linux в формат Windows FILETIME. У кого-нибудь есть идеи о том, как это сделать?

Я видел несколько статей о том, как это сделать, но все они используют функции Win32, и я не могу их использовать! Я могу опубликовать код Windows, если это не имеет смысла.

Они также берут текущее время и вычитают его из 1 января 1900 года, чтобы получить дельту для поиска NTP, я бы предположил, что в Linux я просто добавляю

const unsigned long EPOCH   = 2208988800UL

к моему времени, чтобы получить этот результат?


person Dixon Steel    schedule 27.08.2010    source источник
comment
У меня есть работающее преобразование, но теперь проблема в том, что мне нужно микросекундное разрешение. Если кому-то интересно, я могу опубликовать код, но сейчас я застрял на долях секунд.   -  person Dixon Steel    schedule 31.08.2010


Ответы (3)


Документация Microsoft для FILETIME< Структура /a> объясняет, что это такое. Основная идея заключается в том, что Windows FILETIME ведет отсчет с шагом 10-7 секунд (с интервалом в 100 наносекунд) с 1 января 1601 года (почему 1601 года? понятия не имею...). В Linux вы можете получить время в микросекундах (10-6) с 1 января 1970 года, используя gettimeofday(). Таким образом, следующая функция C выполняет эту работу:

#include <sys/time.h>
/**
 * number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
 */
#define EPOCH_DIFF 11644473600LL

unsigned long long
getfiletime() {
    struct timeval tv;
    unsigned long long result = EPOCH_DIFF;
    gettimeofday(&tv, NULL);
    result += tv.tv_sec;
    result *= 10000000LL;
    result += tv.tv_usec * 10;
    return result;
}
person Leo 'TheHobbit'    schedule 09.11.2010
comment
1601, потому что 1600 год будет високосным, и стоящее за этим бизнес-решение, должны ли мы добавить код для правильной обработки дат в январе/феврале 1600 года или просто начать с 1601 года? довольно очевидно. - person Simon Richter; 03.04.2012
comment
объяснение от MS Raymond Chen: Почему эпоха Win32 приходится на 1 января 1601 года?: Григорианский календарь работает по 400-летнему циклу, и 1601 год — это первый год цикла, который был активен во время разработки Windows NT. Другими словами, он был выбран, чтобы математика выглядела красиво. - person phuclv; 28.11.2020

Во-первых, почему 1601? Потому что григорианский календарь повторяется каждые 400 лет, а пролептический григорианский календарь начинается с 01-01-01. Таким образом, 1601 год был последним началом цикла перед 1980 (1970,...), и это упрощает расчеты. (О, и вот почему 3-е тысячелетие началось 01-01-2001, а не 01-01-2000...)

Чтобы создать что-то вроде метки времени NTP с двоичной дробью из FILETIME,

  1. Сдвиньте начало эпохи, используя LONGLONG или ULONGLONG для представления тактов FILETIME.
  2. Выполните деление тиков на 10 ^ 7, чтобы получить секунды (как частное) и дробь (как остаток). Если вы считаете чисто без знака, просто разделите. Но вы не можете представлять отрицательные значения в этом случае.
  3. Unsigned умножает 64-битную дробь (которая должна быть >= 0) на 1844674407371ull, что равно 2^64/10^7 (округлено)
  4. Возьмите старшие 32 бита произведения как двоичную часть временной метки NTP. (Возможно, вы захотите округлить от младших 32 битов произведения. Обратите внимание, что перенос округления на полные секунды невозможен, если дробные деления не превышают 10^7.)
person Jürgen Perlinger    schedule 18.09.2013
comment
Это обратная сторона вопроса, но очень полезная, спасибо. - person Rob; 11.11.2014

Предполагая, что вы получаете значение временной метки, удаляя какой-либо веб-сайт. Вы определяете

unsigned __int64 timestamp = 0;

Возможно, полученное значение нужно разделить на 1000.

if (element.HasMember(L"timestamp"))
{
    timestamp = element[L"timestamp"].GetUint64() / 1000;
}                                       }

то вы делаете следующее:

LONGLONG ll;
ll = Int32x32To64(timestamp, 10000000) + 116444736000000000;
FILETIME ft;
ft.dwLowDateTime = (DWORD)ll;
ft.dwHighDateTime = ll >> 32;
SYSTEMTIME stTime;
FileTimeToSystemTime(&ft, &stTime);
person Michael Haephrati    schedule 03.01.2017
comment
Спасибо @NathanMoinvaziri - person Michael Haephrati; 26.10.2017
comment
Я пытаюсь использовать это, чтобы получить вывод в Objective-C, аналогичный выводу, который я получаю от DateTime.Now.ToFileTimeUtc() в Unity. Кажется, это должно быть именно то, что мне нужно, но когда я запускаю их одновременно, эта функция возвращает число с разницей примерно в 252 276 759 470 от результата Unity. Любая идея, почему это было бы? - person Nerrolken; 19.04.2018
comment
Я тестировал этот код на Windows/Visual Studio C++, и до сих пор он никогда не подводил. Если вы обнаружите конкретные случаи, когда это не удается, дайте мне знать. - person Michael Haephrati; 19.04.2018