53

In windows XP "FileInfo.LastWriteTime" will return the date a picture is taken - regardless of how many times the file is moved around in the filesystem.

In Vista it instead returns the date that the picture is copied from the camera.

How can I find out when a picture is taken in Vista? In windows explorer this field is referred to as "Date Taken".

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
sepang
  • 4,640
  • 4
  • 23
  • 23

8 Answers8

112

Here's as fast and clean as you can get it. By using FileStream, you can tell GDI+ not to load the whole image for verification. It runs over 10 × as fast on my machine.

//we init this once so that if the function is repeatedly called
//it isn't stressing the garbage man
private static Regex r = new Regex(":");

//retrieves the datetime WITHOUT loading the whole image
public static DateTime GetDateTakenFromImage(string path)
{
    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    using (Image myImage = Image.FromStream(fs, false, false))
    {
        PropertyItem propItem = myImage.GetPropertyItem(36867);
        string dateTaken = r.Replace(Encoding.UTF8.GetString(propItem.Value), "-", 2);
        return DateTime.Parse(dateTaken);
    }
}

And yes, the correct id is 36867, not 306.

The other Open Source projects below should take note of this. It is a huge performance hit when processing thousands of files.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
kDar
  • 3,246
  • 4
  • 19
  • 23
  • 1
    After some testing I found out that your answer the best one. Thanks. – Sergio Dec 04 '11 at 23:16
  • 4
    Now only if you'd have done a check to see if it exists first. There is always a chance this property does not. – Tim Meers Jan 15 '12 at 17:26
  • 8
    Great solution! The property check is important. If you add if (myImage.PropertyIdList.Any(x => x == 36867)) as your check it works great! – Brian ONeil Dec 17 '12 at 02:15
  • 6
    To demystify this answer a little bit. PropertyItem 36867 (or 0x9003 in hex) is the "PropertyTagExifDTOrig" as described on MSDN in this list http://msdn.microsoft.com/en-us/library/system.drawing.imaging.propertyitem.id(v=vs.110).aspx – Sverrir Sigmundarson Dec 27 '14 at 19:00
  • 1
    I'm seeing the Date Taken property in explorer, but not a property with ID 36867. Any ideas? – koenmetsu May 27 '15 at 20:12
  • 2
    It shows me an error "Property cannot be found." Iam trying to process an .PNG file – Muflix Nov 02 '15 at 13:09
  • An even faster and cleaner way of doing this is described in [my answer](http://stackoverflow.com/a/39839380/24874). In benchmarks, the approach I show is 12.7 times faster than the above. – Drew Noakes Oct 03 '16 at 20:56
  • @Drew, maybe faster, but not cleaner. All sorts of extra stuff needs to be downloaded via NuGet. However, if I was ever to try to wrestle with a CS2 (RAW Canon) file someday, I would head lickity split over to your stuff. thank you.. . . . . with only adding a couple references to my cs (which VS suggested) and this code I was able to get the date the img was taken. . . .PropertyItem propItem = img.GetPropertyItem(36867); string dateTaken = r.Replace(Encoding.UTF8.GetString(propItem.Value), "-", 2); DateTime whenTaken = DateTime.Parse(dateTaken); . . – JustJohn Mar 26 '17 at 19:38
  • @JustJohn the code is cleaner in my answer, and it runs a lot faster at runtime. Why don't you like NuGet packages? There are only two assemblies you need to include. But cool that you found a solution that works for you. – Drew Noakes Mar 26 '17 at 19:48
  • @Drew, I accept your suggestion. I will get over there and see what I can use your stuff for. Currently, I can't figure out how to convert a CS2 file to a TIFF or jpeg. – JustJohn Mar 26 '17 at 21:18
  • 1
    @Drew. yes this answer didn't work for Canon .CR2 file. The .GetPropertyItem(36867) didn't exist for .CR2. So your answer is the best and fastest. With actually a couple less lines of actual code LOL – JustJohn May 08 '17 at 04:38
  • 1
    It says "Property 36867 not found" but, property 306 returns taken date. (the JPG images captured by my Xperia x phone). – Ehsan Aug 13 '17 at 05:16
  • 36867 did not work. It returns property error. 306 worked for me. – Mark Dec 06 '17 at 16:24
  • 1
    In case you are like me and it's not obvious what's the correct namespace for this in .Net Core: System.Drawing.Common and System.Drawing; The former is available as a Nuget. – Superman.Lopez Oct 31 '19 at 13:23
  • 2
    In case someone wants to get this to run in Unity, search your main drive for "System.Drawing.dll" and copy the file into your Assets folder. Now add references to `using System.Text; using System.Drawing; using System.Drawing.Imaging;` – Philipp Lenssen Oct 13 '20 at 06:12
  • Thx to Phillip Lenssen! Without knowing the namespace(s)/assemblies to use/include is's a guessing game to make this code to compile... – hfrmobile Jul 17 '22 at 18:30
12

I maintained a simple open-source library since 2002 for extracting metadata from image/video files.

// Read all metadata from the image
var directories = ImageMetadataReader.ReadMetadata(stream);

// Find the so-called Exif "SubIFD" (which may be null)
var subIfdDirectory = directories.OfType<ExifSubIfdDirectory>().FirstOrDefault();

// Read the DateTime tag value
var dateTime = subIfdDirectory?.GetDateTime(ExifDirectoryBase.TagDateTimeOriginal);

In my benchmarks, this code runs over 12 times faster than Image.GetPropertyItem, and around 17 times faster than the WPF JpegBitmapDecoder/BitmapMetadata API.

There's a tonne of extra information available from the library such as camera settings (F-stop, ISO, shutter speed, flash mode, focal length, ...), image properties (dimensions, pixel configurations) and other things such as GPS positions, keywords, copyright info, etc.

If you're only interested in the metadata, then using this library is very fast as it doesn't decode the image (i.e. bitmap). You can scan thousands of images in a few seconds if you have fast enough storage.

ImageMetadataReader understands many files types (JPEG, PNG, GIF, BMP, TIFF, PCX, WebP, ICO, ...). If you know that you're dealing with JPEG, and you only want Exif data, then you can make the process even faster with the following:

var directories = JpegMetadataReader.ReadMetadata(stream, new[] { new ExifReader() });

The metadata-extractor library is available via NuGet and the code's on GitHub. Thanks to all the amazing contributors who have improved the library and submitted test images over the years.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • Drew, I'm back! I now have code to convert RAW to Tiff but my Image.GetPropertyItem doesn't have date taken by using 36867. Will your code do Tiffs and Raws? – JustJohn May 07 '17 at 23:58
  • 1
    And. . . . drum roll. . . . it installed flawlessly as per your Nugetter and using the code above in your anwer it returned the photoTaken date for a .CR2 file (Canon)! Of course, had to click on several potential fixes to get the usings put at the top but VS 2017 babysits you. – JustJohn May 08 '17 at 04:36
  • Just to confirm: this library can't be used to *write* metadata, correct? – rory.ap Jun 12 '19 at 11:36
  • @rory.ap not yet. You can subscribe to https://github.com/drewnoakes/metadata-extractor-dotnet/issues/23 to track this feature request. – Drew Noakes Jun 12 '19 at 23:30
  • 1
    Thanks, this worked well in a macOS environment, unlike System.Drawing. Will test in Ubuntu environment later in project. – Superman.Lopez Oct 31 '19 at 14:04
  • @PhilippLenssen the MetadataExtractor library does not have a dependency upon `System.Drawing.dll`. – Drew Noakes Oct 12 '20 at 22:58
  • @DrewNoakes Thanks, I was replying to the wrong thread, fixed it now. – Philipp Lenssen Oct 13 '20 at 06:12
11
Image myImage = Image.FromFile(@"C:\temp\IMG_0325.JPG");
PropertyItem propItem = myImage.GetPropertyItem(306);
DateTime dtaken;

//Convert date taken metadata to a DateTime object
string sdate = Encoding.UTF8.GetString(propItem.Value).Trim();
string secondhalf = sdate.Substring(sdate.IndexOf(" "), (sdate.Length - sdate.IndexOf(" ")));
string firsthalf = sdate.Substring(0, 10);
firsthalf = firsthalf.Replace(":", "-");
sdate = firsthalf + secondhalf;
dtaken = DateTime.Parse(sdate);
Jonas W
  • 3,200
  • 1
  • 31
  • 44
sepang
  • 4,640
  • 4
  • 23
  • 23
  • 3
    ACTUALLY, 306 is the last MODIFIED date identifier... I tried and it was VERY CLOSE... However, looking at ALL the Property IDs, and dumping to a text file, I found that ID 36867 was the date taken (although 36868 also has same Date Taken value, so I'm not positive which was which) – DRapp Feb 06 '11 at 20:23
  • the answer above should be changed to the answer as far as speed of getting the EXIFG if it exists. This method works, but is slow - especially in batch! – TheSoftwareJedi Apr 02 '12 at 02:01
5

With WPF and C# you can get the Date Taken property using the BitmapMetadata class:

MSDN - BitmapMetada

WPF and BitmapMetadata

DaveK
  • 4,509
  • 3
  • 33
  • 33
  • Can I use BitmapMetadata with Window.Forms or is it WPF only? (getting weird error messages from Visual Studio 2008) – sepang Oct 07 '08 at 20:42
  • Or use [my library](http://stackoverflow.com/a/39839380/24874) which has a simpler API and runs around 17 times faster than these WPF APIs (or 30 times faster if you only want Exif). – Drew Noakes Oct 03 '16 at 21:05
2

In windows XP "FileInfo.LastWriteTime" will return the date a picture is taken - regardless of how many times the file is moved around in the filesystem.

I have great doubts XP was actually doing that. More likely the tool you used to copy the image from the camera to you hard disk was reseting the File Modified Date to the image's Date Taken.

James Curran
  • 101,701
  • 37
  • 181
  • 258
1

you'll have to check the EXIF information from the picture. I don't think with regular .Net functions you'll know when the picture was taken.

It might get a little complicated...

Miles
  • 5,646
  • 18
  • 62
  • 86
0

There will be EXIF data embedded in the image. There are a ton of examples on the web if you search for EXIF and C#.

Paul Hammond
  • 6,226
  • 3
  • 17
  • 10
0
    //retrieves the datetime WITHOUT loading the whole image
    public static DateTime GetDateTakenFromImage(string path)
    {
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        using (Image myImage = Image.FromStream(fs, false, false))
        {
            PropertyItem propItem = null;
            try
            {
                propItem = myImage.GetPropertyItem(36867);
            }
            catch { }
            if (propItem != null)
            {
                string dateTaken = r.Replace(Encoding.UTF8.GetString(propItem.Value), "-", 2);
                return DateTime.Parse(dateTaken);
            }
            else
                return new FileInfo(path).LastWriteTime;
        }
    }
  • 1
    This is literally a duplicate of @kDar's answer, with a little bit of error handling... – Mark Jan 02 '18 at 11:06