如何以毫秒级分辨率获得Windows系统时间?

时间:2010-09-16 17:32:05

标签: windows time system resolution

如何以毫秒分辨率获得Windows系统时间?

如果无法解决上述问题,那么如何才能获得操作系统的启动时间?我想将此值与timeGetTime()一起使用,以便以毫秒分辨率计算系统时间。

提前谢谢。

10 个答案:

答案 0 :(得分:21)

尝试MSDN杂志上的这篇文章。它实际上非常复杂。

Implement a Continuously Updating, High-Resolution Time Provider for Windows

答案 1 :(得分:11)

这是对上述评论的详细说明,以解释其中的一些原因。

首先,GetSystemTime *调用是唯一提供系统时间的Win32 API。这个时间具有相当粗略的粒度,因为大多数应用程序不需要维持更高分辨率所需的开销。时间(可能)在内部存储为64位毫秒计数。调用timeGetTime获得低位32位。调用GetSystemTime等请求Windows返回此毫秒时间,转换为天后等,并包括系统启动时间。

机器中有两个时间源:CPU的时钟和板载时钟(例如,实时时钟(RTC),可编程间隔定时器(PIT)和高精度事件定时器(HPET))。第一个的分辨率约为0.5ns(2GHz),第二个通常可编程为1ms(尽管较新的芯片(HPET)具有更高的分辨率)。 Windows使用这些定期滴答来执行某些操作,包括更新系统时间。

应用程序可以通过timerBeginPeriod更改此期间;但是,这会影响整个系统。操作系统将以请求的频率检查/更新常规事件。在低CPU负载/频率下,存在节省功率的空闲时段。在高频率下,没有时间将处理器置于低功耗状态。有关详细信息,请参阅Timer Resolution。最后,每个tick都有一些开销,增加频率会消耗更多的CPU周期。

对于更高分辨率的时间,系统时间不能保持这种精确度,不会超过大本钟的秒针。使用QueryPerformanceCounter(QPC)或CPU的ticks(rdtsc)可以提供系统时间标记之间的分辨率。这种方法被用于MSDN杂志文章凯文所引用的文章中。虽然这些方法可能有漂移(例如,由于频率缩放等),因此需要同步到系统时间。

答案 2 :(得分:6)

GetTickCount无法为您完成任务。

查看QueryPerformanceFrequency / QueryPerformanceCounter。这里唯一的问题是CPU扩展,你的研究也是如此。

答案 3 :(得分:4)

从Windows 8开始Microsoft已经引入了新的API命令GetSystemTimePreciseAsFileTime:

https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895%28v=vs.85%29.aspx

不幸的是,如果您创建的软件也必须在较旧的操作系统上运行,则无法使用它。

我目前的解决方案如下,但要注意:确定的时间不准确,只是接近实时。结果应始终小于或等于实时,但具有固定的错误(除非计算机进入待机状态)。结果具有毫秒分辨率。为了我的目的,它足够精确。

