Why can’t you treat a FILETIME as an __int64?

The FILETIME structure represents a 64-bit value in two parts:

typedef struct _FILETIME {
  DWORD dwLowDateTime;
  DWORD dwHighDateTime;

You may be tempted to take the entire FILETIME structure and access it directly as if it were an __int64. After all, its memory layout exactly matches that of a 64-bit (little-endian) integer. Some people have written sample code that does exactly this:

pi = (__int64*)&ft; // WRONG
(*pi) += (__int64)num*datepart; // WRONG

Why is this wrong?


Since a FILETIME is a structure containing two DWORDs, it requires only 4-byte alignment, since that is sufficient to put each DWORD on a valid DWORD boundary. There is no need for the first DWORD to reside on an 8-byte boundary. And in fact, you've probably already used a structure where it doesn't: The WIN32_FIND_DATA structure.

typedef struct _WIN32_FIND_DATA {
    DWORD dwFileAttributes;
    FILETIME ftCreationTime;
    FILETIME ftLastAccessTime;
    FILETIME ftLastWriteTime;
    DWORD nFileSizeHigh;
    DWORD nFileSizeLow;
    DWORD dwReserved0;
    DWORD dwReserved1;
    TCHAR  cFileName[ MAX_PATH ];
    TCHAR  cAlternateFileName[ 14 ];

Observe that the three FILETIME structures appear at offsets 4, 12, and 20 from the beginning of the structure. They have been thrown off 8-byte alignment by the dwFileAttributes member.

Casting a FILETIME to an __int64 therefore can (and in the WIN32_FIND_DATA case, will) create a misaligned pointer. Accessing a misaligned pointer will raise a STATUS_DATATYPE_MISALIGNMENT exception on architectures which require alignment.

Even if you are on a forgiving platform that performs automatic alignment fixups, you can still run into trouble. More on this and other consequences of alignment in the next few entries.

Exercise: Why are the LARGE_INTEGER and ULARGE_INTEGER structures not affected?

Comments (27)
  1. LARGE_INTEGER and ULARGE_INTEGER are unions, and the C standard states that they must be aligned suitably for the largest contained member — which is a LONLONG or ULONGLONG, respectively.

  2. Vatsan says:

    And that is probably why the C++ casts static_cast<> and reinterpret_cast<> got invented. If only you’d used them, there would never have been an a ‘subtle’ bug in the code to begin with. You’d have been forced to use reinterpret_cast<> and it would have become an ‘obvious’ problem spot.

  3. Oh, and maybe you’ll be able to answer this one, Raymond. Why 100ns units? Why not 1ns, or 1000ns? Is it a Dave Cutler VMS -> NT thing?

    Moreover, what do you call these things? Ticks? Shakes?

    Constants like NUM_100NS_UNITS_PER_SECOND are ugly. Currently, I’m using FILETIME_TICKS_PER_SECOND, but I’d prefer something catchier.

  4. Because 100ns is "good enough". You can represent any date from the 1601 to the year 20,000 in 64bits worth of 100ns units.

    1ns is too small, it runs out in 200 years. 1000ns is too granular for some clock speeds (it’s 1/10th of a millisecond).

  5. SteveM says:

    Wow – what field do you work in Larry? If any of my programs are still running in 200 years on a Windows platform I’ll be well happy!

    Hmm. Bet that’s what they said about the 2 digit year field too…. :-/

  6. Ben Hutchings says:

    I have been using a union to "convert" from FILETIME to integer. I wonder whether this is officially supported or whether I should be using some other technique?

  7. Raymond Chen says:

    Depends how you use that union. If


    FILETIME ft;

    __int64 i64;

    } u;

    u.ft = ft;

    result = u.i64;

    that’s okay. But if you use

    result = ((union FILETIMEorLONGLONG*)&ft)->i64;

    then you’re back where you started.

  8. Ben Hutchings says:

    I use the former, of course.

  9. Merle says:

    Larry, 1000ns = 1us (microsecond) = 0.001ms (millisecond). Metric is gaps of 1000. So 100ns = 1/10us.

    1/10ms would be 100,000ns.

    Apologia for using us instead of [mu]s, which is hard to type.

  10. Aarrgghh says:

    SteveM: 200 years from 1601 may not be as far in the future as you think.

  11. Anonymous Coward says:

    The 100ns time slices with an origin in 1604 are actually the 64 bit DCE time type. I believe NT copied them from DCE, but it may have been the other way round.

  12. RJ says:

    Just to be overly clear, it is also not correct to cast a FILETIME* to ULARGE_INTEGER*. Instead, code like the following should be used:

    FILETIME ft;


    temp.HighPart = ft.dwHighDateTime;

    temp.LowPart = ft.dwLowDateTime;

    /* now ready to use temp.QuadPart */


  13. Anonymous Coward. Nice idea, but NT’s time starts at January 1, 1601, not 1604.

    I just looked it up in the NT OS/2 Timer specification (which I happen to have on my desk), it’s more likely to have come from VMS than from DCE.

  14. Nick Parker says:

    What about this?


    FILETIME start, end;



    SystemTimeToFileTime(&st, &start);

    // Some long process


    SystemTimeToFileTime(&st, &end);

    ULARGE_INTEGER time_start = *(ULARGE_INTEGER*)&start;

    ULARGE_INTEGER time_end = *(ULARGE_INTEGER*)&end;

    __int64 i64time_start = *(__int64 *)&time_start;

    __int64 i64time_end = *(__int64 *)&time_end;

    __int64 i64time_diff_ns = i64time_end – i64time_start; // in 100 nano seconds

    __int64 i64time_diff_ms = i64time_diff_ns / 10 / 1000; // in milli seconds

    __int64 i64time_diff_sec = i64time_diff_ms / 1000; // in seconds


  15. Brian says:

    Nick, to quote RJ: "Just to be overly clear, it is also not correct to cast a FILETIME* to ULARGE_INTEGER*"

  16. Wow, that’s a funny coincidence, I was just working with FILETIME:s today (and it’s not something I do every day…) (and yes, I don’t cast, I copy)

  17. josh says:

    You can use a union like the FILETIMEorLONGLONG above and pass its FILETIME member to the API. (though obviously not for WIN32_FIND_DATA) Then such casts are safe and, in fact, unnecessary because you’ve already got the other half of the union. Of course neither way is guaranteed by the language to work…

  18. Grant says:

    I´m working with time right now in C++ but I´ve really only programmed in Java before. Is there any simple function that returns the time in milliseconds, like System.currentTimeMillis() in Java? Can I get this as one long value or do I need some struct?

  19. Stew says:


    FILETIME ft;

    __int64 i64;

    } u;

    u.ft = ft;

    result = u.i64;

    The Standard (section 9.5) says "In a union, at most one of the data members can be active at any time, that is, the value of at most one of the data members can be stored in a union at any time."

    I know that the above code will work, but is it guaranteed to work?

  20. ejor says:

    Ok but what about in wininternl.h

    // use the Win32 API instead

    // GetSystemTimeAsFileTime



    NtQuerySystemTime (




    // use the Win32 API instead

    // LocalFileTimeToFileTime



    RtlLocalTimeToSystemTime (





    // use the Win32 API instead

    // SystemTimeToFileTime to convert to FILETIME structures

    // copy the resulting FILETIME structures to ULARGE_INTEGER structures

    // perform the calculation



    RtlTimeToSecondsSince1970 (


    PULONG ElapsedSeconds


    Does Microsoft do the same mistake ?

  21. JamesW says:

    Larry Osterman: NT’s time starts at January 1, 1601, not 1604 [snip] it’s more likely to have come from VMS than from DCE.

    VMS time begins at Midnight Wednesday, November 17, 1858 and ends on 31-JUL-31086 02:48:05.47. NT does copy the 100ns granularity though. There is some logic in the VMS base date too as it is also the base date of the Modified Julian Day system making VMS time astronomer friendly.

  22. Stew: it is not guaranteed to work by the language because it assumes a specific byte ordering (little-endian). It may be guaranteed to work by the Windows Platform SDK if it happens to reverse the FILETIME structure depending on your CPU, but I doubt that.

    This makes Raymond’s post sound a little strange because he seems to be saying that it’s ok to assume little-endian but that you shouldn’t assume mis-aligned access will be forgiven. In my mind these two assumptions are equally bad. Maybe I just mis-read the whole thing…

  23. gkdada says:

    Hey, I go like this all the time (to save some clock cycles)

    ULONGLONG ullCurTime;


    You mean to say, this is wrong?

  24. gkdada: it is wrong in theory because C doesn’t provide the guarantees for this. It works fine in practice on the platforms that are currently supported by Windows. The alignment problem comes into play only when casting the other way (from FILETIME to ULONGLONG).

    And that also provides the answer to my question above: why is endianness unimportant and alignment important? Because all currently supported Windows platforms are little-endian but some of them will fault on a mis-aligned access.

    Still, I think it’s a shame that NT strayed from the goal of being portable. IBM’s upcoming processors look interesting but only Linux and OS X will be able to take advantage. If it weren’t for AMD providing some competition, Windows would not be winning those TPC benchmarks anymore (I’m not saying they win them on AMD processors, I’m saying that AMD’s competition keeps Intel on their toes).

  25. Gah! Just checked the TPC results (hadn’t been there in a while, that’s not exactly on my daily reading list :)) and Windows isn’t winning anymore. Maybe it’s time to prod hp into making a few submissions, as the Windows results are the oldest in the top ten.

  26. Iain Clarke says:


    Does Microsoft do the same mistake ?

    // copy the resulting FILETIME structures to ULARGE_INTEGER structures


    In NtQuerySystemTime, you pass a pointer

    to a PLARGE_INTEGER which will be already


    In RtlLocalTimeToSystemTime you similarly pass already aligned pointers.

    And the comments for RtlTimeToSecondsSince1970 say to *copy* between FILETIME and ULARGE_INTEGER structures. Not *cast*.


  27. I got an email the other day from someone (who will remain nameless) complaining about the fact that…

Comments are closed.