记录具有毫秒精度的时间戳& Windows C ++中的分辨率

时间:2010-12-31 12:51:18

标签: c++ windows timestamp resolution

我知道,对于计时准确性,timeGetTime,timeBeginPeriod,QueryPerformanceCounter等功能都很棒,同时提供了良好的分辨率和优点。准确性,但仅基于启动时间,没有与时钟时间的直接链接。

但是,我不想这样做时间事件。我希望能够生成一个精确的时间戳(本地时间),以便我可以在日志文件中显示它,例如31-12-2010 12:38:35.345,对于每个条目。 (我需要毫秒精度)

标准的Windows时间函数,如GetLocalTime,虽然它们提供毫秒值,但没有毫秒级的分辨率,具体取决于运行的操作系统。我正在使用XP,所以我不能指望比大约15ms的分辨率好多了。

我需要的是一种获得两全其美的方法,而不会产生大量开销来获得所需的输出。过大的方法/计算意味着记录器在操作过程中会开始耗费太多时间。

最好/最简单的方法是什么?

5 个答案:

答案 0 :(得分:5)

您可以尝试GetSystemAsFileTime,以100纳秒为单位表示时间。您可以使用Windows来确定它填充的实际分辨率。

替代方法只是查询本地时间并使用QueryPerformanceCounter锁定应用程序启动时的偏移量与时间比率,并将其应用于后续计数器读数。

答案 1 :(得分:5)

首先,一些功能:

// ==========================================================================
#define NOMINMAX
#define _AFXDLL
#include "afxwin.h"               // TRACE
#include "windows.h"              // ULARGE_INTEGER
#include "mmSystem.h"             // timeGetTime
#pragma comment(lib, "Winmm.lib") // timeGetTime

// ==========================================================================
// convert FILETIME to ULONGLONG
// (casting won't work on 64-bit platforms, due to alignment of FILETIME members)
inline void ToULL(const FILETIME& ft, ULONGLONG& uft)
{
    ULARGE_INTEGER uli;
    uli.LowPart = ft.dwLowDateTime ;
    uli.HighPart= ft.dwHighDateTime;
    uft= uli.QuadPart;
}

// --------------------------------------------------------------------------
// convert ULONGLONG to FILETIME
// (casting won't work on 64-bit platforms, due to alignment of FILETIME members)
inline void ToFILETIME(const ULONGLONG& uft, FILETIME& ft)
{
    ULARGE_INTEGER uli;
    uli.QuadPart= uft;
    ft.dwLowDateTime = uli.LowPart ;
    ft.dwHighDateTime= uli.HighPart;
}

// --------------------------------------------------------------------------
// ULONGLONG version for GetSystemTimeAsFileTime
inline void GetSystemTimeAsULL(ULONGLONG& uft)
{
    FILETIME ft;
    ::GetSystemTimeAsFileTime(&ft);
    ToULL(ft, uft);
}

// --------------------------------------------------------------------------
// convert ULONGLONG to time-components
bool ULLToSystemTime(const ULONGLONG nTime        ,  // [i]
                     WORD&           nYear        ,  // [o] 1601 - 30827
                     WORD&           nMonth       ,  // [o] 1 -    12
                     WORD&           nDay         ,  // [o] 1 -    31
                     WORD&           nHour        ,  // [o] 0 -    23
                     WORD&           nMinute      ,  // [o] 0 -    59
                     WORD&           nSecond      ,  // [o] 0 -    59
                     WORD&           nMilliseconds ) // [o] 0 -   999
{
    SYSTEMTIME sysTime;
    FILETIME   ft     ;
    ToFILETIME(nTime, ft);

    // the wDayOfWeek member of the SYSTEMTIME structure is ignored
    if (0 == ::FileTimeToSystemTime(&ft, &sysTime))
        return false;

    nYear        = sysTime.wYear        ;
    nMonth       = sysTime.wMonth       ;
    nDay         = sysTime.wDay         ;
    nHour        = sysTime.wHour        ;
    nMinute      = sysTime.wMinute      ;
    nSecond      = sysTime.wSecond      ;
    nMilliseconds= sysTime.wMilliseconds;
    return true;
}

