99

I'm looking to find out a way of seeing when a file was last modified in C#. I have full access to the file.

T.Todua
  • 53,146
  • 19
  • 236
  • 237
Candyfloss
  • 3,848
  • 4
  • 31
  • 32
  • 1
    possible duplicate of [How to get Modified date from file in c#](http://stackoverflow.com/questions/1185378/how-to-get-modified-date-from-file-in-c-sharp) – nawfal Dec 25 '13 at 07:35
  • 1
    Problem in link above is for more specific case but I agree - the accepted answer here is mentioned in that question. – Candyfloss Dec 31 '13 at 11:41

5 Answers5

148

System.IO.File.GetLastWriteTime is what you need.

Pang
  • 9,564
  • 146
  • 81
  • 122
Dean Harding
  • 71,468
  • 13
  • 145
  • 180
  • 1
    or possibly even [System.IO.File.GetLastWriteTimeUtc](https://msdn.microsoft.com/en-us/library/system.io.file.getlastwritetimeutc(v=vs.110).aspx) – Martin Connell Jun 01 '17 at 17:53
  • 4
    Sad fact - GetLastWriteTime does not get the last write time. See https://stackoverflow.com/questions/9992223/file-getlastwritetime-seems-to-be-returning-out-of-date-value – speciesUnknown Oct 15 '18 at 10:03
  • How to get real modified time? I mean this property changes even with simple re-save without actual change. is there any way to detect fake changes? – Mehdi Dehghani Oct 26 '19 at 09:22
  • @MehdiDehghani a good way of checking if a file really is changed is by checking the checksum. The checksum only changes if the content of the file changes see https://stackoverflow.com/questions/10520048/calculate-md5-checksum-for-a-file – Christiaan Beekhuizen Nov 26 '19 at 15:06
  • 1
    If the given **file does not exist**, you get _no_ exception. Instead you get the time stamp `1601-01-01 01:00:00`. . . . . . (just for information) – Beauty Dec 21 '22 at 10:47
78

You simply want the File.GetLastWriteTime static method.

Example:

var lastModified = System.IO.File.GetLastWriteTime("C:\foo.bar");

Console.WriteLine(lastModified.ToString("dd/MM/yy HH:mm:ss"));

Note however that in the rare case the last-modified time is not updated by the system when writing to the file (this can happen intentionally as an optimisation for high-frequency writing, e.g. logging, or as a bug), then this approach will fail, and you will instead need to subscribe to file write notifications from the system, constantly listening.

Pang
  • 9,564
  • 146
  • 81
  • 122
Noldorin
  • 144,213
  • 56
  • 264
  • 302
  • 1
    Upvote for taking the time to include a code example. – Phil Nicholas Jan 31 '17 at 19:57
  • Sad that many answers here say GetLastWriteTime. Unfortunately, it doesnt do what it says - it doesn't get the last write time. The only way to be sure is to actively observe for file changes. https://stackoverflow.com/questions/9992223/file-getlastwritetime-seems-to-be-returning-out-of-date-value – speciesUnknown Oct 15 '18 at 10:02
  • @gburton It certainly does! Only programs that write to the file without updating the last modified time (which is very non-standard) will cause problems with this approach. – Noldorin Oct 15 '18 at 16:28
  • @gburton Obviously. But you miss the entire point: just because it doesn't work in your weird case, it doesn't mean it doesn't work in the majority. Yours is the exception, not the rule. It disobeys conventions. – Noldorin Oct 15 '18 at 16:38
  • @Glorfindel Nice username, just noticed! – Noldorin Oct 15 '18 at 16:38
  • 1
    @Noldorin my "wierd case" is actually a .net 4.5 web application, and the tool which supposedly isnt properly updating the timestamp (which isn't supposed to be a manual operation) is the angular command line tool. These are perfectly normal situations; this is a performance optimisation by microsoft which has rendered GetLastWriteTime useless. check this SO thread: https://stackoverflow.com/a/4381256/3931173 – speciesUnknown Oct 15 '18 at 16:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/181897/discussion-between-gburton-and-noldorin). – speciesUnknown Oct 15 '18 at 16:57
  • I'll put a caveat into my post, to make everyone happy. – Noldorin Oct 15 '18 at 19:59
20

Be aware that the function File.GetLastWriteTime does not always work as expected, the values are sometimes not instantaneously updated by the OS. You may get an old Timestamp, even if the file has been modified right before.

The behaviour may vary between OS versions. For example, this unit test worked well every time on my developer machine, but it always fails on our build server.

  [TestMethod]
  public void TestLastModifiedTimeStamps()
  {
     var tempFile = Path.GetTempFileName();
     var lastModified = File.GetLastWriteTime(tempFile);
     using (new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None))
     {

     }
     Assert.AreNotEqual(lastModified, File.GetLastWriteTime(tempFile));
  }

See File.GetLastWriteTime seems to be returning 'out of date' value

Your options:

a) live with the occasional omissions.

b) Build up an active component realising the observer pattern (eg. a tcp server client structure), communicating the changes directly instead of writing / reading files. Fast and flexible, but another dependency and a possible point of failure (and some work, of course).

c) Ensure the signalling process by replacing the content of a dedicated signal file that other processes regularly read. It´s not that smart as it´s a polling procedure and has a greater overhead than calling File.GetLastWriteTime, but if not checking the content from too many places too often, it will do the work.

/// <summary>
/// type to set signals or check for them using a central file 
/// </summary>
public class FileSignal
{
    /// <summary>
    /// path to the central file for signal control
    /// </summary>
    public string FilePath { get; private set; }

