【问题标题】:Convert Unix/Linux time to Windows FILETIME将 Unix/Linux 时间转换为 Windows FILETIME
【发布时间】:2011-04-04 21:01:21
【问题描述】:

我再次从 Windows 转到 Linux,我必须将计算 NTP 时间的函数从 Windows 移植到 Linux。看起来很简单,但格式是 Windows FILETIME 格式。我有点知道有什么区别,但到目前为止,我无法正确地将我的 Linux 时间转换为 Windows FILETIME 格式。有人对如何做到这一点有任何想法吗?

我看过一些关于如何做到这一点的文章,但它们都使用 Win32 函数,我无法使用它们!如果这没有意义,我可以发布 Windows 代码。

他们还采用当前时间并从 1900 年 1 月 1 日减去它以获得增量来找到 NTP,我假设在 Linux 中我只是添加了

const unsigned long EPOCH   = 2208988800UL

等我得到这个结果的时间?

【问题讨论】:

  • 我确实有一个工作转换,但现在问题是我需要微秒分辨率。如果有人有兴趣,我可以发布代码,但现在我被困在几秒钟内。

标签: c porting datetime-conversion time-t filetime


【解决方案1】:

FILETIME 结构的 Microsoft 文档解释了它是什么。基本思想是,Windows FILETIME 从 1601 年 1 月 1 日起以 10-7 秒(100 纳秒间隔)为步长计数(为什么是 1601?不知道......)。在 Linux 中,您可以使用 gettimeofday() 获取从 1970 年 1 月 1 日开始的微秒 (10-6) 时间。因此,下面的 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 因为 1600 将是闰年,并且背后的业务决策是“我们应该添加代码以正确处理 1600 年 1 月/2 月的日期,还是直接从 1601 年开始?”很明显。
  • 来自 MS Raymond Chen 的解释:Why is the Win32 epoch January 1, 1601?公历以 400 年为周期运行,而 1601 年是在 Windows NT 出现时有效的周期的第一年正在设计。换句话说,选择它是为了使数学结果很好。
【解决方案2】:

首先,为什么是 1601?因为公历每 400 年重复一次,而预测的公历从 0001-01-01 开始。所以 1601 是 1980(1970,...)之前的最后一个周期开始,这简化了计算。 (哦,这就是为什么第三个千年始于 2001 年 1 月 1 日,不是 2000 年 1 月 1 日...)

要使用 FILETIME 的二进制分数创建类似 NTP 时间戳的东西,

  1. 使用 LONGLONG 或 ULONGLONG 将历元的原点移动到 表示 FILETIME 的刻度。
  2. 将刻度除以 10^7 以获得秒数(如 商)和分数(作为余数)。如果你计算 纯粹是无符号的,简单地划分。但你不能代表消极 这种情况下的值。
  3. 无符号乘以 64 位中的小数(必须 >= 0) 1844674407371ull,即 2^64 / 10^7(四舍五入)
  4. 取积的高32位作为二进制小数 NTP 时间戳。 (您可能想从低 32 位四舍五入 产品。请注意,四舍五入到整秒不能 只要分数刻度严格低于 10^7,就会发生。)

【讨论】:

  • 这是问题的反面,但非常有用,谢谢。
【解决方案3】:

假设您从废弃某个网站中获取时间戳值。你定义

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);

【讨论】:

  • 谢谢@NathanMoinvaziri
  • 我正在尝试使用它在 Objective-C 中获得与我在 Unity 中从 DateTime.Now.ToFileTimeUtc() 获得的输出类似的输出。看起来这应该正是我所需要的,但是当我同时运行它们时,这个函数返回一个与 Unity 结果相差约 252,276,759,470 的数字。知道为什么会这样吗?
  • 我在 Windows / Visual Studio c++ 上测试了该代码,到目前为止它从未失败过。如果您发现失败的特定情况,请告诉我。
猜你喜欢
  • 2011-09-03
  • 2017-01-21
  • 1970-01-01
  • 2014-10-03
  • 2014-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多