为何C++获取的Epoch秒数在C# DateTime中转换出现偏差?
问题:C++获取的文件修改时间Epoch秒数在C#转换后时区不符
我有一个C++方法用于获取文件最后修改时间,返回从Epoch(1970年1月1日)开始的秒数,代码如下:
const HANDLE handle = CreateFile(filePath.c_str(), // file to open GENERIC_READ, // open for reading FILE_SHARE_READ, // share for reading nullptr, // default security OPEN_EXISTING, // Open existing file only if it exists, and only intact. FILE_FLAG_BACKUP_SEMANTICS, nullptr); // no attr. if (INVALID_HANDLE_VALUE == handle) { ThrowWindowsErrorMessage(__CODE_LOCATION__); } if (BY_HANDLE_FILE_INFORMATION fileInformation; 0 == GetFileInformationByHandle(handle, &fileInformation)) { ThrowWindowsErrorMessage(__CODE_LOCATION__); } else { SYSTEMTIME systemTime; if (0 == FileTimeToSystemTime(&fileInformation.ftLastWriteTime, &systemTime)) { ThrowWindowsErrorMessage(__CODE_LOCATION__); } else { tm tim = {0}; tim.tm_sec = systemTime.wSecond; tim.tm_min = systemTime.wMinute; tim.tm_hour = systemTime.wHour; tim.tm_mday = systemTime.wDay; tim.tm_mon = systemTime.wMonth - 1; tim.tm_year = systemTime.wYear - 1900; // daylight time applies or not. tim.tm_isdst = -1; const time_t t0 = mktime(&tim); if (static_cast<time_t>(-1) != t0) { tTime = t0; } } } CloseHandle(handle); return tTime;
运行时,systemTime符合本地UTC+2时区(本地9:35对应UTC 7:35),返回的tTime值为1667453701。
但在C#中用以下代码构造DateTime对象时:
var lastModifiedFileTime = new DateTime(1970, 1,1).AddSeconds(1667453701);
得到的时间显示为5:35,比预期少2小时。期望两者归一化到UTC后数值一致,请问问题原因及修复方法?
问题原因
C++端的时区转换逻辑:
fileInformation.ftLastWriteTime是Windows系统存储的UTC时间,通过FileTimeToSystemTime转换后得到的systemTime是**本地时区(UTC+2)**的时间。mktime函数的作用是将本地时间转换为从1970-01-01 UTC开始的Epoch秒数,所以最终返回的1667453701对应的是UTC 7:35,这部分是正确的。
C#端的基准时间时区错误:
new DateTime(1970, 1, 1)默认创建的是本地时区的1970年1月1日,调用AddSeconds(1667453701)后得到的仍然是本地时区的时间。- 由于你的本地时区是UTC+2,C#会把Epoch秒数对应的UTC时间再转换一次到本地时区,导致出现双重转换错误,最终显示的时间比预期少2小时。
修复方案
方案1:C#中直接构造UTC时间(推荐)
使用DateTime.UnixEpoch(.NET Core 2.1+/NET 5+)或显式指定DateTimeKind.Utc来创建基准时间,确保转换后的时间是UTC时区:
// 方法1:使用UnixEpoch(简洁高效) var lastModifiedFileTime = DateTime.UnixEpoch.AddSeconds(1667453701); // 方法2:显式指定UTC类型(兼容旧版.NET Framework) var lastModifiedFileTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(1667453701);
此时得到的lastModifiedFileTime会显示为UTC 7:35,与C++端的预期一致。
方案2:C#中转换为本地时间
如果需要得到对应本地时区的时间(即C++中的9:35),可以将UTC时间转换为本地时区:
var utcTime = DateTime.UnixEpoch.AddSeconds(1667453701); var localTime = utcTime.ToLocalTime();
优化C++代码(避免时区转换误差)
可以直接将FILETIME转换为Epoch秒数,跳过本地时间转换步骤,避免时区相关的潜在问题:
ULARGE_INTEGER ft; ft.LowPart = fileInformation.ftLastWriteTime.dwLowDateTime; ft.HighPart = fileInformation.ftLastWriteTime.dwHighDateTime; // FILETIME是从1601-01-01 UTC开始的100纳秒单位,转换为1970-01-01 UTC的秒数 const time_t epochSeconds = (ft.QuadPart - 116444736000000000LL) / 10000000LL; tTime = epochSeconds;
内容的提问来源于stack exchange,提问作者NirMH
相关产品推荐
相关产品推荐

