This project has moved and is read-only. For the latest updates, please go here.


Canon specific EXIF values


When i use comand line exiftool to dump all of the metadata from this JPG i see two specific fields i really want to get. They are in a profile listed by exiftool as "Canon"

Time Zone : -05:00
Time Zone City : New York

When i'm using if i get all of the available profiles for the JPG which i'm told are "exif" and "xmp" and loop through all available tags of both i don't see these tags or values and i've got no other tags that will give me the timezone. I'm attaching the JPG and the exiftool's dump file. I'd really love a way to be able to get those two values out otherwise i have no way to know which timezone the date/time fields i'm pulling from EXIF are in.

file attachments

Closed Feb 15 at 4:35 PM by dlemstra


scohen2002 wrote Feb 8 at 2:23 PM

Crap, the JPG is too big to attach it's 7.3 MB, i'm afraid to resize it and save it again in photoshop because photoshop will redo the metadata in side the jpg.

scohen2002 wrote Feb 8 at 2:57 PM

Here is a second smaller JPG demonstrating the same problem. I'm going to attach the exiftool dump showing those two Canon EXIF tags and the JPG.

scohen2002 wrote Feb 8 at 3:14 PM


dlemstra wrote Feb 10 at 10:54 AM

The Canon specific information is stored inside the ExifTag.MakerNote. I might be able to add something for that in the future but it's going to be a lot of work. For now you will need to parse that yourself. I tried to parse the data but I am getting an incorrect offset at some point and I will let you figure out how to resolve that. I hope you will come back here with the answer though :) Here is my code sample:
using (MagickImage image = new MagickImage("1MR1215.jpg"))
  ExifProfile profile = image.GetExifProfile();
  ExifValue markerNote = profile.GetValue(ExifTag.MakerNote);

  byte[] data = (byte[])markerNote.Value;
  ushort count = BitConverter.ToUInt16(data, 0);

  int offset = 2;
  int start = offset + (count * 12);
  for (ushort i = 0; i < count; i++)
    ushort id = BitConverter.ToUInt16(data, offset);
    offset += 8;

    // ID list at:
    if (id == 0x0035) // TimeInfo
      int dataOffset = (int)BitConverter.ToUInt32(data, offset);

      // dataOffset = 3892 but should be 2850 ???
      dataOffset = 2850;

      int timeZone = BitConverter.ToInt32(data, dataOffset);
      // -300 => -300 / 60 = -5

      int timeZoneCity = BitConverter.ToInt32(data, dataOffset + 4);
      // 27 = New York

      int daylightSavings = BitConverter.ToInt32(data, dataOffset + 8);
      // 0 = Off

    offset += 4;

scohen2002 wrote Feb 14 at 9:22 PM

I'm not very proud of what i am trying. But after a lot of frustration it's where i am at. I normally read what i consider the creation date/time/zone out of IPTC. Photo that my system sees have it there pretty uniformly because some software post processed the photo and set those values. The photos i'm having this problem with are tethered so to speak. In reality they are actually submitting via FTP live from the field via a wifi hotspot from a phone. Just giving more information than you need in case it's interesting. These special photos that come to me straight from a Canon 1DX mk ii have no IPTC at all and i have no issue reading what i need except for this f^!%$&! timezone. So... what i did here in this hack was to extract the date and time from the EXIF's DateTimeOriginal field and extract the timezone from that specific field you found in Maker's note that is the offset in minutes. With those two values in hand i form them into a string that looks like what i am used to seeing from IPTC and then run the same code i use there to turn it into a .Net DateTime and DateTimeOffset, then get the timezone information from my database based on the offset. I need to do all of this none sense because we push out all of of our photos in UTC and we have to convert it to that properly from whatever it was on the way in.
            // if the date and time and time zone was not found in IPTC then try to get it out of the EXIF
            // this should only be the case on photos recieved via teathered cameras as all post processing
            // done in software provides us proper IPTC values
            if (!setDate)
                // EpixTimeZoneManager.IncomingPhotoDateFormat:: "20170214 090332-0600"
                // Exif.DateTimeOriginal::"2017:02:07 13:32:34"
                    var exifDate = exifProfile.GetValue(ExifTag.DateTimeOriginal);
                    var markersNote = exifProfile.GetValue(ExifTag.MakerNote);
                    if (exifDate != null)
                        string exifDateString = exifDate.Value.ToString();
                        int offsetMinutes = 0;

                        if (markersNote != null)
                            offsetMinutes = BitConverter.ToInt32((byte[])markersNote.Value, 2850);

                        TimeSpan offsetTS = TimeSpan.FromMinutes(offsetMinutes);
                        string dateTimeOffsetString = exifDateString.Substring(0, 4);
                        dateTimeOffsetString += exifDateString.Substring(5, 2);
                        dateTimeOffsetString += exifDateString.Substring(8, 2);
                        dateTimeOffsetString += " ";
                        dateTimeOffsetString += exifDateString.Substring(11, 2);
                        dateTimeOffsetString += exifDateString.Substring(14, 2);
                        dateTimeOffsetString += exifDateString.Substring(17, 2);
                        if (offsetMinutes >= 0)
                            dateTimeOffsetString += "+";
                        dateTimeOffsetString += offsetTS.Hours.ToString("00");
                        dateTimeOffsetString += offsetTS.Minutes.ToString("00");

                        DateTime dateCreated = DateTime.UtcNow;
                        if (DateTime.TryParseExact(dateTimeOffsetString, EpixTimeZoneManager.IncomingPhotoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateCreated))
                            metadataRecord.IPTCMetadata.DateCreated = dateCreated;

                        DateTimeOffset dateCreatedOffSet = DateTimeOffset.MinValue;
                        if (DateTimeOffset.TryParseExact(dateTimeOffsetString, EpixTimeZoneManager.IncomingPhotoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateCreatedOffSet))
                            string dateCreatedTimeZone = EpixTimeZoneManager.GetTimeZoneBasedonOffset(offsetMinutes);
                            if (!string.IsNullOrEmpty(dateCreatedTimeZone)
                                && dateCreatedTimeZone.ToLower() != EpixTimeZoneManager.DefaultTimeZone.ToLower())
                                metadataRecord.IPTCMetadata.DateCreatedTimeZone = dateCreatedTimeZone;
                                metadataRecord.IPTCMetadata.DateCreatedOffsetMinutes = (double)offsetMinutes;
                catch (Exception exc)
                    logger.Warn(instanceLogFormatter.SetMessage("Error extracting creation time from EXIF: Exception: {0}",
                        exc.Message).SetErrorLocation(exc.StackTrace, 0));

dlemstra wrote Feb 15 at 1:51 PM

I am not sure if the offset (2850) will be the same everytime. I am doing something wrong in my code and I was hoping you would find why :)

I have created an issue on GitHub that sort of addresses what is discussed here:

Can I close this issue now?

scohen2002 wrote Feb 15 at 3:03 PM

Yes you can close and thank you for your help, you gave me perfect info to solve my pressing issue. I know i didn't return the favor and figure anything out with the offset problem.

I had one of my photographers change the timezone and other metadata and submit photos from the camera with all kinds of time zones and tested it and it's holding.

I'm aware it's in-elegant and fragile and will probably break if they use a different camera model or upgrade the firmware or something, but i'll take it for now.