Преобразовать Windows Filetime на второе место в Unix/Linux
У меня есть файл трассировки, в котором каждое время транзакции представлено в формате файла Windows. Эти временные числа выглядят примерно так:
- 128166372003061629
- 128166372016382155
- 128166372026382245
Не могли бы вы сообщить мне, есть ли в Unix/Linux библиотека C/С++ для извлечения фактического времени (особенно второго) из этих чисел? Могу ли я написать свою собственную функцию извлечения?
Ответы
Ответ 1
Это довольно просто: начинается эпоха windows 1601-01-01T00: 00: 00Z. Это 11644473600 секунд до эпохи UNIX/Linux (1970-01-01T00: 00: 00Z). Тики Windows составляют 100 наносекунд. Таким образом, функция, чтобы получить секунды от эпохи UNIX, будет следующей:
#define WINDOWS_TICK 10000000
#define SEC_TO_UNIX_EPOCH 11644473600LL
unsigned WindowsTickToUnixSeconds(long long windowsTicks)
{
return (unsigned)(windowsTicks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
}
Ответ 2
Тип FILETIME - это число с шагом 100 нс с 1 января 1601 года.
Чтобы преобразовать это в unix time_t, вы можете использовать следующее.
#define TICKS_PER_SECOND 10000000
#define EPOCH_DIFFERENCE 11644473600LL
time_t convertWindowsTimeToUnixTime(long long int input){
long long int temp;
temp = input / TICKS_PER_SECOND; //convert from 100ns intervals to seconds;
temp = temp - EPOCH_DIFFERENCE; //subtract number of seconds between epochs
return (time_t) temp;
}
вы можете использовать функции ctime для управления им.
Ответ 3
(я обнаружил, что не могу ввести читаемый код в комментарий, поэтому...)
Обратите внимание, что Windows может представлять время вне диапазона времени эпохи POSIX, и, таким образом, программа преобразования должна возвращать индикацию "вне диапазона" по мере необходимости. Самый простой способ:
... (as above)
long long secs;
time_t t;
secs = (windowsTicks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
t = (time_t) secs;
if (secs != (long long) t) // checks for truncation/overflow/underflow
return (time_t) -1; // value not representable as a POSIX time
return t;
Ответ 4
Новый ответ для старого вопроса.
Использование С++ 11 <chrono>
плюс эта бесплатная библиотека с открытым исходным кодом:
https://github.com/HowardHinnant/date
Можно очень легко преобразовать эти временные метки в std::chrono::system_clock::time_point
, а также преобразовать эти временные метки в читаемый человеком формат в григорианском календаре:
#include "date.h"
#include <iostream>
std::chrono::system_clock::time_point
from_windows_filetime(long long t)
{
using namespace std::chrono;
using namespace date;
using wfs = duration<long long, std::ratio<1, 10'000'000>>;
return system_clock::time_point{floor<system_clock::duration>(wfs{t} -
(sys_days{1970_y/jan/1} - sys_days{1601_y/jan/1}))};
}
int
main()
{
using namespace date;
std::cout << from_windows_filetime(128166372003061629) << '\n';
std::cout << from_windows_filetime(128166372016382155) << '\n';
std::cout << from_windows_filetime(128166372026382245) << '\n';
}
Для меня эти выходы:
2007-02-22 17:00:00.306162
2007-02-22 17:00:01.638215
2007-02-22 17:00:02.638224
В Windows вы можете пропустить floor
и получить последнюю десятичную цифру точности:
return system_clock::time_point{wfs{t} -
(sys_days{1970_y/jan/1} - sys_days{1601_y/jan/1})};
2007-02-22 17:00:00.3061629
2007-02-22 17:00:01.6382155
2007-02-22 17:00:02.6382245
При использовании оптимизаций подвыражение (sys_days{1970_y/jan/1} - sys_days{1601_y/jan/1})
будет транслировать во время компиляции до days{134774}
, которое будет далее компилировать-время-преобразовать в те единицы, которые требуются для полного выражения (секунды, 100-наносекунды, что угодно). Итог: это очень читаемо и очень эффективно.
Ответ 5
Предполагая, что вы спрашиваете о FILETIME, затем FileTimeToSystemTime делает то, что вы хотите, вы можете получить секунды из созданной структуры SYSTEMTIME.
Ответ 6
Решение, которое делит и добавляет, не будет корректно работать с летней экономией.
Вот фрагмент, который работает, но он предназначен для окон.
time_t FileTime_to_POSIX(FILETIME ft)
{
FILETIME localFileTime;
FileTimeToLocalFileTime(&ft,&localFileTime);
SYSTEMTIME sysTime;
FileTimeToSystemTime(&localFileTime,&sysTime);
struct tm tmtime = {0};
tmtime.tm_year = sysTime.wYear - 1900;
tmtime.tm_mon = sysTime.wMonth - 1;
tmtime.tm_mday = sysTime.wDay;
tmtime.tm_hour = sysTime.wHour;
tmtime.tm_min = sysTime.wMinute;
tmtime.tm_sec = sysTime.wSecond;
tmtime.tm_wday = 0;
tmtime.tm_yday = 0;
tmtime.tm_isdst = -1;
time_t ret = mktime(&tmtime);
return ret;
}
Ответ 7
Здесь по существу одно и то же решение, кроме этого, правильно кодирует отрицательные числа из Ldap и отбрасывает последние 7 цифр перед преобразованием.
public static int LdapValueAsUnixTimestamp(SearchResult searchResult, string fieldName)
{
var strValue = LdapValue(searchResult, fieldName);
if (strValue == "0") return 0;
if (strValue == "9223372036854775807") return -1;
return (int)(long.Parse(strValue.Substring(0, strValue.Length - 7)) - 11644473600);
}
Ответ 8
Также здесь используется чистый С# ian способ сделать это.
(Int32)(DateTime.FromFileTimeUtc(129477880901875000).Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
Здесь результат обоих методов в моем ближайшем окне:
(Int32)(DateTime.FromFileTimeUtc(long.Parse(strValue)).Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
1303314490
(int)(long.Parse(strValue.Substring(0, strValue.Length - 7)) - 11644473600)
1303314490
DateTime.FromFileTimeUtc(long.Parse(strValue))
{2011-04-20 3:48:10 PM}
Date: {2011-04-20 12:00:00 AM}
Day: 20
DayOfWeek: Wednesday
DayOfYear: 110
Hour: 15
InternalKind: 4611686018427387904
InternalTicks: 634389112901875000
Kind: Utc
Millisecond: 187
Minute: 48
Month: 4
Second: 10
Ticks: 634389112901875000
TimeOfDay: {System.TimeSpan}
Year: 2011
dateData: 5246075131329262904
Ответ 9
Если кому-то нужно преобразовать его в MySQL
SELECT timestamp,
FROM_UNIXTIME(ROUND((((timestamp) / CAST(10000000 AS UNSIGNED INTEGER)))
- CAST(11644473600 AS UNSIGNED INTEGER),0))
AS Converted FROM events LIMIT 100