// --------------------------------------------------------------------------
void TraceTime(const ULONGLONG nTime) // [i]
{
    WORD nYear,nMonth,nDay,nHour,nMinute,nSecond,nMilliseconds;
    ULLToSystemTime(nTime, nYear,nMonth,nDay,nHour,nMinute,nSecond,nMilliseconds);
    TRACE("Time: %02u-%02u-%04u %02u:%02u:%02u.%03u\n", nDay,nMonth,nYear,nHour,nMinute,nSecond,nMilliseconds);
}

现在,如何使用:

ULONGLONG u0,u1;
::GetSystemTimeAsULL(u0);

// wait for tick (each 14.4mS)
do
{
    ::GetSystemTimeAsULL(u1);
}
while (u0==u1);

DWORD d1= ::timeGetTime();

// d1 and u1 are now synchronized

// ... do some work

// get current time:
ULONGLONG u2= u1+(::timeGetTime() - d1)*10000; // mSec --> HectoNanoSec

TraceTime(u2);

请注意,您应该在2-3分钟内重新同步d1和u1一次,以保持准确性。 实际上,您可以测量时钟之间的漂移,以找到最佳的重新同步间隔。

答案 2 :(得分:0)

我曾为此编写过代码,但最终放弃了它,并通过操作系统时间分辨率实现了和平。该代码在紧密循环中调用了GetLocalTime和QueryPerformanceCounter 50次。当我检测到GetLocalTime的结果被一个分辨率滴答改变时,我假设相应的QueryPerformanceCounter的结果足够接近该滴答的开头。这样我得到了精确的时间偏移。从那时起,我调用QueryPerformanceCounter并使用时间偏移进行数学计算,以获得精确的本地时间。

最后我推断所有这些都不值得这么麻烦,而且我并不需要那么精确。

答案 3 :(得分:0)

我要做的是在启动时获取系统时间和queryperfcounter。你知道有一个相当准确的起点。

然后在您的日志系统中调用QueryPerformanceCounter(),减去start-QPC值,除以QPF得到秒(存储为double),将此值与启动时的系统时间相结合并打印。

答案 4 :(得分:0)

typedef struct _TIME_FIELDS {

USHORT Year; USHORT Month; USHORT Day; USHORT Hour; USHORT Minute; USHORT Second; USHORT Milliseconds; USHORT Weekday;

} TIME_FIELDS;

typedef signed __int64      INT64;
typedef unsigned __int64    UINT64;


INT64 get_current_time ()
{
    ULONG secs;
    LARGE_INTEGER tm;

    KeQuerySystemTime (&tm);

    RtlTimeToSecondsSince1970(&tm, &secs);

    /* NOTE: tm is in 100s of nano seconds */
    return (secs * 1000 + (tm.QuadPart/10000)%1000);
}

LARGE_INTEGER get_system_time ()
{
    LARGE_INTEGER tm;
    INT64 time = get_current_time ();

    RtlSecondsSince1970ToTime (time, &tm);

    /* NOTE: tm is in 100s of nano seconds */
    tm.QuadPart += (QTIME_MSECS(time)%1000)*10000;
    return tm;

}

int log_current_time ()
{
    static char* month_names[] =
    {
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec"
    };

    LARGE_INTEGER tm, lcl_time;
    TIME_FIELDS fields;

    tm = get_system_time ();

   ExSystemTimeToLocalTime (&tm, &lcl_time);

   RtlTimeToTimeFields(&tm, &fields);

    printf ("%s %02d %04d:%02d:%02d:%02d:%02d", month_names[fields.Month - 1],
        fields.Day, fields.Year, fields.Hour, fields.Minute, fields.Second,
        fileds.Milliseconds);
}

请注意,此代码使用两个未记录的内容,TIME_FILEDS结构和RtlTimeToTimeFields函数。我在我的代码中使用了类似的实现,它适用于所有当前的WIN NT风格。但是,使用它不能保证在下一个WIN NT版本中可移植