void GetHighResolutionSystemTime(SYSTEMTIME* pst)
{
    static LARGE_INTEGER    uFrequency = { 0 };
    static LARGE_INTEGER    uInitialCount;
    static LARGE_INTEGER    uInitialTime;
    static bool             bNoHighResolution = false;

    if(!bNoHighResolution && uFrequency.QuadPart == 0)
    {
        // Initialize performance counter to system time mapping
        bNoHighResolution = !QueryPerformanceFrequency(&uFrequency);
        if(!bNoHighResolution)
        {
            FILETIME ftOld, ftInitial;

            GetSystemTimeAsFileTime(&ftOld);
            do
            {
                GetSystemTimeAsFileTime(&ftInitial);
                QueryPerformanceCounter(&uInitialCount);
            } while(ftOld.dwHighDateTime == ftInitial.dwHighDateTime && ftOld.dwLowDateTime == ftInitial.dwLowDateTime);
            uInitialTime.LowPart  = ftInitial.dwLowDateTime;
            uInitialTime.HighPart = ftInitial.dwHighDateTime;
        }
    }

    if(bNoHighResolution)
    {
        GetSystemTime(pst);
    }
    else
    {
        LARGE_INTEGER   uNow, uSystemTime;

        {
            FILETIME    ftTemp;
            GetSystemTimeAsFileTime(&ftTemp);
            uSystemTime.LowPart  = ftTemp.dwLowDateTime;
            uSystemTime.HighPart = ftTemp.dwHighDateTime;
        }
        QueryPerformanceCounter(&uNow);

        LARGE_INTEGER   uCurrentTime;
        uCurrentTime.QuadPart = uInitialTime.QuadPart + (uNow.QuadPart - uInitialCount.QuadPart) * 10000000 / uFrequency.QuadPart;

        if(uCurrentTime.QuadPart < uSystemTime.QuadPart || abs(uSystemTime.QuadPart - uCurrentTime.QuadPart) > 1000000)
        {
            // The performance counter has been frozen (e. g. after standby on laptops)
            // -> Use current system time and determine the high performance time the next time we need it
            uFrequency.QuadPart = 0;
            uCurrentTime = uSystemTime;
        }

        FILETIME ftCurrent;
        ftCurrent.dwLowDateTime  = uCurrentTime.LowPart;
        ftCurrent.dwHighDateTime = uCurrentTime.HighPart;
        FileTimeToSystemTime(&ftCurrent, pst);
    }
}

答案 4 :(得分:4)

在Windows中,有史以来的基础是一个名为GetSystemTimeAsFiletime的函数。

  • 它返回一个能够持有100ns资源的时间的结构。
  • 以UTC格式保存

FILETIME结构记录了自1600年1月1日以来100ns间隔的数量;意思是它的分辨率限制在100ns。

这构成了我们的第一个功能:

enter image description here

自1600年1月1日以来,64位数的100ns刻度有点笨拙。 Windows提供了一个方便的帮助函数FileTimeToSystemTime,可以将这个64位整数解码为有用的部分:

record SYSTEMTIME {
   wYear: Word;
   wMonth: Word;
   wDayOfWeek: Word;
   wDay: Word;
   wHour: Word;
   wMinute: Word;
   wSecond: Word;
   wMilliseconds: Word;
}

请注意,SYSTEMTIME的内置分辨率限制为1ms

现在我们可以从FILETIME转到SYSTEMTIME

enter image description here

我们可以编写函数来将当前系统时间作为SYSTEIMTIME结构:

SYSTEMTIME GetSystemTime()
{
    //Get the current system time utc in it's native 100ns FILETIME structure
    FILETIME ftNow;
    GetSytemTimeAsFileTime(ref ft);

    //Decode the 100ns intervals into a 1ms resolution SYSTEMTIME for us
    SYSTEMTIME stNow;
    FileTimeToSystemTime(ref stNow);

    return stNow;
}

除了Windows已经为您编写了这样的功能:GetSystemTime

enter image description here

本地,而不是UTC

现在,如果您不想要UTC中的当前时间,该怎么办?如果你想在当地时间怎么办? Windows提供了将UTC中的FILETIME转换为您当地时间的功能:FileTimeToLocalFileTime

enter image description here

您可以编写一个函数,在本地时间内返回FILETIME

FILETIME GetLocalTimeAsFileTime()
{
   FILETIME ftNow;
   GetSystemTimeAsFileTime(ref ftNow);

   //convert to local
   FILETIME ftNowLocal
   FileTimeToLocalFileTime(ftNow, ref ftNowLocal);

   return ftNowLocal;
}

enter image description here

并且假设您想要将 local FILETIME解码为SYSTEMTIME。这没问题,您可以再次使用FileTimeToSystemTime

enter image description here

幸运的是,Windows已经为您提供了一个函数,它返回值:

