从Windows系统时钟获取当前时间(毫秒)?

38

如何在C++中获取系统时钟的当前时间(以毫秒为单位)? 这是一个Windows特定的应用程序。

7个回答

62

最简单(也是最直接)的方法是调用GetSystemTimeAsFileTime()函数,它返回一个FILETIME结构,该结构存储了自1601年1月1日午夜以来的100纳秒间隔的64位值。

至少在Windows NT 3.1、3.51和4.01时期,GetSystemTimeAsFileTime() API是检索当前时间的最快用户模式API。与GetSystemTime() -> SystemTimeToFileTime()相比,它还具有以下优点:是单个API调用,在正常情况下不会失败。

要将FILETIME ft_now;转换为名为ll_now的64位整数,请使用以下代码:
ll_now = (LONGLONG)ft_now.dwLowDateTime + ((LONGLONG)(ft_now.dwHighDateTime) << 32LL);

然后,您可以将其除以一毫秒内的100纳秒间隔数(即10,000),并得到自Win32时代以来的毫秒数。

要转换为Unix时代,请减去116444736000000000LL,即可达到1970年1月1日。

您提到想要找到当天已过去的毫秒数。由于Win32时代始于午夜,因此可以使用模数运算从文件时间计算出到目前为止今天经过的毫秒数。具体来说,由于每天有24小时/天 * 60分钟/小时 * 60秒/分钟 * 1000毫秒/秒 = 86,400,000毫秒/天,您可以使用系统时间的毫秒数模 86400000LL

对于不同的应用程序,可能不希望使用模数。特别是如果要计算流逝的时间,可能会因午夜时分的回绕而遇到困难。这些困难是可以解决的,我所知道的最好的例子是Linus Torvald在Linux内核中处理计数器回绕的那行代码。

请注意,系统时间是以协调世界时(UTC)返回的(无论是在 GetSystemTimeAsFileTime() 还是简单的 GetSystemTime() 的情况下)。如果您需要管理员配置的本地时间,则可以使用 GetLocalTime()


3
给你点赞,因为你提供了深入的解释。能否补充一下关于“至少在Windows NT 3.1、3.51和4.01时期最快的用户API”的NT5.1+部分?这是否意味着现在不再是这种情况? - rama-jka toti
2
你也可以利用FILETIME的布局方式,像使用long long那样进行转换。看一下如何使用union实现LARGE_INTEGER,这是相同的思路。 *reinterpret_cast<long long*>(&ft_now) - Matt Price
3
互联网上最佳答案 - notbad.jpeg
2
将文件时间(以100纳秒为单位)转换为Unix纪元,需减去116444736000000000LL,即可到达1970年1月1日。 - Kaifei
3
@MattPrice说:“你不能简单地进行强制类型转换,参见https://msdn.microsoft.com/en-gb/library/windows/desktop/ms724284(v=vs.85).aspx:‘不要将指向FILETIME结构的指针强制转换为ULARGE_INTEGER*或__int64*值,因为这可能会在64位Windows上导致对齐错误’。” - Saul
显示剩余3条评论

47

要将时间表示为协调世界时(UTC),请使用Win32 API中的GetSystemTime

SYSTEMTIME st;
GetSystemTime(&st);

SYSTEMTIME文档中记录了以下相关成员:

WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;

正如shf301在下面有益的指出的那样,GetLocalTime(使用相同的原型)将提供校正到用户当前时区的时间。

这里有几个很好的答案,具体取决于你想要什么。如果你只需要时间,我的回答是最佳方法-如果你需要进行算术运算的可靠日期,请考虑Alex的回答。在Windows上有很多处理时间的方式,其中一些比其他方法更准确(还没有人提到QueryPerformanceCounter)。