    /// <summary>
    /// numbers of retries when not able to retrieve (exclusive) file access
    /// </summary>
    public int MaxCollisions { get; private set; }

    /// <summary>
    /// timespan to wait until next try
    /// </summary>
    public TimeSpan SleepOnCollisionInterval { get; private set; }

    /// <summary>
    /// Timestamp of the last signal
    /// </summary>
    public DateTime LastSignal { get; private set; }

    /// <summary>
    /// constructor
    /// </summary>
    /// <param name="filePath">path to the central file for signal control</param>
    /// <param name="maxCollisions">numbers of retries when not able to retrieve (exclusive) file access</param>
    /// <param name="sleepOnCollisionInterval">timespan to wait until next try </param>
    public FileSignal(string filePath, int maxCollisions, TimeSpan sleepOnCollisionInterval)
    {
        FilePath = filePath;
        MaxCollisions = maxCollisions;
        SleepOnCollisionInterval = sleepOnCollisionInterval;
        LastSignal = GetSignalTimeStamp();
    }

    /// <summary>
    /// constructor using a default value of 50 ms for sleepOnCollisionInterval
    /// </summary>
    /// <param name="filePath">path to the central file for signal control</param>
    /// <param name="maxCollisions">numbers of retries when not able to retrieve (exclusive) file access</param>        
    public FileSignal(string filePath, int maxCollisions): this (filePath, maxCollisions, TimeSpan.FromMilliseconds(50))
    {
    }

    /// <summary>
    /// constructor using a default value of 50 ms for sleepOnCollisionInterval and a default value of 10 for maxCollisions
    /// </summary>
    /// <param name="filePath">path to the central file for signal control</param>        
    public FileSignal(string filePath) : this(filePath, 10)
    {
    }

    private Stream GetFileStream(FileAccess fileAccess)
    {
        var i = 0;
        while (true)
        {
            try
            {
                return new FileStream(FilePath, FileMode.Create, fileAccess, FileShare.None);
            }
            catch (Exception e)
            {
                i++;
                if (i >= MaxCollisions)
                {
                    throw e;
                }
                Thread.Sleep(SleepOnCollisionInterval);
            };
        };
    }

    private DateTime GetSignalTimeStamp()
    {
        if (!File.Exists(FilePath))
        {
            return DateTime.MinValue;
        }
        using (var stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            if(stream.Length == 0)
            {
                return DateTime.MinValue;
            }
            using (var reader = new BinaryReader(stream))
            {
                return DateTime.FromBinary(reader.ReadInt64());
            };                
        }
    }

    /// <summary>
    /// overwrites the existing central file and writes the current time into it.
    /// </summary>
    public void Signal()
    {
        LastSignal = DateTime.Now;
        using (var stream = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            using (var writer = new BinaryWriter(stream))
            {
                writer.Write(LastSignal.ToBinary());
            }
        }
    }

    /// <summary>
    /// returns true if the file signal has changed, otherwise false.
    /// </summary>        
    public bool CheckIfSignalled()
    {
        var signal = GetSignalTimeStamp();
        var signalTimestampChanged = LastSignal != signal;
        LastSignal = signal;
        return signalTimestampChanged;
    }
}

Some tests for it:

    [TestMethod]
    public void TestSignal()
    {
        var fileSignal = new FileSignal(Path.GetTempFileName());
        var fileSignal2 = new FileSignal(fileSignal.FilePath);
        Assert.IsFalse(fileSignal.CheckIfSignalled());
        Assert.IsFalse(fileSignal2.CheckIfSignalled());
        Assert.AreEqual(fileSignal.LastSignal, fileSignal2.LastSignal);
        fileSignal.Signal();
        Assert.IsFalse(fileSignal.CheckIfSignalled());
        Assert.AreNotEqual(fileSignal.LastSignal, fileSignal2.LastSignal);
        Assert.IsTrue(fileSignal2.CheckIfSignalled());
        Assert.AreEqual(fileSignal.LastSignal, fileSignal2.LastSignal);
        Assert.IsFalse(fileSignal2.CheckIfSignalled());
    }
Community
  • 1
  • 1
Udontknow
  • 1,472
  • 12
  • 32
  • 1
    I had exact same issue with my unit tests. Since the code logic I'm testing is not straightforward, I spent literally days to find out that the last modified time is not accurate. – gimbup Feb 24 '19 at 19:25
5

Just use File.GetLastWriteTime. There's a sample on that page showing how to use it.

Pang
  • 9,564
  • 146
  • 81
  • 122
Hans Olsson
  • 54,199
  • 15
  • 94
  • 116
  • 1
    Unfortuantely, File.GetLastWriteTime does not always get the last write time. It seems like a good solution until you realise there are internal caches which won't be accurate to anywhere near the precision that GetLastWriteTime seems to provide. See https://stackoverflow.com/questions/9992223/file-getlastwritetime-seems-to-be-returning-out-of-date-value – speciesUnknown Oct 15 '18 at 10:00
0

The actual real last modified DateTime is in Microsoft.VisualBasic.FileSystem.FileDateTime(pathString).

LastWriteTime will give you a false positive if the file has been copied from its original location.

See https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.filesystem.filedatetime?view=netframework-4.8

Also: I have noticed that some files transferred between computers can differ in their modified DateTime by a second or so. NFI why. I added a 5-second fudge-factor to my check to account for this discrepancy.

CAD bloke
  • 8,578
  • 7
  • 65
  • 114