Конвертировать время Unix/Linux в Windows FILETIME

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

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

Они также берут текущее время и вычитают его с 1 января 1900 года, чтобы получить дельту для поиска NTP, я бы предположил, что в Linux я просто добавляю const unsigned long EPOCH = 2208988800UL к моему времени, чтобы получить этот результат?

Благодарю.

3 ответа

Решение

Документация Microsoft для FILETIME структура объясняет, что это такое. Основная идея заключается в том, что 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;
}

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

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

  1. Сдвиньте начало эпохи, используя LONGLONG или ULONGLONG, чтобы представить галочки FILETIME.
  2. Сделайте деление тиков по полу на 10^7, чтобы получить секунды (как частное) и дробь (как остаток). Если вы рассчитываете чисто без знака, просто делите. Но вы не можете представлять отрицательные значения в этом случае.
  3. Без знака умножьте дробь (которая должна быть>= 0) на 64 бита на 1844674407371, что составляет 2^64 / 10^7 (округлено)
  4. Возьмите старшие 32 бита продукта как двоичную дробь отметки времени NTP. (Вы можете захотеть округлить с младших 32 битов продукта. Обратите внимание, что округление до полных секунд не может иметь место, если дробные тики строги ниже 10^7.)

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

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);
Другие вопросы по тегам