6
使用GetLocalTime函数获取用户可见的时间和日期。http://msdn.microsoft.com/en-us/library/ms724338%28VS.85%29.aspx - shf301
2
QueryPerformanceCounter... 我认为你在开玩笑,因为它有很多缺点,这使得它在这里选择不佳。 - Heath Hunnicutt
1
再一次,我们需要搜索正确的对应包含文件... 唉。 - Phil

9

Jed 上面的回答提供了一个简明扼要的例子:

const std::string currentDateTime() {

SYSTEMTIME st, lt;

GetSystemTime(&st);

char currentTime[84] = "";

sprintf(currentTime,"%d/%d/%d  %d:%d:%d %d",st.wDay,st.wMonth,st.wYear, st.wHour, st.wMinute, st.wSecond , st.wMilliseconds);

return string(currentTime); }

1
sprintf(currentTime,"%.4d%.2d%.2d%.2d%.2d%.2d%.4d",st.wYear,st.wMonth,st.wDay, st.wHour, st.wMinute, st.wSecond , st.wMilliseconds); 用于生成固定长度的字符串。 - Antonio
1
这会获取UTC时间;GetLocalTime()的工作方式类似,但会给出本地时区时间。 - M.M

3

首先使用GetSystemTime函数,然后如果需要,可以在前者填充的SYSTEMTIME结构上调用SystemTimeToFileTime函数。一个FILETIME是一个64位计数器,表示自纪元以来的100纳秒间隔,因此更适合进行算术运算;一个SYSTEMTIME是一个带有所有预期字段(年、月、日、小时等,直到毫秒)的结构体。例如,如果您想知道“从午夜以来经过了多少毫秒”,那么减去两个FILETIME结构(一个是当前时间,另一个是将相同的SYSTEMTIME转换后置零适当字段所得到的)并除以适当的10的幂可能是最简单的方法。


Jed,没错,我凭记忆写的时候认为有一个下划线——现在正在编辑以修复,谢谢。 - Alex Martelli

2
根据您的应用程序需求,有六种常见选项。这篇Dr Dobbs Journal文章将为您提供选择最佳选项所需的所有信息(以及更多信息)。
在您的具体情况下,从这篇文章中:
GetSystemTime()检索当前系统时间并实例化SYSTEMTIME结构,该结构由许多单独的字段组成,包括年份、月份、日期、小时、分钟、秒和毫秒。

1
请纠正我,但是GetTickCount不是给出自应用程序启动以来经过的毫秒数吗? - Mark
1
是的,没错,我加了错误的引号。抱歉。我已经修复了它。 - Ash
1
@Mark:自系统启动以来,GetTickCount64 自 Windows Vista 开始扩展到 64 位;前者仅限于 32 位。 - Jed Smith
3
我相信实际上它是从系统启动开始的,但你说错了函数。我认同你的观点。 - Ash
根据您想要做什么,GetTickCount可能是您想要的,因为它只返回自启动以来经过的毫秒数的简单DWORD。 - Al Ro

2

虽然这不是问题所问的,但考虑一下为什么你需要这些信息是值得的。

如果你只是想跟踪计算所需的时间或自上次用户交互以来经过的时间,请考虑使用系统启动后的运行时间(毫秒):GetTickCount()或GetTickCount64()。这要简单得多。但我之前陷入了时代漩涡,因为在Unix下你需要这样做。


1

这里有一些在Windows中可用的代码,我在Open Watcom C项目中使用过。它应该在C++中也可以使用。它使用_dos_gettime或gettime返回秒数(而不是毫秒)。

  double seconds(void)
  {
  #ifdef __WATCOMC__
  struct dostime_t t;
  _dos_gettime(&t);
  return ((double)t.hour * 3600 + (double)t.minute * 60 + (double)t.second + (double)t.hsecond * 0.01);
  #else
  struct time t;
  gettime(&t);
  return ((double)t.ti_hour * 3600 + (double)t.ti_min * 60 + (double)t.ti_sec + (double)t.ti_hund * 0.01);
  #endif
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接