15

Most of the files I read get the right time when using the following method to convert:

// works great most of the time
private static DateTime convertToDateTime(System.Runtime.InteropServices.ComTypes.FILETIME time)
{
    long highBits = time.dwHighDateTime;
    highBits = highBits << 32;
    return DateTime.FromFileTimeUtc(highBits + time.dwLowDateTime);
}

Here I have an example in visual studio to show how this method sometimes does not work for example I will show the actual file in my computer and the debug. So the file that happens to be in my debug is:

"A:\Users\Tono\Documents\Visual Studio 2010\Projects\WpfApplication4\WpfApplication4\obj\x86\Debug\App.g.cs" enter image description here

And here is the FILETIME that I am trying to convert to DateTime "I need the LastWriteTime by the way"

enter image description here

Here you can see that dwHighDateTime = 30136437 and also that dwLowDateTime = -2138979250 from that file.

And when I run my method plus other techniques I get the following dates: enter image description here

So so far everything seems to be working great. But why is that that when I browse and look for that specific file in windows I get a different date !? Here is the date that I get when seeing the file's properties: enter image description here

Why does the dates don't match? What am I doing wrong?

Tono Nam
  • 34,064
  • 78
  • 298
  • 470
  • 2
    Why are you doing this the hard way? Seems like you're pretty much rewriting the FileInfo class... http://msdn.microsoft.com/en-us/library/system.io.fileinfo.aspx – Oskar Kjellin May 21 '11 at 19:08
  • Im not sure why the OP is doing this the hard way. When working with long path names, that is paths longer than [MAX_LENGTH] = 260 bytes, you _will need_ to rewrite most of System.IO to use unmanaged code. There is more info to be found here: http://blogs.msdn.com/b/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton.aspx – Sascha Hennig Aug 30 '11 at 10:06
  • 1
    There are legitimate scenarios for using FILETIME / GetFileTime instead of FileInfo: For example, if you want to get statistics about a file whose handle you already have open (and cannot re-open it via FileInfo for some reason) – David Roberts Sep 01 '16 at 09:18

5 Answers5

19

You need to combine the LS and MS values bitwise, not arithmetically.

Try:

        ulong high = 30136437;
        unchecked
        {
            int low = -2138979250;
            uint uLow = (uint)low;
            high = high << 32;
            Date dt = DateTime.FromFileTime((long) (high | (ulong)uLow));
        }

Or any of the following should work too:

long highBits = time.dwHighDateTime;     
highBits = highBits << 32;     

return DateTime.FromFileTimeUtc(highBits + (long) (uint) time.dwLowDateTime); 

return DateTime.FromFileTimeUtc(highBits | (long) (uint) time.dwLowDateTime); 

return DateTime.FromFileTimeUtc(highBits + ((long)low & 0xFFFFFFFF))

return DateTime.FromFileTimeUtc(highBits | ((long)low & 0xFFFFFFFF))

You can get away with adding rather than a bitwise-or if you are sure the values are positive (and have no bits in common). But bitwise-or expresses the intent better.

Joe
  • 122,218
  • 32
  • 205
  • 338
  • 1
    time.dwLowDateTime is showing up as a negative value (it shouldn't be) so casting it to long is going to give an incorrect result. instead IIRC you need to do `(long)((uint)(time.dwLowDateTime))` or fix the interop so it starts as a uint. – Yaur May 21 '11 at 19:20
  • tired but did not work. I think that my method is the one that is reading the wrong date. I'll keep trying to figure it out. – Tono Nam May 21 '11 at 19:35
9

I'm a bit late to the party, but this has worked reliably for me:

public static class FILETIMEExtensions
{
    public static DateTime ToDateTime(this System.Runtime.InteropServices.ComTypes.FILETIME time)
    {
        ulong high = (ulong)time.dwHighDateTime;
        uint low = (uint)time.dwLowDateTime;
        long fileTime = (long)((high << 32) + low);
        try
        {
            return DateTime.FromFileTimeUtc(fileTime);
        }
        catch
        {
            return DateTime.FromFileTimeUtc(0xFFFFFFFF);
        }
    }
}

Note: Don't trust Windows Explorer. Use File.GetLastWriteTimeUtc method, for example, to verify what the file system actually has against what this extension method returns. Explorer has some bugs in it that don't update file times in certain situations. Cheers! :)

Note: To test this, you need to use maximum values. So, assuming dwHighDateTime = dwLowDateTime = UInt32.MaxValue = 4294967295 = 0xFFFFFFFF, it follows that (long)(((ulong)UInt32.MaxValue << 32) + UInt32.MaxValue) = -1 = 0xFFFFFFFFFFFFFFFF. Unfortunately, the fallacy in the Windows API seems to be that eventually the time needs to be casted to a long value in order to work with it for any useful applications (since most Windows API methods take the file time as a long value), which means once the leading bit is high (1) on dwHighDateTime, the value becomes negative. Lets try with the maximum time not being high. Assuming dwHighDateTime = Int32.MaxValue = 2147483647 = 0x7FFFFFFF and dwLowDateTime = UInt32.MaxValue = 4294967295 = 0xFFFFFFFF, it follows that (long)(((ulong)Int32.MaxValue << 32) + UInt32.MaxValue) = 0x7FFFFFFFFFFFFFFF.

Note: 0x7FFFFFFFFFFFFFFF is already much larger than DateTime.MaxValue.ToFileTimeUtc() = 2650467743999999999 = 0x24C85A5ED1C04000, rendering numbers that large already useless for any practical applications in .NET.

Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • 2
    This solution works for me, whereas the other answers return incorrect results – David Roberts Sep 01 '16 at 09:35
  • 2
    @DavidRoberts That's because other answers don't factor in that the high date time component as an unsigned 64 bit value...instead I see they incorrectly cast to a signed 64 bit value, which can return a negative result, which is incorrect. – Alexandru Sep 01 '16 at 11:44
  • Indeed this seems to be the most accurate solution. – Anton Krouglov Nov 30 '18 at 16:39
5

This is another method that I have seen to convert a FileTime structure to a long (using a coded operator in the struct), which can then easily be converted to DateTime using the DateTime.FromFileTime functions:

public struct FileTime
{
    public uint dwLowDateTime;
    public uint dwHighDateTime;

    public static implicit operator long(FileTime fileTime)
    {
        long returnedLong;
        // Convert 4 high-order bytes to a byte array
        byte[] highBytes = BitConverter.GetBytes(fileTime.dwHighDateTime);
        // Resize the array to 8 bytes (for a Long)
        Array.Resize(ref highBytes, 8);

        // Assign high-order bytes to first 4 bytes of Long
        returnedLong = BitConverter.ToInt64(highBytes, 0);
        // Shift high-order bytes into position
        returnedLong = returnedLong << 32;
        // Or with low-order bytes
        returnedLong = returnedLong | fileTime.dwLowDateTime;
        // Return long 
        return returnedLong;
    }
}
mkisaacs
  • 51
  • 1
  • 3
0

dwLowDateTime and dwHighDateTime should be uint and it looks like they are int. Changing this will most likely fix it though as @Joe pointed out you should still use | instead of +.

mtb
  • 1,350
  • 16
  • 32
Yaur
  • 7,333
  • 1
  • 25
  • 36
0

I have tried the following and non of them get me the right time:
enter image description here

And I got the method from here

Community
  • 1
  • 1
Tono Nam
  • 34,064
  • 78
  • 298
  • 470