enter image description here

精确

还有另一个考虑因素。在Windows 8之前,时钟的分辨率约为15ms。在Windows 8中,他们将时钟提高到100ns(匹配FILETIME的分辨率。)

  • GetSystemTimeAsFileTime (传统,15毫秒分辨率)
  • GetSystemTimeAsPreciseFileTime (Windows 8,100ns分辨率)

这意味着我们应该总是更喜欢新值:

enter image description here

你问了时间

你问时间;但你有一些选择。

时区:

  • UTC (系统原生)
  • 当地时区

格式:

  • FILETIME (系统原生,100ns分辨率)
  • SYTEMTIME (已解码,1ms分辨率)

摘要

  • 100ns分辨率:FILETIME
    • UTC:GetSytemTimeAsPreciseFileTime (或GetSystemTimeAsFileTime
    • 本地:(自己动手)
  • 1ms分辨率:SYSTEMTIME
    • UTC:GetSystemTime
    • 本地:GetLocalTime

答案 5 :(得分:1)

GetSystemTimeAsFileTime在绝对时间内提供任何Win32函数的最佳精度。 QPF / QPC作为乔尔克拉克建议将提供更好的相对时间。

答案 6 :(得分:0)

QueryPerformanceCounter()是为细粒度计时器分辨率而构建的。

这是系统必须提供的最高分辨率计时器,您可以在应用程序代码中使用它来识别性能瓶颈

以下是C#devs的简单实现:

    [DllImport("kernel32.dll")]
    extern static short QueryPerformanceCounter(ref long x);
    [DllImport("kernel32.dll")]
    extern static short QueryPerformanceFrequency(ref long x);
    private long m_endTime;
    private long m_startTime;
    private long m_frequency;

    public Form1()
    {
        InitializeComponent();
    }
    public void Begin()
    {
        QueryPerformanceCounter(ref m_startTime);
    }
    public void End()
    {
        QueryPerformanceCounter(ref m_endTime);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        QueryPerformanceFrequency(ref m_frequency);
        Begin();
        for (long i = 0; i < 1000; i++) ;
        End();
        MessageBox.Show((m_endTime - m_startTime).ToString());
    }

如果您是C / C ++开发人员,那么请看一下: http://support.microsoft.com/kb/815668

答案 7 :(得分:0)

嗯,这个很老了,但是在Windows C库_ftime中还有另一个有用的功能,它返回一个本地时间为time_t,毫秒,时区和夏令时标志的结构

答案 8 :(得分:0)

由于我们大家都是来这里快速摘录而不是无聊的解释的,所以我写一个:

FILETIME t;
GetSystemTimeAsFileTime(&t); // unusable as is

ULARGE_INTEGER i;
i.LowPart = t.dwLowDateTime;
i.HighPart = t.dwHighDateTime;

int64_t ticks_since_1601 = i.QuadPart; // now usable
int64_t us_since_1601   = (i.QuadPart * 1e-1);
int64_t ms_since_1601   = (i.QuadPart * 1e-4);
int64_t sec_since_1601  = (i.QuadPart * 1e-7);

// unix epoch
int64_t unix_us  = (i.QuadPart * 1e-1) - 11644473600LL * 1000000;
int64_t unix_ms  = (i.QuadPart * 1e-4) - 11644473600LL * 1000;
double  unix_sec = (i.QuadPart * 1e-7) - 11644473600LL;

// i.QuadPart is # of 100ns ticks since 1601-01-01T00:00:00Z
// difference to Unix Epoch is 11644473600 seconds (attention to units!)

不知道基于性能计数器的答案是如何上升的,伙计们不要滑脱错误。

答案 9 :(得分:-2)

在我对"Microsecond resolution timestamps on Windows"问题的回答中,我已经以适合大多数目的的方式快速,轻松地写了一些关于如何快速轻松地实现这一点的信息。